Skip to content

Update ghs_ token detection/redaction for stateless JWT format#3786

Merged
lpcox merged 3 commits into
mainfrom
copilot/update-ghs-token-regex
May 25, 2026
Merged

Update ghs_ token detection/redaction for stateless JWT format#3786
lpcox merged 3 commits into
mainfrom
copilot/update-ghs-token-regex

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 25, 2026

GitHub App installation tokens (ghs_) now use a long stateless JWT-like format (including . and _), while existing patterns assumed fixed-length alphanumeric tokens. This caused DLP matching and CLI redaction to miss valid new-format tokens.

  • DLP pattern update

    • Updated src/dlp.ts:
      • ghs_[a-zA-Z0-9]{36}ghs_[A-Za-z0-9._]{36,}
    • This aligns ghs_ detection with the new token shape/length expectations.
  • Secret redaction update

    • Updated src/redact-secrets.ts GitHub token matcher:
      • \b(gh[pousr]_[a-zA-Z0-9]{36,255})\b(gh[pousr]_[A-Za-z0-9._]{36,})
    • Removes the old upper bound and allows JWT-compatible characters so redaction covers full tokens instead of stopping at delimiters.
  • Focused test coverage

    • Updated src/dlp.test.ts expected ghs_ regex string and added a stateless JWT-style ghs_ case with long segments plus . / _.
    • Added src/cli.test.ts case verifying redaction of long JWT-style ghs_ tokens.
// Before
regex: 'ghs_[a-zA-Z0-9]{36}'

// After
regex: 'ghs_[A-Za-z0-9._]{36,}'

Copilot AI linked an issue May 25, 2026 that may be closed by this pull request
Copilot AI changed the title [WIP] Update ghs_ token regex for new stateless JWT format Update ghs_ token detection/redaction for stateless JWT format May 25, 2026
Copilot finished work on behalf of lpcox May 25, 2026 16:56
Copilot AI requested a review from lpcox May 25, 2026 16:56
@lpcox lpcox marked this pull request as ready for review May 25, 2026 16:57
Copilot AI review requested due to automatic review settings May 25, 2026 16:57
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates GitHub App installation token (ghs_) detection and redaction to handle newer stateless/JWT-like token formats so DLP blocking and CLI log redaction don’t miss valid tokens.

Changes:

  • Expanded ghs_ DLP regex to allow longer tokens containing . / _.
  • Expanded GitHub token redaction regex to allow . / _ and removed the previous max-length cap.
  • Added tests covering long JWT-like ghs_ tokens for both DLP detection and CLI redaction.
Show a summary per file
File Description
src/redact-secrets.ts Broadens GitHub token redaction regex to cover JWT-like ghs_ formats.
src/dlp.ts Broadens ghs_ DLP pattern to match longer token shapes including . / _.
src/dlp.test.ts Updates expected ghs_ regex string and adds a long JWT-like ghs_ detection test.
src/cli.test.ts Adds a CLI redaction test for long JWT-like ghs_ tokens.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 4/4 changed files
  • Comments generated: 4

Comment thread src/redact-secrets.ts Outdated
Comment on lines +10 to +13
// 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***');
Comment thread src/dlp.ts
Comment on lines 52 to 56
{
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,}',
},
Comment thread src/dlp.test.ts Outdated
Comment on lines +59 to +63
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,}');
Comment thread src/cli.test.ts
Comment on lines +113 to +120
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);
});
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@lpcox
Copy link
Copy Markdown
Collaborator

lpcox commented May 25, 2026

@copilot address review feedback

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 25, 2026

@copilot address review feedback

Addressed in 68e2c41.

Updated both ghs_ matchers to include - ([A-Za-z0-9._-]{36,}) in DLP and redaction, and updated the DLP/CLI tests to include JWT-like ghs_ tokens containing . _ and - to prevent regressions.

Copilot finished work on behalf of lpcox May 25, 2026 17:10
@github-actions
Copy link
Copy Markdown
Contributor

⚠️ Coverage Regression Detected

This PR decreases test coverage. Please add tests to maintain coverage levels.

Overall Coverage

Metric Base PR Delta
Lines 96.53% 96.58% 📈 +0.05%
Statements 96.37% 96.42% 📈 +0.05%
Functions 97.99% 97.99% ➡️ +0.00%
Branches 90.88% 90.84% 📉 -0.04%
📁 Per-file Coverage Changes (1 files)
File Lines (Before → After) Statements (Before → After)
src/config-writer.ts 89.3% → 90.9% (+1.65%) 89.3% → 90.9% (+1.65%)

Coverage comparison generated by scripts/ci/compare-coverage.ts

@github-actions
Copy link
Copy Markdown
Contributor

Smoke Test Results

