Skip to content

Commit 305aa98

Browse files
committed
fix: harden token redaction in GitLab logger and session error handling
- Widen Bearer token regex from [a-zA-Z0-9_-]+ to \S+ to cover JWTs (containing dots) and base64 tokens (containing = padding) - Wrap GitHub token fetch error in sessions.go with gitlab.SanitizeErrorMessage() to prevent upstream errors from leaking token material - Add tests for JWT, base64, and error message redaction Closes #1668
1 parent 4f74150 commit 305aa98

3 files changed

Lines changed: 58 additions & 2 deletions

File tree

components/backend/gitlab/logger.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func RedactToken(s string) string {
2121
s = gitlabCIPattern.ReplaceAllString(s, "gitlab-ci-token: "+TokenRedactionPlaceholder)
2222

2323
// Bearer tokens in Authorization headers
24-
bearerPattern := regexp.MustCompile(`Bearer\s+[a-zA-Z0-9_-]+`)
24+
bearerPattern := regexp.MustCompile(`Bearer\s+\S+`)
2525
s = bearerPattern.ReplaceAllString(s, "Bearer "+TokenRedactionPlaceholder)
2626

2727
// OAuth2 tokens in URLs: oauth2:TOKEN@
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package gitlab
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
"testing"
7+
)
8+
9+
func TestRedactToken_BearerJWT(t *testing.T) {
10+
jwt := "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.SflKxwRJSMeKKF2QT4fwpM"
11+
input := "Authorization: Bearer " + jwt
12+
result := RedactToken(input)
13+
if strings.Contains(result, jwt) {
14+
t.Errorf("JWT token was not fully redacted: %s", result)
15+
}
16+
if !strings.Contains(result, "Bearer "+TokenRedactionPlaceholder) {
17+
t.Errorf("expected Bearer [REDACTED], got: %s", result)
18+
}
19+
}
20+
21+
func TestRedactToken_BearerBase64(t *testing.T) {
22+
base64Token := "dGhpcyBpcyBhIHRva2VuIHdpdGggcGFkZGluZw=="
23+
input := "Authorization: Bearer " + base64Token
24+
result := RedactToken(input)
25+
if strings.Contains(result, base64Token) {
26+
t.Errorf("base64 token was not fully redacted: %s", result)
27+
}
28+
if !strings.Contains(result, "Bearer "+TokenRedactionPlaceholder) {
29+
t.Errorf("expected Bearer [REDACTED], got: %s", result)
30+
}
31+
}
32+
33+
func TestRedactToken_BearerSimple(t *testing.T) {
34+
input := "Authorization: Bearer ghp_abc123def456"
35+
result := RedactToken(input)
36+
if strings.Contains(result, "ghp_abc123def456") {
37+
t.Errorf("simple token was not redacted: %s", result)
38+
}
39+
}
40+
41+
func TestSanitizeErrorMessage_RedactsTokenInError(t *testing.T) {
42+
token := "ghp_secrettoken123"
43+
err := fmt.Errorf("failed to fetch: Bearer %s was rejected", token)
44+
result := SanitizeErrorMessage(err)
45+
if strings.Contains(result, token) {
46+
t.Errorf("token leaked in sanitized error message: %s", result)
47+
}
48+
}
49+
50+
func TestSanitizeErrorMessage_NilError(t *testing.T) {
51+
result := SanitizeErrorMessage(nil)
52+
if result != "" {
53+
t.Errorf("expected empty string for nil error, got: %s", result)
54+
}
55+
}

components/backend/handlers/sessions.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"unicode/utf8"
2222

2323
"ambient-code-backend/git"
24+
"ambient-code-backend/gitlab"
2425
"ambient-code-backend/pathutil"
2526
"ambient-code-backend/types"
2627

@@ -1368,7 +1369,7 @@ func MintSessionGitHubToken(c *gin.Context) {
13681369
// Get GitHub token (GitHub App or PAT fallback via project runner secret)
13691370
tokenStr, err := GetGitHubToken(c.Request.Context(), K8sClient, DynamicClient, project, userID)
13701371
if err != nil {
1371-
log.Printf("Failed to get GitHub token for project %s: %v", project, err)
1372+
log.Printf("Failed to get GitHub token for project %s: %s", project, gitlab.SanitizeErrorMessage(err))
13721373
c.JSON(http.StatusBadGateway, gin.H{"error": "Failed to retrieve GitHub token"})
13731374
return
13741375
}

0 commit comments

Comments
 (0)