|
|
|
|
@ -4,7 +4,9 @@ package security
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"regexp"
|
|
|
|
|
"strings"
|
|
|
|
|
"testing"
|
|
|
|
|
"unicode/utf8"
|
|
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
)
|
|
|
|
|
@ -51,6 +53,29 @@ func TestSanitizeTheme(t *testing.T) {
|
|
|
|
|
require.Equal(t, "Science Fiction", SanitizeTheme(" science fiction "))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TestSanitizeLengthBoundaries verifies exact max length is preserved and over-limit input is truncated.
|
|
|
|
|
func TestSanitizeLengthBoundaries(t *testing.T) {
|
|
|
|
|
playerExact := strings.Repeat("a", PlayerNameMaxLength)
|
|
|
|
|
playerOver := strings.Repeat("a", PlayerNameMaxLength+1)
|
|
|
|
|
require.Equal(t, PlayerNameMaxLength, len(SanitizePlayerName(playerExact)))
|
|
|
|
|
require.Equal(t, PlayerNameMaxLength, len(SanitizePlayerName(playerOver)))
|
|
|
|
|
|
|
|
|
|
answerExact := strings.Repeat("x", AnswerMaxLength)
|
|
|
|
|
answerOver := strings.Repeat("x", AnswerMaxLength+1)
|
|
|
|
|
require.Equal(t, AnswerMaxLength, len(SanitizeAnswer(answerExact)))
|
|
|
|
|
require.Equal(t, AnswerMaxLength, len(SanitizeAnswer(answerOver)))
|
|
|
|
|
|
|
|
|
|
questionExact := strings.Repeat("q", QuestionTextMaxLength)
|
|
|
|
|
questionOver := strings.Repeat("q", QuestionTextMaxLength+1)
|
|
|
|
|
require.Equal(t, QuestionTextMaxLength, len(SanitizeQuestionText(questionExact)))
|
|
|
|
|
require.Equal(t, QuestionTextMaxLength, len(SanitizeQuestionText(questionOver)))
|
|
|
|
|
|
|
|
|
|
themeExact := strings.Repeat("z", ThemeMaxLength)
|
|
|
|
|
themeOver := strings.Repeat("z", ThemeMaxLength+1)
|
|
|
|
|
require.Equal(t, ThemeMaxLength, len(SanitizeTheme(themeExact)))
|
|
|
|
|
require.Equal(t, ThemeMaxLength, len(SanitizeTheme(themeOver)))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TestRemoveHTMLTags verifies all HTML tags are stripped from input.
|
|
|
|
|
func TestRemoveHTMLTags(t *testing.T) {
|
|
|
|
|
require.Equal(t, "Hi", RemoveHTMLTags("<b>Hi</b>"))
|
|
|
|
|
@ -58,8 +83,29 @@ func TestRemoveHTMLTags(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
// TestContainsDangerousPatterns detects known dangerous substrings.
|
|
|
|
|
func TestContainsDangerousPatterns(t *testing.T) {
|
|
|
|
|
require.True(t, ContainsDangerousPatterns("javascript:alert(1)"))
|
|
|
|
|
require.False(t, ContainsDangerousPatterns("hello"))
|
|
|
|
|
cases := []struct {
|
|
|
|
|
name string
|
|
|
|
|
input string
|
|
|
|
|
expected bool
|
|
|
|
|
}{
|
|
|
|
|
{name: "javascript", input: "javascript:alert(1)", expected: true},
|
|
|
|
|
{name: "mixed_case_javascript", input: "JaVaScRiPt:alert(1)", expected: true},
|
|
|
|
|
{name: "data", input: "data:text/html;base64,abc", expected: true},
|
|
|
|
|
{name: "vbscript", input: "vbscript:msgbox(1)", expected: true},
|
|
|
|
|
{name: "onerror", input: `<img src=x onerror="alert(1)">`, expected: true},
|
|
|
|
|
{name: "onload", input: `<body onload="x()">`, expected: true},
|
|
|
|
|
{name: "onclick", input: `<a onclick="x()">link</a>`, expected: true},
|
|
|
|
|
{name: "onmouseover", input: `<div onmouseover="x()">`, expected: true},
|
|
|
|
|
{name: "benign", input: "hello", expected: false},
|
|
|
|
|
{name: "benign_near_miss_1", input: "onboarding flow", expected: false},
|
|
|
|
|
{name: "benign_near_miss_2", input: "scripture reference", expected: false},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tc := range cases {
|
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
|
require.Equal(t, tc.expected, ContainsDangerousPatterns(tc.input))
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TestIsValidEmail verifies basic email pattern validation.
|
|
|
|
|
@ -67,3 +113,19 @@ func TestIsValidEmail(t *testing.T) {
|
|
|
|
|
require.True(t, IsValidEmail("a@b.com"))
|
|
|
|
|
require.False(t, IsValidEmail("bad@"))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TestSanitizeRuneSafeClamping verifies rune-based truncation remains valid UTF-8 and deterministic.
|
|
|
|
|
func TestSanitizeRuneSafeClamping(t *testing.T) {
|
|
|
|
|
input := strings.Repeat("é", 5)
|
|
|
|
|
opts := SanitizeOptions{
|
|
|
|
|
TrimWhitespace: false,
|
|
|
|
|
RemoveMultipleSpaces: false,
|
|
|
|
|
HTMLEscape: false,
|
|
|
|
|
MaxLength: 3,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result := Sanitize(input, opts)
|
|
|
|
|
require.True(t, utf8.ValidString(result))
|
|
|
|
|
require.Equal(t, 3, utf8.RuneCountInString(result))
|
|
|
|
|
require.Equal(t, "ééé", result)
|
|
|
|
|
}
|
|
|
|
|
|