diff --git a/app/channel-helpers.test.ts b/app/channel-helpers.test.ts index 9d7815a..360549f 100644 --- a/app/channel-helpers.test.ts +++ b/app/channel-helpers.test.ts @@ -154,4 +154,16 @@ describe("userFacingRunError", () => { expect(text).not.toContain("ghp_LIVESECRET99"); expect(text).toContain("ghp_[redacted]"); }); + + it.each(["ghs", "ghu", "ghr"])( + "redacts a %s_ GitHub App token from the reason", + (prefix) => { + const secret = `${prefix}_LIVESECRET99`; + const text = userFacingRunError( + new Error(`git push failed for https://x-access-token:${secret}@github.com/o/r`), + ); + expect(text).not.toContain(secret); + expect(text).toContain(`${prefix}_[redacted]`); + }, + ); }); diff --git a/app/channel-helpers.ts b/app/channel-helpers.ts index db0393e..7e9c034 100644 --- a/app/channel-helpers.ts +++ b/app/channel-helpers.ts @@ -69,7 +69,9 @@ export function reportRecoverableError( }); } -const TOKEN_RE = /(ghp|github_pat|gho)_[A-Za-z0-9_]+/g; +// Same families as `agent/coding/sandbox.py`. GitHub App auth mints `ghs_` +// installation tokens, so the narrower pattern let those through to Slack. +const TOKEN_RE = /(ghp|github_pat|gho|ghs|ghu|ghr)_[A-Za-z0-9_]+/g; function errorText(error: unknown): string { if (error instanceof AggregateError) {