From ef8d8de892c716bbe951f3ac3aae9e790899a56d Mon Sep 17 00:00:00 2001 From: wiwi Date: Mon, 19 Jan 2026 09:03:29 +0000 Subject: [PATCH] add: validation of sucscriptionID --- validator/subscription.go | 26 ++++++++++++++++ validator/subscription_test.go | 57 ++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 validator/subscription.go create mode 100644 validator/subscription_test.go diff --git a/validator/subscription.go b/validator/subscription.go new file mode 100644 index 0000000..046fded --- /dev/null +++ b/validator/subscription.go @@ -0,0 +1,26 @@ +package validator + +import "unicode" + +// Check empty strings, control characters (including NUL), +// Check characters that would affect URL parsing such as '/', '?', '#'. +func IsValidSubscriptionID(id string) bool { + if len(id) == 0 { + return false + } + + for _, r := range id { + if r <= 31 || r == 127 { // control characters + return false + } + switch r { + case '/', '?', '#': + return false + } + if unicode.IsSpace(r) { + return false + } + } + + return true +} diff --git a/validator/subscription_test.go b/validator/subscription_test.go new file mode 100644 index 0000000..1e28713 --- /dev/null +++ b/validator/subscription_test.go @@ -0,0 +1,57 @@ +package validator + +import ( + "testing" +) + +func TestIsValidSubscriptionID(t *testing.T) { + type Args struct { + id string + } + + type testCase struct { + name string + Args Args + Want bool + } + + runTests := func(t *testing.T, tests []testCase) { + t.Helper() + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := IsValidSubscriptionID(tt.Args.id); got != tt.Want { + t.Errorf("IsValidSubscriptionID() = %v, Want %v (input: %q)", got, tt.Want, tt.Args.id) + } + }) + } + } + + t.Run("Valid", func(t *testing.T) { + tests := []testCase{ + {"Numeric", Args{"123"}, true}, + {"AlphaNumeric", Args{"sub-abc-123"}, true}, + {"LongID", Args{"a-very-long-subscription-id-0123456789"}, true}, + } + runTests(t, tests) + }) + + t.Run("Invalid_ControlChars", func(t *testing.T) { + tests := []testCase{ + {"Empty", Args{""}, false}, + {"NullByte", Args{"abc\x00def"}, false}, + {"Ctrl1", Args{"abc\x01"}, false}, + {"DEL", Args{"abc\x7f"}, false}, + {"ContainsSpace", Args{"sub id"}, false}, + } + runTests(t, tests) + }) + + t.Run("Invalid_URLChars", func(t *testing.T) { + tests := []testCase{ + {"Slash", Args{"a/b"}, false}, + {"Question", Args{"a?b"}, false}, + {"Hash", Args{"a#b"}, false}, + } + runTests(t, tests) + }) +}