xss mitigation & test
This commit is contained in:
31
internal/controllers/XSS_test.go
Normal file
31
internal/controllers/XSS_test.go
Normal 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)
|
||||||
|
}
|
||||||
@@ -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"`
|
||||||
|
|||||||
@@ -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"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"`
|
||||||
|
|||||||
@@ -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"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;"`
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user