From 0b072db36df3460b5878800b05c81daa22c0d0cc Mon Sep 17 00:00:00 2001 From: jamesread Date: Tue, 6 Jan 2026 22:11:41 +0000 Subject: [PATCH] fix: Constant time comparison for webhook authentication --- service/internal/webhooks/auth.go | 5 ++++- service/internal/webhooks/matcher.go | 15 +++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/service/internal/webhooks/auth.go b/service/internal/webhooks/auth.go index 5e7f26c..c3d6a5a 100644 --- a/service/internal/webhooks/auth.go +++ b/service/internal/webhooks/auth.go @@ -2,6 +2,7 @@ package webhooks import ( "crypto/hmac" + "crypto/subtle" "crypto/sha1" "crypto/sha256" "encoding/hex" @@ -105,7 +106,9 @@ func (v *AuthVerifier) verifyBearer(r *http.Request) bool { } token := strings.TrimPrefix(authHeader, "Bearer ") - return token == v.config.Secret + tokenBytes := []byte(token) + secretBytes := []byte(v.config.Secret) + return len(tokenBytes) == len(secretBytes) && subtle.ConstantTimeCompare(tokenBytes, secretBytes) == 1 } func (v *AuthVerifier) verifyBasic(r *http.Request) bool { diff --git a/service/internal/webhooks/matcher.go b/service/internal/webhooks/matcher.go index 3031db3..8b43165 100644 --- a/service/internal/webhooks/matcher.go +++ b/service/internal/webhooks/matcher.go @@ -12,7 +12,6 @@ import ( type WebhookMatcher struct { config config.WebhookConfig req *http.Request - body interface{} bodyBytes []byte } @@ -50,9 +49,9 @@ func (m *WebhookMatcher) matchHeaders() bool { actualValue := m.req.Header.Get(key) if !m.compareValues(actualValue, expectedValue) { log.WithFields(log.Fields{ - "header": key, - "expected": expectedValue, - "actual": actualValue, + "header": key, + "expected": expectedValue, + "actual": actualValue, }).Debugf("Header mismatch") return false } @@ -70,9 +69,9 @@ func (m *WebhookMatcher) matchQuery() bool { actualValue := query.Get(key) if !m.compareValues(actualValue, expectedValue) { log.WithFields(log.Fields{ - "query": key, - "expected": expectedValue, - "actual": actualValue, + "query": key, + "expected": expectedValue, + "actual": actualValue, }).Debugf("Query parameter mismatch") return false } @@ -144,7 +143,7 @@ func (m *WebhookMatcher) ExtractArguments() (map[string]string, error) { value, err := matcher.ExtractValue(jsonPath) if err != nil { log.WithFields(log.Fields{ - "argName": argName, + "argName": argName, "jsonPath": jsonPath, "error": err, }).Debugf("Failed to extract value")