From 2799045ee7cc1884504d065cd2c901e4834d70bc Mon Sep 17 00:00:00 2001 From: CarterPerez-dev Date: Thu, 14 May 2026 00:31:49 -0400 Subject: [PATCH] refactor(canary): consolidate realIP into middleware across 5 generators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes deferred Phase-9 item 8. Phase 9 promoted realIP / lastNonEmptyXFF / OptionalHeader to internal/middleware and refactored webbug only; the other generators kept their byte-identical local copies. Sweeping them all over now while the touch surface is already open for Phase 10. - docx, envfile, kubeconfig, pdf, slowredirect: drop the local realIP / lastNonEmptyXFF / optionalHeader functions plus the CF-Connecting-IP / X-Forwarded-For / X-Real-IP header constants. Trigger paths now call middleware.RealIP / middleware.OptionalHeader directly. Drops the unused "net" import from each. - slowredirect/fingerprint_handler.go: same treatment for the AttachFingerprint call site. No behavior change — middleware.RealIP is byte-identical to what was previously inlined per generator; verified by re-running the per-package trigger tests post-refactor. --- .../token/generators/docx/generator.go | 54 +++--------------- .../token/generators/envfile/generator.go | 54 +++--------------- .../token/generators/kubeconfig/handler.go | 57 +++---------------- .../token/generators/pdf/generator.go | 54 +++--------------- .../slowredirect/fingerprint_handler.go | 3 +- .../generators/slowredirect/generator.go | 56 +++--------------- 6 files changed, 39 insertions(+), 239 deletions(-) diff --git a/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/docx/generator.go b/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/docx/generator.go index a418cc0c..3b8ed5b4 100644 --- a/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/docx/generator.go +++ b/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/docx/generator.go @@ -10,23 +10,20 @@ import ( _ "embed" "fmt" "io" - "net" "net/http" "strings" "github.com/CarterPerez-dev/cybersecurity-projects/canary-token-generator/backend/internal/event" + "github.com/CarterPerez-dev/cybersecurity-projects/canary-token-generator/backend/internal/middleware" "github.com/CarterPerez-dev/cybersecurity-projects/canary-token-generator/backend/internal/token" "github.com/CarterPerez-dev/cybersecurity-projects/canary-token-generator/backend/internal/token/generators" "github.com/CarterPerez-dev/cybersecurity-projects/canary-token-generator/backend/internal/token/generators/pixel" ) const ( - headerCFConnectingIP = "CF-Connecting-IP" - headerXForwardedFor = "X-Forwarded-For" - headerXRealIP = "X-Real-IP" - headerReferer = "Referer" - headerCacheControl = "Cache-Control" - headerPragma = "Pragma" + headerReferer = "Referer" + headerCacheControl = "Cache-Control" + headerPragma = "Pragma" cacheControlNoStore = "no-store, no-cache, must-revalidate, max-age=0" pragmaNoCache = "no-cache" @@ -90,9 +87,9 @@ func (g *Generator) Trigger( evt := &event.Event{ TokenID: t.ID, - SourceIP: realIP(r), - UserAgent: optionalHeader(r.UserAgent()), - Referer: optionalHeader(r.Header.Get(headerReferer)), + SourceIP: middleware.RealIP(r), + UserAgent: middleware.OptionalHeader(r.UserAgent()), + Referer: middleware.OptionalHeader(r.Header.Get(headerReferer)), } return evt, resp, nil } @@ -153,40 +150,3 @@ func patchTemplate(template []byte, triggerURL string) ([]byte, error) { } return out.Bytes(), nil } - -func optionalHeader(v string) *string { - v = strings.TrimSpace(v) - if v == "" { - return nil - } - return &v -} - -func realIP(r *http.Request) string { - if v := strings.TrimSpace(r.Header.Get(headerCFConnectingIP)); v != "" { - return v - } - if v := lastNonEmptyXFF(r.Header.Get(headerXForwardedFor)); v != "" { - return v - } - if v := strings.TrimSpace(r.Header.Get(headerXRealIP)); v != "" { - return v - } - if host, _, err := net.SplitHostPort(r.RemoteAddr); err == nil { - return host - } - return r.RemoteAddr -} - -func lastNonEmptyXFF(header string) string { - if header == "" { - return "" - } - parts := strings.Split(header, ",") - for i := len(parts) - 1; i >= 0; i-- { - if v := strings.TrimSpace(parts[i]); v != "" { - return v - } - } - return "" -} diff --git a/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/envfile/generator.go b/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/envfile/generator.go index 86a268bb..c3d0255a 100644 --- a/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/envfile/generator.go +++ b/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/envfile/generator.go @@ -10,11 +10,11 @@ import ( "encoding/json" "fmt" "math/big" - "net" "net/http" "strings" "github.com/CarterPerez-dev/cybersecurity-projects/canary-token-generator/backend/internal/event" + "github.com/CarterPerez-dev/cybersecurity-projects/canary-token-generator/backend/internal/middleware" "github.com/CarterPerez-dev/cybersecurity-projects/canary-token-generator/backend/internal/token" "github.com/CarterPerez-dev/cybersecurity-projects/canary-token-generator/backend/internal/token/generators" "github.com/CarterPerez-dev/cybersecurity-projects/canary-token-generator/backend/internal/token/generators/envfile/recipes" @@ -22,12 +22,9 @@ import ( ) const ( - headerCFConnectingIP = "CF-Connecting-IP" - headerXForwardedFor = "X-Forwarded-For" - headerXRealIP = "X-Real-IP" - headerReferer = "Referer" - headerCacheControl = "Cache-Control" - headerPragma = "Pragma" + headerReferer = "Referer" + headerCacheControl = "Cache-Control" + headerPragma = "Pragma" cacheControlNoStore = "no-store, no-cache, must-revalidate, max-age=0" pragmaNoCache = "no-cache" @@ -104,9 +101,9 @@ func (g *Generator) Trigger( evt := &event.Event{ TokenID: t.ID, - SourceIP: realIP(r), - UserAgent: optionalHeader(r.UserAgent()), - Referer: optionalHeader(r.Header.Get(headerReferer)), + SourceIP: middleware.RealIP(r), + UserAgent: middleware.OptionalHeader(r.UserAgent()), + Referer: middleware.OptionalHeader(r.Header.Get(headerReferer)), } return evt, resp, nil } @@ -205,40 +202,3 @@ func resolveFilename(name *string) string { } return trimmed } - -func optionalHeader(v string) *string { - v = strings.TrimSpace(v) - if v == "" { - return nil - } - return &v -} - -func realIP(r *http.Request) string { - if v := strings.TrimSpace(r.Header.Get(headerCFConnectingIP)); v != "" { - return v - } - if v := lastNonEmptyXFF(r.Header.Get(headerXForwardedFor)); v != "" { - return v - } - if v := strings.TrimSpace(r.Header.Get(headerXRealIP)); v != "" { - return v - } - if host, _, err := net.SplitHostPort(r.RemoteAddr); err == nil { - return host - } - return r.RemoteAddr -} - -func lastNonEmptyXFF(header string) string { - if header == "" { - return "" - } - parts := strings.Split(header, ",") - for i := len(parts) - 1; i >= 0; i-- { - if v := strings.TrimSpace(parts[i]); v != "" { - return v - } - } - return "" -} diff --git a/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/kubeconfig/handler.go b/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/kubeconfig/handler.go index c2028ece..0d974032 100644 --- a/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/kubeconfig/handler.go +++ b/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/kubeconfig/handler.go @@ -7,24 +7,20 @@ import ( "context" "encoding/json" "fmt" - "net" "net/http" "path" - "strings" "github.com/CarterPerez-dev/cybersecurity-projects/canary-token-generator/backend/internal/event" + "github.com/CarterPerez-dev/cybersecurity-projects/canary-token-generator/backend/internal/middleware" "github.com/CarterPerez-dev/cybersecurity-projects/canary-token-generator/backend/internal/token" "github.com/CarterPerez-dev/cybersecurity-projects/canary-token-generator/backend/internal/token/generators" ) const ( - headerCFConnectingIP = "CF-Connecting-IP" - headerXForwardedFor = "X-Forwarded-For" - headerXRealIP = "X-Real-IP" - headerReferer = "Referer" - headerUserAgent = "User-Agent" - headerCacheControl = "Cache-Control" - headerPragma = "Pragma" + headerReferer = "Referer" + headerUserAgent = "User-Agent" + headerCacheControl = "Cache-Control" + headerPragma = "Pragma" cacheControlNoStore = "no-store, no-cache, must-revalidate, max-age=0" pragmaNoCache = "no-cache" @@ -102,9 +98,9 @@ func (g *Generator) Trigger( evt := &event.Event{ TokenID: t.ID, - SourceIP: realIP(r), - UserAgent: optionalHeader(r.UserAgent()), - Referer: optionalHeader(r.Header.Get(headerReferer)), + SourceIP: middleware.RealIP(r), + UserAgent: middleware.OptionalHeader(r.UserAgent()), + Referer: middleware.OptionalHeader(r.Header.Get(headerReferer)), Extra: extra, } return evt, resp, nil @@ -162,40 +158,3 @@ func verbFromMethod(method string) string { return verbList } } - -func optionalHeader(v string) *string { - v = strings.TrimSpace(v) - if v == "" { - return nil - } - return &v -} - -func realIP(r *http.Request) string { - if v := strings.TrimSpace(r.Header.Get(headerCFConnectingIP)); v != "" { - return v - } - if v := lastNonEmptyXFF(r.Header.Get(headerXForwardedFor)); v != "" { - return v - } - if v := strings.TrimSpace(r.Header.Get(headerXRealIP)); v != "" { - return v - } - if host, _, err := net.SplitHostPort(r.RemoteAddr); err == nil { - return host - } - return r.RemoteAddr -} - -func lastNonEmptyXFF(header string) string { - if header == "" { - return "" - } - parts := strings.Split(header, ",") - for i := len(parts) - 1; i >= 0; i-- { - if v := strings.TrimSpace(parts[i]); v != "" { - return v - } - } - return "" -} diff --git a/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/pdf/generator.go b/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/pdf/generator.go index 09499d59..6b02b3c2 100644 --- a/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/pdf/generator.go +++ b/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/pdf/generator.go @@ -9,23 +9,20 @@ import ( _ "embed" "errors" "fmt" - "net" "net/http" "strings" "github.com/CarterPerez-dev/cybersecurity-projects/canary-token-generator/backend/internal/event" + "github.com/CarterPerez-dev/cybersecurity-projects/canary-token-generator/backend/internal/middleware" "github.com/CarterPerez-dev/cybersecurity-projects/canary-token-generator/backend/internal/token" "github.com/CarterPerez-dev/cybersecurity-projects/canary-token-generator/backend/internal/token/generators" "github.com/CarterPerez-dev/cybersecurity-projects/canary-token-generator/backend/internal/token/generators/pixel" ) const ( - headerCFConnectingIP = "CF-Connecting-IP" - headerXForwardedFor = "X-Forwarded-For" - headerXRealIP = "X-Real-IP" - headerReferer = "Referer" - headerCacheControl = "Cache-Control" - headerPragma = "Pragma" + headerReferer = "Referer" + headerCacheControl = "Cache-Control" + headerPragma = "Pragma" cacheControlNoStore = "no-store, no-cache, must-revalidate, max-age=0" pragmaNoCache = "no-cache" @@ -126,9 +123,9 @@ func (g *Generator) Trigger( evt := &event.Event{ TokenID: t.ID, - SourceIP: realIP(r), - UserAgent: optionalHeader(r.UserAgent()), - Referer: optionalHeader(r.Header.Get(headerReferer)), + SourceIP: middleware.RealIP(r), + UserAgent: middleware.OptionalHeader(r.UserAgent()), + Referer: middleware.OptionalHeader(r.Header.Get(headerReferer)), } return evt, resp, nil } @@ -143,40 +140,3 @@ func resolveFilename(name *string) string { } return trimmed } - -func optionalHeader(v string) *string { - v = strings.TrimSpace(v) - if v == "" { - return nil - } - return &v -} - -func realIP(r *http.Request) string { - if v := strings.TrimSpace(r.Header.Get(headerCFConnectingIP)); v != "" { - return v - } - if v := lastNonEmptyXFF(r.Header.Get(headerXForwardedFor)); v != "" { - return v - } - if v := strings.TrimSpace(r.Header.Get(headerXRealIP)); v != "" { - return v - } - if host, _, err := net.SplitHostPort(r.RemoteAddr); err == nil { - return host - } - return r.RemoteAddr -} - -func lastNonEmptyXFF(header string) string { - if header == "" { - return "" - } - parts := strings.Split(header, ",") - for i := len(parts) - 1; i >= 0; i-- { - if v := strings.TrimSpace(parts[i]); v != "" { - return v - } - } - return "" -} diff --git a/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/slowredirect/fingerprint_handler.go b/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/slowredirect/fingerprint_handler.go index afc44cd0..4ec322b4 100644 --- a/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/slowredirect/fingerprint_handler.go +++ b/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/slowredirect/fingerprint_handler.go @@ -16,6 +16,7 @@ import ( "github.com/go-chi/chi/v5" "github.com/CarterPerez-dev/cybersecurity-projects/canary-token-generator/backend/internal/event" + "github.com/CarterPerez-dev/cybersecurity-projects/canary-token-generator/backend/internal/middleware" ) const ( @@ -73,7 +74,7 @@ func (h *FingerprintHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if err := h.attacher.AttachFingerprint( ctx, tokenID, - realIP(r), + middleware.RealIP(r), json.RawMessage(body), h.window, ); err != nil && !errors.Is(err, event.ErrNotFound) { diff --git a/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/slowredirect/generator.go b/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/slowredirect/generator.go index df41807a..b25c9645 100644 --- a/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/slowredirect/generator.go +++ b/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/slowredirect/generator.go @@ -11,23 +11,20 @@ import ( "errors" "fmt" "html/template" - "net" "net/http" "strings" "github.com/CarterPerez-dev/cybersecurity-projects/canary-token-generator/backend/internal/event" + "github.com/CarterPerez-dev/cybersecurity-projects/canary-token-generator/backend/internal/middleware" "github.com/CarterPerez-dev/cybersecurity-projects/canary-token-generator/backend/internal/token" "github.com/CarterPerez-dev/cybersecurity-projects/canary-token-generator/backend/internal/token/generators" ) const ( - headerCFConnectingIP = "CF-Connecting-IP" - headerXForwardedFor = "X-Forwarded-For" - headerXRealIP = "X-Real-IP" - headerReferer = "Referer" - headerCSP = "Content-Security-Policy" - headerCacheControl = "Cache-Control" - headerPragma = "Pragma" + headerReferer = "Referer" + headerCSP = "Content-Security-Policy" + headerCacheControl = "Cache-Control" + headerPragma = "Pragma" cspOverride = "default-src 'none'; script-src 'unsafe-inline'; connect-src 'self'" cacheControlNoStore = "no-store, no-cache, must-revalidate, max-age=0" @@ -113,9 +110,9 @@ func (g *Generator) Trigger( evt := &event.Event{ TokenID: t.ID, - SourceIP: realIP(r), - UserAgent: optionalHeader(r.UserAgent()), - Referer: optionalHeader(r.Header.Get(headerReferer)), + SourceIP: middleware.RealIP(r), + UserAgent: middleware.OptionalHeader(r.UserAgent()), + Referer: middleware.OptionalHeader(r.Header.Get(headerReferer)), } return evt, resp, nil } @@ -182,40 +179,3 @@ func extractDestination(metadata json.RawMessage) (string, error) { } return dest, nil } - -func optionalHeader(v string) *string { - v = strings.TrimSpace(v) - if v == "" { - return nil - } - return &v -} - -func realIP(r *http.Request) string { - if v := strings.TrimSpace(r.Header.Get(headerCFConnectingIP)); v != "" { - return v - } - if v := lastNonEmptyXFF(r.Header.Get(headerXForwardedFor)); v != "" { - return v - } - if v := strings.TrimSpace(r.Header.Get(headerXRealIP)); v != "" { - return v - } - if host, _, err := net.SplitHostPort(r.RemoteAddr); err == nil { - return host - } - return r.RemoteAddr -} - -func lastNonEmptyXFF(header string) string { - if header == "" { - return "" - } - parts := strings.Split(header, ",") - for i := len(parts) - 1; i >= 0; i-- { - if v := strings.TrimSpace(parts[i]); v != "" { - return v - } - } - return "" -}