xss mitigation & test

This commit is contained in:
$(pass /github/name)
2024-09-20 07:57:54 +02:00
parent b34a85e9d6
commit 46afa417b7
7 changed files with 81 additions and 33 deletions

View File

@@ -0,0 +1,31 @@
package controllers
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
)
func testXSSAttempt(t *testing.T) {
gin.SetMode(gin.TestMode)
router := gin.New()
router.POST("/register", Uc.RegisterUser)
xssPayload := "<script>alert('XSS')</script>"
user := getBaseUser()
user.FirstName = xssPayload
user.Email = "user@xss.hack"
jsonData, _ := json.Marshal(RegistrationData{User: user})
req, _ := http.NewRequest("POST", "/register", bytes.NewBuffer(jsonData))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusNotAcceptable, w.Code)
assert.NotContains(t, w.Body.String(), xssPayload)
}

View File

@@ -6,8 +6,8 @@ type BankAccount struct {
CreatedAt time.Time CreatedAt time.Time
UpdatedAt time.Time UpdatedAt time.Time
MandateDateSigned time.Time `gorm:"not null"` // json:"mandate_date_signed"` MandateDateSigned time.Time `gorm:"not null"` // json:"mandate_date_signed"`
Bank string //`json:"bank_name" validate:"omitempty,alphanumunicode"` Bank string //`json:"bank_name" validate:"omitempty,alphanumunicode,safe_content"`
AccountHolderName string //`json:"account_holder_name" validate:"omitempty,alphaunicode"` AccountHolderName string //`json:"account_holder_name" validate:"omitempty,alphaunicode,safe_content"`
IBAN string `gorm:"not null" json:"iban" validate:"required,iban"` IBAN string `gorm:"not null" json:"iban" validate:"required,iban"`
BIC string //`json:"bic" validate:"omitempty,bic"` BIC string //`json:"bic" validate:"omitempty,bic"`
MandateReference string `gorm:"not null"` //json:"mandate_reference"` MandateReference string `gorm:"not null"` //json:"mandate_reference"`

View File

@@ -5,10 +5,10 @@ import "time"
type Consent struct { type Consent struct {
CreatedAt time.Time CreatedAt time.Time
UpdatedAt time.Time UpdatedAt time.Time
FirstName string `gorm:"not null" json:"first_name"` FirstName string `gorm:"not null" json:"first_name" validate:"safe_content"`
LastName string `gorm:"not null" json:"last_name"` LastName string `gorm:"not null" json:"last_name" validate:"safe_content"`
Email string `json:"email"` Email string `json:"email" validate:"email,safe_content"`
ConsentType string `gorm:"not null" json:"consent_type"` ConsentType string `gorm:"not null" json:"consent_type" validate:"safe_content"`
ID int64 `gorm:"primaryKey"` ID int64 `gorm:"primaryKey"`
UserID int64 `gorm:"not null" json:"user_id"` UserID int64 `gorm:"not null" json:"user_id"`
} }

View File

@@ -7,7 +7,7 @@ type Membership struct {
UpdatedAt time.Time UpdatedAt time.Time
StartDate time.Time `json:"start_date"` StartDate time.Time `json:"start_date"`
EndDate time.Time `json:"end_date"` EndDate time.Time `json:"end_date"`
Status string `json:"status"` Status string `json:"status" validate:"safe_content"`
SubscriptionModel SubscriptionModel `gorm:"foreignKey:SubscriptionModelID" json:"subscription_model"` SubscriptionModel SubscriptionModel `gorm:"foreignKey:SubscriptionModelID" json:"subscription_model"`
ParentMembershipID int64 `json:"parent_member_id" validate:"omitempty,omitnil,number"` ParentMembershipID int64 `json:"parent_member_id" validate:"omitempty,omitnil,number"`
SubscriptionModelID int64 `json:"subsription_model_id"` SubscriptionModelID int64 `json:"subsription_model_id"`

View File

@@ -7,13 +7,13 @@ import (
type SubscriptionModel struct { type SubscriptionModel struct {
CreatedAt time.Time CreatedAt time.Time
UpdatedAt time.Time UpdatedAt time.Time
Name string `json:"name" validate:"required,subscriptionModel"` Name string `gorm:"unique" json:"name" validate:"required,subscriptionModel,safe_content"`
Details string `json:"details" validate:"required"` Details string `json:"details" validate:"required"`
Conditions string `json:"conditions"` Conditions string `json:"conditions"`
RequiredMembershipField string `json:"required_membership_field" validate:"membershipField"` RequiredMembershipField string `json:"required_membership_field" validate:"membershipField"`
ID int64 `gorm:"primaryKey"` ID int64 `gorm:"primaryKey"`
MonthlyFee float32 `json:"monthly_fee" validate:"required,number"` MonthlyFee float32 `json:"monthly_fee" validate:"required,number,gte=0"`
HourlyRate float32 `json:"hourly_rate" validate:"required,number"` HourlyRate float32 `json:"hourly_rate" validate:"required,number,gte=0"`
IncludedPerYear int16 `json:"included_hours_per_year" validate:"omitempty,number"` IncludedPerYear int16 `json:"included_hours_per_year" validate:"omitempty,number,gte=0"`
IncludedPerMonth int16 `json:"included_hours_per_month" validate:"omitempty,number"` IncludedPerMonth int16 `json:"included_hours_per_month" validate:"omitempty,number,gte=0"`
} }

View File

@@ -12,17 +12,17 @@ type User struct {
UpdatedAt time.Time UpdatedAt time.Time
DateOfBirth time.Time `gorm:"not null" json:"date_of_birth" validate:"required,age"` DateOfBirth time.Time `gorm:"not null" json:"date_of_birth" validate:"required,age"`
CreatedAt time.Time CreatedAt time.Time
Company string `json:"company" validate:"omitempty,omitnil"` Company string `json:"company" validate:"omitempty,omitnil,safe_content"`
Phone string `json:"phone" validate:"omitempty,omitnil"` Phone string `json:"phone" validate:"omitempty,omitnil,safe_content"`
Notes *string `json:"notes"` Notes *string `json:"notes,safe_content"`
FirstName string `gorm:"not null" json:"first_name" validate:"required"` FirstName string `gorm:"not null" json:"first_name" validate:"required,safe_content"`
Password string `json:"password" validate:"required_unless=RoleID 0"` Password string `json:"password" validate:"required_unless=RoleID 0,safe_content"`
Email string `gorm:"unique;not null" json:"email" validate:"required,email"` Email string `gorm:"unique;not null" json:"email" validate:"required,email,safe_content"`
LastName string `gorm:"not null" json:"last_name" validate:"required"` LastName string `gorm:"not null" json:"last_name" validate:"required,safe_content"`
ProfilePicture string `json:"profile_picture" validate:"omitempty,omitnil,image"` ProfilePicture string `json:"profile_picture" validate:"omitempty,omitnil,image,safe_content"`
Address string `gorm:"not null" json:"address" validate:"required"` Address string `gorm:"not null" json:"address" validate:"required,safe_content"`
ZipCode string `gorm:"not null" json:"zip_code" validate:"required,alphanum"` ZipCode string `gorm:"not null" json:"zip_code" validate:"required,alphanum,safe_content"`
City string `form:"not null" json:"city" validate:"required,alphaunicode"` City string `form:"not null" json:"city" validate:"required,alphaunicode,safe_content"`
Consents []Consent `gorm:"constraint:OnUpdate:CASCADE"` Consents []Consent `gorm:"constraint:OnUpdate:CASCADE"`
BankAccount BankAccount `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"bank_account"` BankAccount BankAccount `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"bank_account"`
Verification Verification `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"` Verification Verification `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`

View File

@@ -7,7 +7,9 @@ import (
"GoMembership/internal/models" "GoMembership/internal/models"
"GoMembership/pkg/logger" "GoMembership/pkg/logger"
"reflect" "reflect"
"regexp"
"slices" "slices"
"strings"
"time" "time"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
@@ -15,19 +17,24 @@ import (
"github.com/jbub/banking/swift" "github.com/jbub/banking/swift"
) )
// var xssPatterns = []*regexp.Regexp{
// func IsEmailValid(email string) bool { regexp.MustCompile(`(?i)<script`),
// regex := `^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}$` regexp.MustCompile(`(?i)javascript:`),
// re := regexp.MustCompile(regex) regexp.MustCompile(`(?i)on\w+\s*=`),
// return re.MatchString(email) regexp.MustCompile(`(?i)(vbscript|data):`),
// } regexp.MustCompile(`(?i)<(iframe|object|embed|applet)`),
regexp.MustCompile(`(?i)expression\s*\(`),
regexp.MustCompile(`(?i)url\s*\(`),
regexp.MustCompile(`(?i)<\?`),
regexp.MustCompile(`(?i)<%`),
regexp.MustCompile(`(?i)<!\[CDATA\[`),
regexp.MustCompile(`(?i)<(svg|animate)`),
regexp.MustCompile(`(?i)<(audio|video|source)`),
regexp.MustCompile(`(?i)base64`),
}
func AgeValidator(fl validator.FieldLevel) bool { func AgeValidator(fl validator.FieldLevel) bool {
fieldValue := fl.Field() fieldValue := fl.Field()
// Ensure the field is of type time.Time
// if fieldValue.Kind() != reflect.Struct || !fieldValue.Type().ConvertibleTo(reflect.TypeOf(time.Time{})) {
// return false
// }
dateOfBirth := fieldValue.Interface().(time.Time) dateOfBirth := fieldValue.Interface().(time.Time)
now := time.Now() now := time.Now()
age := now.Year() - dateOfBirth.Year() age := now.Year() - dateOfBirth.Year()
@@ -113,3 +120,13 @@ func BICValidator(fl validator.FieldLevel) bool {
return swift.Validate(fieldValue) == nil return swift.Validate(fieldValue) == nil
} }
func ValidateSafeContent(fl validator.FieldLevel) bool {
input := strings.ToLower(fl.Field().String())
for _, pattern := range xssPatterns {
if pattern.MatchString(input) {
return false
}
}
return true
}