From 91ea4e03faaa1cea917d0e64c963b63bede22265 Mon Sep 17 00:00:00 2001 From: Pavan Kumar Gondhi Date: Fri, 24 Jul 2026 14:15:15 +0530 Subject: [PATCH] fix: redact additional GitLab token prefixes [AI] (#112954) * fix: redact additional GitLab token prefixes * fix: cover GitLab runner token variants * test: construct GitLab redaction fixtures * fix: consume full GitLab OAuth tokens * fix: preserve long GitLab OAuth redaction * fix: keep legacy OAuth token masking --- src/logging/redact.test.ts | 49 +++++++++++++++++++++++++++++++++++++- src/logging/redact.ts | 17 +++++++++++-- 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/src/logging/redact.test.ts b/src/logging/redact.test.ts index d6b27e51059e..d4aa87cca703 100644 --- a/src/logging/redact.test.ts +++ b/src/logging/redact.test.ts @@ -1434,9 +1434,56 @@ describe("redactSensitiveText", () => { } }); + it("masks additional GitLab token prefixes through the default fast path", () => { + const dashToken = (prefix: string, suffix: string): string => [prefix, suffix].join("-"); + const repeatedDashToken = (prefix: string, length: number): string => + dashToken(prefix, "A".repeat(length)); + const legacyOauthToken = dashToken("gloas", "a".repeat(32)); + const longHexOauthToken = dashToken("gloas", "a".repeat(80)); + const mixedOauthToken = dashToken("gloas", `${"a".repeat(32)}Z${"b".repeat(31)}`); + const tokens = [ + legacyOauthToken, + longHexOauthToken, + mixedOauthToken, + repeatedDashToken("gldt", 20), + dashToken("glcbt", `a1B2_${"A".repeat(20)}`), + repeatedDashToken("glptt", 40), + repeatedDashToken("glft", 20), + dashToken("glft", "a0b1-123_"), + repeatedDashToken("glimt", 25), + repeatedDashToken("glagent", 50), + repeatedDashToken("glwt", 20), + repeatedDashToken("glsoat", 20), + repeatedDashToken("glffct", 20), + dashToken("glrt", `t1_${"A".repeat(20)}`), + dashToken("glrt", "2CR8_eVxiioB1QmzPZwa"), + dashToken("glrt", "ABCdef1234567890xyzW"), + dashToken("glrt", `${"A".repeat(27)}.01.${"a".repeat(9)}`), + dashToken("glrtr", `${"A".repeat(27)}.01.${"a".repeat(9)}`), + `GR1348941${"A".repeat(20)}`, + `_gitlab_session=${"A".repeat(32)}`, + ]; + + for (const token of tokens) { + expect(redactSensitiveText(token, { mode: "tools" }), token).not.toContain(token); + } + expect(redactSensitiveText(mixedOauthToken, { mode: "tools" })).not.toContain( + mixedOauthToken.slice("gloas-".length + 32), + ); + expect(redactSensitiveText(longHexOauthToken, { mode: "tools" })).not.toContain( + longHexOauthToken.slice("gloas-".length + 64), + ); + expect(redactSensitiveText(`${legacyOauthToken}_suffix`, { mode: "tools" })).not.toContain( + legacyOauthToken, + ); + expect(redactSensitiveText(`${longHexOauthToken}_suffix`, { mode: "tools" })).not.toContain( + `${longHexOauthToken.slice("gloas-".length + 64)}_suffix`, + ); + }); + it("does not redact ordinary identifiers containing short token-prefix substrings", () => { const input = [ - "npm_telegram_package_spec ask_openclaw_query_patterns team_management risk_assessment glpat-docs dapi-example sbp_short nfp_site CCIPAT_docs ATATT-example fw-tooshort fw_tooshort fpk_tooshort", + "npm_telegram_package_spec ask_openclaw_query_patterns team_management risk_assessment glpat-docs gloas-docs gldt-docs glcbt-docs glptt-docs glft-docs glimt-docs glagent-docs glwt-docs glsoat-docs glffct-docs glrt-docs glrtr-docs GR1348941-docs _gitlab_session=short dapi-example sbp_short nfp_site CCIPAT_docs ATATT-example fw-tooshort fw_tooshort fpk_tooshort", `fixturefw-${"C".repeat(40)}`, `fixture_fw_${"A".repeat(40)}`, `fixture_fpk_${"B".repeat(40)}`, diff --git a/src/logging/redact.ts b/src/logging/redact.ts index ee04c9a83748..d1683176c5ed 100644 --- a/src/logging/redact.ts +++ b/src/logging/redact.ts @@ -243,7 +243,20 @@ const DEFAULT_REDACT_PATTERNS: string[] = [ String.raw`(ghs_[A-Za-z0-9]{10,})`, String.raw`(ghr_[A-Za-z0-9]{10,})`, String.raw`(glpat-[A-Za-z0-9._=\-]{20,})`, - String.raw`(gloas-[A-Fa-f0-9]{32,})`, + String.raw`(gloas-(?:[A-Fa-f0-9]{65,}|[A-Za-z0-9_-]{64}|[A-Fa-f0-9]{32,}))`, + String.raw`(gldt-[A-Za-z0-9_-]{20,})`, + String.raw`(glcbt-[A-Za-z0-9]{1,5}_[A-Za-z0-9_-]{20,})`, + String.raw`(glptt-[A-Za-z0-9_-]{40,})`, + String.raw`(glft-(?:[A-Za-z0-9_-]{20,}|[a-h0-9]+-[0-9]+_))`, + String.raw`(glimt-[A-Za-z0-9_-]{25,})`, + String.raw`(glagent-[A-Za-z0-9_-]{50,})`, + String.raw`(glwt-[A-Za-z0-9_-]{20,})`, + String.raw`(glsoat-[A-Za-z0-9_-]{20,})`, + String.raw`(glffct-[A-Za-z0-9_-]{20,})`, + String.raw`(glrt-[A-Za-z0-9._-]{20,})`, + String.raw`(glrtr?-[A-Za-z0-9_-]{27,300}\.[0-9a-z]{2}\.[0-9a-z]{9})`, + String.raw`(GR1348941[A-Za-z0-9_-]{20,})`, + String.raw`(_gitlab_session=[A-Za-z0-9%._-]{20,})`, String.raw`(xox[baprs]-[A-Za-z0-9-]{10,})`, String.raw`(xapp-[A-Za-z0-9-]{10,})`, String.raw`(https:\/\/hooks\.slack\.com\/(?:services\/T[A-Z0-9]+\/B[A-Z0-9]+|workflows\/T[A-Z0-9]+\/A[A-Z0-9]+\/[0-9]{17,19})\/[A-Za-z0-9]{20,})`, @@ -334,7 +347,7 @@ const DEFAULT_REDACT_PREFILTER_SOURCES: string[] = [ // URL userinfo and connection-string password slots (`scheme://user:pass@host`). String.raw`:\/\/[^\/\s:@]*:[^\/\s@]+@`, // Vendor token prefixes and webhook hosts, ordered like DEFAULT_REDACT_PATTERNS. - String.raw`sk-|gh[opsur]_|github_pat_|glpat-|gloas-|xox[baprs]-|xapp-|hooks\.slack\.com|discord|gsk_|AIza|ya29\.|1\/\/0|eyJ|pplx-|fal_|fc-|bb_live_|gAAAA|[sr]k_(?:live|test)_|\bSG\.|npm_|pypi-|do[opr]_v1_|dp\.(?:ct|pt|sa|st|scim|audit)\.|dckr_|bkua_|CCIPAT_|sbp_|dapi[0-9a-f]|dd[pw]_|glsa_|nfp_|CFPAT-|ATCTT3|ATATT|ATBB|BBDC-|HRKU-|pat-(?:eu|na)1-|apify_api_|FlyV1|fio-u-|tvly-|exa_|syt_|retaindb_|mem0_|brv_|xai-|fw-|fw_|fpk_`, + String.raw`sk-|gh[opsur]_|github_pat_|glpat-|gloas-|gldt-|glcbt-|glptt-|glft-|glimt-|glagent-|glwt-|glsoat-|glffct-|glrt-|glrtr-|GR1348941|_gitlab_session=|xox[baprs]-|xapp-|hooks\.slack\.com|discord|gsk_|AIza|ya29\.|1\/\/0|eyJ|pplx-|fal_|fc-|bb_live_|gAAAA|[sr]k_(?:live|test)_|\bSG\.|npm_|pypi-|do[opr]_v1_|dp\.(?:ct|pt|sa|st|scim|audit)\.|dckr_|bkua_|CCIPAT_|sbp_|dapi[0-9a-f]|dd[pw]_|glsa_|nfp_|CFPAT-|ATCTT3|ATATT|ATBB|BBDC-|HRKU-|pat-(?:eu|na)1-|apify_api_|FlyV1|fio-u-|tvly-|exa_|syt_|retaindb_|mem0_|brv_|xai-|fw-|fw_|fpk_`, String.raw`(?:^|[^A-Za-z0-9_])(?:am_|sk_)`, String.raw`A[KS]IA[A-Z0-9]|AKID|LTAI|hf_|api_org_|r8_`, AWS_SECRET_ACCESS_KEY_VALUE_PATTERN,