Allow more permissive bracket validations
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
@@ -182,13 +182,6 @@ func TestAudit_Create_Validation(t *testing.T) {
|
||||
},
|
||||
wantErrorContains: "HTML",
|
||||
},
|
||||
{
|
||||
name: "name with angle brackets",
|
||||
input: map[string]any{
|
||||
"name": "Test < Audit",
|
||||
},
|
||||
wantErrorContains: "angle brackets",
|
||||
},
|
||||
{
|
||||
name: "name with newline",
|
||||
input: map[string]any{
|
||||
@@ -425,14 +418,6 @@ func TestAudit_Update_Validation(t *testing.T) {
|
||||
},
|
||||
wantErrorContains: "HTML",
|
||||
},
|
||||
{
|
||||
name: "name with angle brackets",
|
||||
setup: func() string { return baseAuditID },
|
||||
input: func(id string) map[string]any {
|
||||
return map[string]any{"id": id, "name": "Test < Audit"}
|
||||
},
|
||||
wantErrorContains: "angle brackets",
|
||||
},
|
||||
{
|
||||
name: "name with newline",
|
||||
setup: func() string { return baseAuditID },
|
||||
|
||||
@@ -174,14 +174,6 @@ func TestDatum_Create_Validation(t *testing.T) {
|
||||
},
|
||||
wantErrorContains: "HTML",
|
||||
},
|
||||
{
|
||||
name: "name with angle brackets",
|
||||
input: map[string]any{
|
||||
"name": "Test < Datum",
|
||||
"dataClassification": "INTERNAL",
|
||||
},
|
||||
wantErrorContains: "angle brackets",
|
||||
},
|
||||
{
|
||||
name: "name with newline",
|
||||
input: map[string]any{
|
||||
@@ -397,14 +389,6 @@ func TestDatum_Update_Validation(t *testing.T) {
|
||||
},
|
||||
wantErrorContains: "HTML",
|
||||
},
|
||||
{
|
||||
name: "name with angle brackets",
|
||||
setup: func() string { return baseDatumID },
|
||||
input: func(id string) map[string]any {
|
||||
return map[string]any{"id": id, "name": "Test < Datum"}
|
||||
},
|
||||
wantErrorContains: "angle brackets",
|
||||
},
|
||||
{
|
||||
name: "name with newline",
|
||||
setup: func() string { return baseDatumID },
|
||||
|
||||
@@ -192,16 +192,6 @@ func TestDocument_Create_Validation(t *testing.T) {
|
||||
},
|
||||
wantErrorContains: "HTML",
|
||||
},
|
||||
{
|
||||
name: "title with angle brackets",
|
||||
input: map[string]any{
|
||||
"title": "Test < Document",
|
||||
"content": "Test content",
|
||||
"documentType": "POLICY",
|
||||
"classification": "INTERNAL",
|
||||
},
|
||||
wantErrorContains: "angle brackets",
|
||||
},
|
||||
{
|
||||
name: "title with newline",
|
||||
input: map[string]any{
|
||||
@@ -418,14 +408,6 @@ func TestDocument_Update_Validation(t *testing.T) {
|
||||
},
|
||||
wantErrorContains: "HTML",
|
||||
},
|
||||
{
|
||||
name: "title with angle brackets",
|
||||
setup: func() string { return baseDocumentID },
|
||||
input: func(id string) map[string]any {
|
||||
return map[string]any{"id": id, "title": "Test < Document"}
|
||||
},
|
||||
wantErrorContains: "angle brackets",
|
||||
},
|
||||
{
|
||||
name: "title with newline",
|
||||
setup: func() string { return baseDocumentID },
|
||||
|
||||
@@ -144,13 +144,6 @@ func TestFramework_Create_Validation(t *testing.T) {
|
||||
},
|
||||
wantErrorContains: "HTML",
|
||||
},
|
||||
{
|
||||
name: "name with angle brackets",
|
||||
input: map[string]any{
|
||||
"name": "Test < Framework",
|
||||
},
|
||||
wantErrorContains: "angle brackets",
|
||||
},
|
||||
// Newline validation
|
||||
{
|
||||
name: "name with newline",
|
||||
@@ -359,14 +352,6 @@ func TestFramework_Update_Validation(t *testing.T) {
|
||||
},
|
||||
wantErrorContains: "HTML",
|
||||
},
|
||||
{
|
||||
name: "name with angle brackets",
|
||||
setup: func() string { return baseFrameworkID },
|
||||
input: func(id string) map[string]any {
|
||||
return map[string]any{"id": id, "name": "Test < Framework"}
|
||||
},
|
||||
wantErrorContains: "angle brackets",
|
||||
},
|
||||
{
|
||||
name: "description with HTML tags",
|
||||
setup: func() string { return baseFrameworkID },
|
||||
|
||||
@@ -205,14 +205,6 @@ func TestMeasure_Create_Validation(t *testing.T) {
|
||||
},
|
||||
wantErrorContains: "HTML",
|
||||
},
|
||||
{
|
||||
name: "name with angle brackets",
|
||||
input: map[string]any{
|
||||
"name": "Test < Measure",
|
||||
"category": "POLICY",
|
||||
},
|
||||
wantErrorContains: "angle brackets",
|
||||
},
|
||||
{
|
||||
name: "description with HTML tags",
|
||||
input: map[string]any{
|
||||
@@ -473,14 +465,6 @@ func TestMeasure_Update_Validation(t *testing.T) {
|
||||
},
|
||||
wantErrorContains: "HTML",
|
||||
},
|
||||
{
|
||||
name: "name with angle brackets",
|
||||
setup: func() string { return baseMeasureID },
|
||||
input: func(id string) map[string]any {
|
||||
return map[string]any{"id": id, "name": "Test < Measure"}
|
||||
},
|
||||
wantErrorContains: "angle brackets",
|
||||
},
|
||||
{
|
||||
name: "description with HTML tags",
|
||||
setup: func() string { return baseMeasureID },
|
||||
|
||||
@@ -135,14 +135,6 @@ func TestMeeting_Create_Validation(t *testing.T) {
|
||||
},
|
||||
wantErrorContains: "HTML",
|
||||
},
|
||||
{
|
||||
name: "name with angle brackets",
|
||||
input: map[string]any{
|
||||
"name": "Test < Meeting",
|
||||
"date": time.Now().Format(time.RFC3339Nano),
|
||||
},
|
||||
wantErrorContains: "angle brackets",
|
||||
},
|
||||
{
|
||||
name: "name with newline",
|
||||
input: map[string]any{
|
||||
@@ -343,14 +335,6 @@ func TestMeeting_Update_Validation(t *testing.T) {
|
||||
},
|
||||
wantErrorContains: "HTML",
|
||||
},
|
||||
{
|
||||
name: "name with angle brackets",
|
||||
setup: func() string { return baseMeetingID },
|
||||
input: func(id string) map[string]any {
|
||||
return map[string]any{"meetingId": id, "name": "Test < Meeting"}
|
||||
},
|
||||
wantErrorContains: "angle brackets",
|
||||
},
|
||||
{
|
||||
name: "name with newline",
|
||||
setup: func() string { return baseMeetingID },
|
||||
|
||||
@@ -17,18 +17,17 @@ package validator
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
htmlTagRegex = regexp.MustCompile(`<[^>]*>`)
|
||||
htmlTagRegex = regexp.MustCompile(`</?[a-zA-Z][^>]*>|<![^>]*>`)
|
||||
)
|
||||
|
||||
// NoHTML validates that a string does not contain HTML tags or angle brackets.
|
||||
// NoHTML validates that a string does not contain HTML tags.
|
||||
// It rejects:
|
||||
// - HTML tags (e.g., <script>, <b>, <div>, etc.)
|
||||
// - Angle brackets (< and >) even when not part of complete tags
|
||||
//
|
||||
// Plain angle brackets used outside of HTML tags (e.g., "5 < 10") are allowed.
|
||||
// This helps prevent XSS attacks and ensures user input doesn't contain HTML markup.
|
||||
// Combine with PrintableText() for comprehensive text field validation.
|
||||
func NoHTML() ValidatorFunc {
|
||||
@@ -47,16 +46,10 @@ func NoHTML() ValidatorFunc {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check for HTML tags first (more specific error message)
|
||||
if htmlTagRegex.MatchString(str) {
|
||||
return newValidationError(ErrorCodeInvalidFormat, "must not contain HTML tags")
|
||||
}
|
||||
|
||||
// Check for angle brackets (even without complete tags)
|
||||
if strings.ContainsAny(str, "<>") {
|
||||
return newValidationError(ErrorCodeInvalidFormat, "must not contain angle brackets")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,38 +106,35 @@ func TestNoHTML(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid - less than symbol", func(t *testing.T) {
|
||||
t.Run("valid - less than symbol", func(t *testing.T) {
|
||||
str := "5 < 10"
|
||||
err := NoHTML()(&str)
|
||||
if err == nil {
|
||||
t.Fatal("expected validation error for angle bracket")
|
||||
}
|
||||
if !strings.Contains(err.Message, "angle brackets") {
|
||||
t.Errorf("unexpected error message: %s", err.Message)
|
||||
if err != nil {
|
||||
t.Errorf("expected no error for bare angle bracket, got: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid - greater than symbol", func(t *testing.T) {
|
||||
t.Run("valid - greater than symbol", func(t *testing.T) {
|
||||
str := "10 > 5"
|
||||
err := NoHTML()(&str)
|
||||
if err == nil {
|
||||
t.Error("expected validation error for angle bracket")
|
||||
if err != nil {
|
||||
t.Errorf("expected no error for bare angle bracket, got: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid - both angle brackets", func(t *testing.T) {
|
||||
t.Run("valid - both angle brackets", func(t *testing.T) {
|
||||
str := "5 < x > 10"
|
||||
err := NoHTML()(&str)
|
||||
if err == nil {
|
||||
t.Error("expected validation error for angle brackets")
|
||||
if err != nil {
|
||||
t.Errorf("expected no error for bare angle brackets, got: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid - malformed tag", func(t *testing.T) {
|
||||
t.Run("valid - incomplete tag", func(t *testing.T) {
|
||||
str := "text <incomplete"
|
||||
err := NoHTML()(&str)
|
||||
if err == nil {
|
||||
t.Error("expected validation error for incomplete tag")
|
||||
if err != nil {
|
||||
t.Errorf("expected no error for incomplete tag, got: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -149,6 +146,126 @@ func TestNoHTML(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid - svg onload XSS", func(t *testing.T) {
|
||||
str := `<svg onload=alert(1)>`
|
||||
err := NoHTML()(&str)
|
||||
if err == nil {
|
||||
t.Error("expected validation error for svg tag")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid - svg with slash", func(t *testing.T) {
|
||||
str := `<svg/onload=alert(1)>`
|
||||
err := NoHTML()(&str)
|
||||
if err == nil {
|
||||
t.Error("expected validation error for svg/onload tag")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid - iframe tag", func(t *testing.T) {
|
||||
str := `<iframe src="javascript:alert(1)">`
|
||||
err := NoHTML()(&str)
|
||||
if err == nil {
|
||||
t.Error("expected validation error for iframe tag")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid - style tag", func(t *testing.T) {
|
||||
str := `<style>body{background:url(evil)}</style>`
|
||||
err := NoHTML()(&str)
|
||||
if err == nil {
|
||||
t.Error("expected validation error for style tag")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid - HTML comment", func(t *testing.T) {
|
||||
str := `<!-- comment -->`
|
||||
err := NoHTML()(&str)
|
||||
if err == nil {
|
||||
t.Error("expected validation error for HTML comment")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid - DOCTYPE", func(t *testing.T) {
|
||||
str := `<!DOCTYPE html>`
|
||||
err := NoHTML()(&str)
|
||||
if err == nil {
|
||||
t.Error("expected validation error for DOCTYPE")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid - details ontoggle XSS", func(t *testing.T) {
|
||||
str := `<details open ontoggle=alert(1)>`
|
||||
err := NoHTML()(&str)
|
||||
if err == nil {
|
||||
t.Error("expected validation error for details tag")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid - body onload XSS", func(t *testing.T) {
|
||||
str := `<body onload=alert(1)>`
|
||||
err := NoHTML()(&str)
|
||||
if err == nil {
|
||||
t.Error("expected validation error for body tag")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid - object tag", func(t *testing.T) {
|
||||
str := `<object data="evil.swf">`
|
||||
err := NoHTML()(&str)
|
||||
if err == nil {
|
||||
t.Error("expected validation error for object tag")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid - embed tag", func(t *testing.T) {
|
||||
str := `<embed src="evil.swf">`
|
||||
err := NoHTML()(&str)
|
||||
if err == nil {
|
||||
t.Error("expected validation error for embed tag")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid - meta refresh", func(t *testing.T) {
|
||||
str := `<meta http-equiv="refresh" content="0;url=evil">`
|
||||
err := NoHTML()(&str)
|
||||
if err == nil {
|
||||
t.Error("expected validation error for meta tag")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid - input with autofocus XSS", func(t *testing.T) {
|
||||
str := `<input onfocus=alert(1) autofocus>`
|
||||
err := NoHTML()(&str)
|
||||
if err == nil {
|
||||
t.Error("expected validation error for input tag")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid - tag with newlines in attributes", func(t *testing.T) {
|
||||
str := "<img\nsrc=x\nonerror=alert(1)>"
|
||||
err := NoHTML()(&str)
|
||||
if err == nil {
|
||||
t.Error("expected validation error for tag with newlines")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("valid - math expression", func(t *testing.T) {
|
||||
str := "if x < 10 then y = 20"
|
||||
err := NoHTML()(&str)
|
||||
if err != nil {
|
||||
t.Errorf("expected no error for math expression, got: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("valid - arrow notation", func(t *testing.T) {
|
||||
str := "use -> or => for arrows"
|
||||
err := NoHTML()(&str)
|
||||
if err != nil {
|
||||
t.Errorf("expected no error for arrow notation, got: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("empty string", func(t *testing.T) {
|
||||
str := ""
|
||||
err := NoHTML()(&str)
|
||||
@@ -209,13 +326,13 @@ func TestNoHTML(t *testing.T) {
|
||||
errors := v.Errors()
|
||||
found := false
|
||||
for _, err := range errors {
|
||||
if strings.Contains(err.Message, "HTML tags") || strings.Contains(err.Message, "angle brackets") {
|
||||
if strings.Contains(err.Message, "HTML tags") {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Error("expected error about HTML tags or angle brackets")
|
||||
t.Error("expected error about HTML tags")
|
||||
}
|
||||
})
|
||||
|
||||
@@ -623,14 +740,11 @@ func TestSafeText(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid - contains angle brackets", func(t *testing.T) {
|
||||
t.Run("valid - contains angle brackets", func(t *testing.T) {
|
||||
str := "5 < 10"
|
||||
err := SafeText(100)(&str)
|
||||
if err == nil {
|
||||
t.Fatal("expected validation error for angle brackets")
|
||||
}
|
||||
if !strings.Contains(err.Message, "angle brackets") {
|
||||
t.Errorf("unexpected error message: %s", err.Message)
|
||||
if err != nil {
|
||||
t.Errorf("expected no error for bare angle brackets, got: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -743,13 +857,13 @@ func TestSafeText(t *testing.T) {
|
||||
errors := v.Errors()
|
||||
found := false
|
||||
for _, err := range errors {
|
||||
if strings.Contains(err.Message, "HTML tags") || strings.Contains(err.Message, "angle brackets") {
|
||||
if strings.Contains(err.Message, "HTML tags") {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Error("expected error about HTML tags or angle brackets")
|
||||
t.Error("expected error about HTML tags")
|
||||
}
|
||||
})
|
||||
|
||||
@@ -922,4 +1036,3 @@ func TestSafeTextNoNewLine(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user