From 0d70355250a2a3cc59d7b0ca42726ebefdeea7eb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 25 May 2026 16:50:51 +0000 Subject: [PATCH 1/3] Initial plan From 2757aa06336dfafcafa4932f4a86897da037acee Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 25 May 2026 16:54:47 +0000 Subject: [PATCH 2/3] fix: update ghs token regex for stateless jwt format --- src/cli.test.ts | 9 +++++++++ src/dlp.test.ts | 10 +++++++++- src/dlp.ts | 2 +- src/redact-secrets.ts | 2 +- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/cli.test.ts b/src/cli.test.ts index 29d677736..ac2a363fc 100644 --- a/src/cli.test.ts +++ b/src/cli.test.ts @@ -110,6 +110,15 @@ describe('cli', () => { expect(result).not.toContain('ghp_'); }); + it('should redact stateless GitHub app installation tokens', () => { + const token = `ghs_${'A'.repeat(170)}.${'b'.repeat(170)}_${'c'.repeat(170)}`; + const command = `echo ${token}`; + const result = redactSecrets(command); + + expect(result).toBe('echo ***REDACTED***'); + expect(result).not.toContain(token); + }); + it('should redact multiple secrets in one command', () => { const command = 'GITHUB_TOKEN=ghp_token API_KEY=secret curl -H "Authorization: Bearer ghp_bearer"'; const result = redactSecrets(command); diff --git a/src/dlp.test.ts b/src/dlp.test.ts index f97a4195b..ebc611140 100644 --- a/src/dlp.test.ts +++ b/src/dlp.test.ts @@ -52,7 +52,15 @@ describe('DLP Patterns', () => { const matchingRegexes = findMatchingDlpRegexes( 'https://api.example.com/?key=ghs_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghij' ); - expect(matchingRegexes).toContain('ghs_[a-zA-Z0-9]{36}'); + expect(matchingRegexes).toContain('ghs_[A-Za-z0-9._]{36,}'); + }); + + it('should detect stateless GitHub App installation token (ghs_ JWT format)', () => { + const jwtLikeToken = `ghs_${'A'.repeat(170)}.${'b'.repeat(170)}_${'c'.repeat(170)}`; + const matchingRegexes = findMatchingDlpRegexes( + `https://api.example.com/?key=${jwtLikeToken}` + ); + expect(matchingRegexes).toContain('ghs_[A-Za-z0-9._]{36,}'); }); it('should detect GitHub App user-to-server token (ghu_)', () => { diff --git a/src/dlp.ts b/src/dlp.ts index 3192a2534..846e5b29a 100644 --- a/src/dlp.ts +++ b/src/dlp.ts @@ -52,7 +52,7 @@ const DLP_PATTERNS: DlpPattern[] = [ { name: 'GitHub App Installation Token', description: 'GitHub App installation access token (ghs_)', - regex: 'ghs_[a-zA-Z0-9]{36}', + regex: 'ghs_[A-Za-z0-9._]{36,}', }, { name: 'GitHub App User-to-Server Token', diff --git a/src/redact-secrets.ts b/src/redact-secrets.ts index b79cc62bc..e9b39e6f8 100644 --- a/src/redact-secrets.ts +++ b/src/redact-secrets.ts @@ -10,5 +10,5 @@ export function redactSecrets(command: string): string { // Redact tokens in environment variables (TOKEN, SECRET, PASSWORD, KEY, API_KEY, etc) .replace(/(\w*(?:TOKEN|SECRET|PASSWORD|KEY|AUTH)\w*)=(\S+)/gi, '$1=***REDACTED***') // Redact GitHub tokens (ghp_, gho_, ghu_, ghs_, ghr_) - .replace(/\b(gh[pousr]_[a-zA-Z0-9]{36,255})/g, '***REDACTED***'); + .replace(/\b(gh[pousr]_[A-Za-z0-9._]{36,})/g, '***REDACTED***'); } From 68e2c4119c5be89ac1bcfd3efeec82665c404ded Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 25 May 2026 17:09:18 +0000 Subject: [PATCH 3/3] fix: include '-' in ghs token regex and coverage --- src/cli.test.ts | 2 +- src/dlp.test.ts | 6 +++--- src/dlp.ts | 2 +- src/redact-secrets.ts | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cli.test.ts b/src/cli.test.ts index ac2a363fc..a1017405b 100644 --- a/src/cli.test.ts +++ b/src/cli.test.ts @@ -111,7 +111,7 @@ describe('cli', () => { }); it('should redact stateless GitHub app installation tokens', () => { - const token = `ghs_${'A'.repeat(170)}.${'b'.repeat(170)}_${'c'.repeat(170)}`; + const token = `ghs_${'A'.repeat(170)}.${'b'.repeat(170)}-${'c'.repeat(170)}_${'d'.repeat(170)}`; const command = `echo ${token}`; const result = redactSecrets(command); diff --git a/src/dlp.test.ts b/src/dlp.test.ts index ebc611140..613a9e0a5 100644 --- a/src/dlp.test.ts +++ b/src/dlp.test.ts @@ -52,15 +52,15 @@ describe('DLP Patterns', () => { const matchingRegexes = findMatchingDlpRegexes( 'https://api.example.com/?key=ghs_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghij' ); - expect(matchingRegexes).toContain('ghs_[A-Za-z0-9._]{36,}'); + expect(matchingRegexes).toContain('ghs_[A-Za-z0-9._-]{36,}'); }); it('should detect stateless GitHub App installation token (ghs_ JWT format)', () => { - const jwtLikeToken = `ghs_${'A'.repeat(170)}.${'b'.repeat(170)}_${'c'.repeat(170)}`; + const jwtLikeToken = `ghs_${'A'.repeat(170)}.${'b'.repeat(170)}-${'c'.repeat(170)}_${'d'.repeat(170)}`; const matchingRegexes = findMatchingDlpRegexes( `https://api.example.com/?key=${jwtLikeToken}` ); - expect(matchingRegexes).toContain('ghs_[A-Za-z0-9._]{36,}'); + expect(matchingRegexes).toContain('ghs_[A-Za-z0-9._-]{36,}'); }); it('should detect GitHub App user-to-server token (ghu_)', () => { diff --git a/src/dlp.ts b/src/dlp.ts index 846e5b29a..1cf895f73 100644 --- a/src/dlp.ts +++ b/src/dlp.ts @@ -52,7 +52,7 @@ const DLP_PATTERNS: DlpPattern[] = [ { name: 'GitHub App Installation Token', description: 'GitHub App installation access token (ghs_)', - regex: 'ghs_[A-Za-z0-9._]{36,}', + regex: 'ghs_[A-Za-z0-9._-]{36,}', }, { name: 'GitHub App User-to-Server Token', diff --git a/src/redact-secrets.ts b/src/redact-secrets.ts index e9b39e6f8..236935d41 100644 --- a/src/redact-secrets.ts +++ b/src/redact-secrets.ts @@ -10,5 +10,5 @@ export function redactSecrets(command: string): string { // Redact tokens in environment variables (TOKEN, SECRET, PASSWORD, KEY, API_KEY, etc) .replace(/(\w*(?:TOKEN|SECRET|PASSWORD|KEY|AUTH)\w*)=(\S+)/gi, '$1=***REDACTED***') // Redact GitHub tokens (ghp_, gho_, ghu_, ghs_, ghr_) - .replace(/\b(gh[pousr]_[A-Za-z0-9._]{36,})/g, '***REDACTED***'); + .replace(/\b(gh[pousr]_[A-Za-z0-9._-]{36,})/g, '***REDACTED***'); }