From ffb4ab2295f630c6a526041d9a89940d7db09ce1 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Sun, 1 Mar 2026 13:08:31 +0300 Subject: [PATCH] internal/normalize: harden against possible attacks; optimize registry Signed-off-by: NotAShelf Change-Id: Iaf89cda3c480d6a8371e5f146ee95fcf6a6a6964 --- internal/normalize/path.go | 16 +++++++++------- internal/normalize/referrer.go | 5 +++-- internal/normalize/referrer_registry.go | 14 ++++++++++++-- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/internal/normalize/path.go b/internal/normalize/path.go index e142651..2148f34 100644 --- a/internal/normalize/path.go +++ b/internal/normalize/path.go @@ -6,20 +6,22 @@ import ( "notashelf.dev/watchdog/internal/config" ) -const maxPathLength = 2048 - type PathNormalizer struct { - cfg config.PathConfig + cfg config.PathConfig + maxLength int } func NewPathNormalizer(cfg config.PathConfig) *PathNormalizer { - return &PathNormalizer{cfg: cfg} + return &PathNormalizer{ + cfg: cfg, + maxLength: 2048, + } } func (n *PathNormalizer) Normalize(path string) string { - // Return as-is if path is too long - if len(path) > maxPathLength { - return path + // Reject paths that are too long; don't bypass normalization + if len(path) > n.maxLength { + return "/" } if path == "" { diff --git a/internal/normalize/referrer.go b/internal/normalize/referrer.go index a785463..df0189d 100644 --- a/internal/normalize/referrer.go +++ b/internal/normalize/referrer.go @@ -89,8 +89,9 @@ func ExtractReferrerDomain(referrer, siteDomain string) string { // - "news.ycombinator.com" -> "ycombinator.com" eTLDPlus1, err := publicsuffix.EffectiveTLDPlusOne(hostname) if err != nil { - // If public suffix lookup fails, use hostname as-is - return hostname + // If public suffix lookup fails (malformed/unknown TLD), return "other" + // to prevent unbounded cardinality from malicious referrers + return "other" } return eTLDPlus1 diff --git a/internal/normalize/referrer_registry.go b/internal/normalize/referrer_registry.go index b573309..daaf69c 100644 --- a/internal/normalize/referrer_registry.go +++ b/internal/normalize/referrer_registry.go @@ -17,16 +17,26 @@ func NewReferrerRegistry(maxSources int) *ReferrerRegistry { } } -// Attempt to add a referrer source to the registry. Returns the source to use ("other" if rejected). +// Attempt to add a referrer source to the registry. +// Returns the source to use ("other" if rejected). func (r *ReferrerRegistry) Add(source string) string { if source == "direct" || source == "internal" { return source } + // Fast path: check with read lock first + r.mu.RLock() + if _, exists := r.sources[source]; exists { + r.mu.RUnlock() + return source + } + r.mu.RUnlock() + + // Slow path: acquire write lock to add r.mu.Lock() defer r.mu.Unlock() - // Already exists + // Double-check after acquiring write lock, another goroutine might have added it beforehand if _, exists := r.sources[source]; exists { return source }