GitHub MCP: Connected (PR #3782: "feat: document model alias logging and wire debugTokens through config")
GitHub.com: File test not found at expected path
File Write/Read: /tmp/smoke-test-file.txt missing

Status: ❌ FAIL

Triggered by @Copilot • Assignees: @lpcox @Copilot

📰 BREAKING: Report filed by Smoke Copilot

@github-actions
Copy link
Copy Markdown
Contributor

BYOK Offline Mode Smoke Test Results

GitHub MCP: PR #3790 - [WIP] Standardize JSONL log record format
File I/O: Test file not found at /tmp/smoke-test-file.txt
GitHub.com: HTTP code not provided in workflow variables
BYOK Inference: Successfully processing this prompt via api-proxy → api.githubcopilot.com

Mode: COPILOT_OFFLINE=true (BYOK offline mode)
Overall: PARTIAL PASS (2/4 tests)

PR Context: #3786 by @Copilot, assigned to @lpcox, @Copilot

🔑 BYOK report filed by Smoke Copilot BYOK

@github-actions
Copy link
Copy Markdown
Contributor

Smoke Test Codex: FAIL

PRs reviewed: feat: document model alias logging and wire debugTokens through config; Increase smoke-claude max-turns from 2 to 10
✅ GitHub PR read; ❌ safeinputs-gh unavailable
✅ Playwright title; ❌ Tavily exposed no search tool
✅ File/bash; ❌ discussion safe-input unavailable; ✅ build
Overall status: FAIL

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • registry.npmjs.org

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "registry.npmjs.org"

See Network Configuration for more information.

🔮 The oracle has spoken through Smoke Codex

@github-actions
Copy link
Copy Markdown
Contributor

Service Connectivity Test Results

❌ Redis: Connection timeout/failed
❌ PostgreSQL pg_isready: No response
❌ PostgreSQL SELECT 1: Not tested (pg_isready failed)

Result: FAIL — Unable to reach host.docker.internal services from AWF sandbox

🔌 Service connectivity validated by Smoke Services

@github-actions
Copy link
Copy Markdown
Contributor

🧪 Chroot Runtime Version Test Results

Compared runtime versions between host and chroot environments:

Runtime Host Version Chroot Version Match?
Python 3.12.13 3.12.3 ❌ NO
Node.js v24.15.0 v22.22.3 ❌ NO
Go go1.22.12 go1.22.12 ✅ YES

Overall Result: Some version mismatches detected

Analysis

  • Go: ✅ Perfect match - chroot successfully uses host Go installation
  • Python: ❌ Minor version mismatch (3.12.13 vs 3.12.3)
  • Node.js: ❌ Major+minor mismatch (v24.15.0 vs v22.22.3)

The Go match confirms that chroot bind mounts work correctly. Python and Node.js differences suggest the container may have its own installations that take precedence over host bind mounts.

Tested by Smoke Chroot

@github-actions
Copy link
Copy Markdown
Contributor

Smoke Test FAIL. MCP: ❌, Conn: ❌, File: ✅, Bash: ✅

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • localhost

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "localhost"

See Network Configuration for more information.

💎 Faceted by Smoke Gemini

@github-actions
Copy link
Copy Markdown
Contributor

🏗️ Build Test Suite Results

All build tests completed successfully! ✅

Ecosystem Project Build/Install Tests Status
Bun elysia 1/1 passed ✅ PASS
Bun hono 1/1 passed ✅ PASS
C++ fmt N/A ✅ PASS
C++ json N/A ✅ PASS
Deno oak N/A 1/1 passed ✅ PASS
Deno std N/A 1/1 passed ✅ PASS
.NET hello-world N/A ✅ PASS
.NET json-parse N/A ✅ PASS
Go color 1/1 passed ✅ PASS
Go env 1/1 passed ✅ PASS
Go uuid 1/1 passed ✅ PASS
Java gson 1/1 passed ✅ PASS
Java caffeine 1/1 passed ✅ PASS
Node.js clsx 1/1 passed ✅ PASS
Node.js execa 1/1 passed ✅ PASS
Node.js p-limit 1/1 passed ✅ PASS
Rust fd 1/1 passed ✅ PASS
Rust zoxide 1/1 passed ✅ PASS

Overall: 8/8 ecosystems passed — ✅ PASS


Summary

  • Total projects tested: 18
  • Passed: 18
  • Failed: 0
  • All ecosystems successfully built and tested their respective projects

Generated by Build Test Suite for issue #3786 · ● 8.4M ·

@lpcox lpcox merged commit fba93a8 into main May 25, 2026
60 of 64 checks passed
@lpcox lpcox deleted the copilot/update-ghs-token-regex branch May 25, 2026 17:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update ghs_ token regex for new stateless JWT format

3 participants