Update ghs_ token detection/redaction for stateless JWT format#3786
Conversation
ghs_ token detection/redaction for stateless JWT format
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
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
| // 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***'); |
| { | ||
| 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,}', | ||
| }, |
| 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 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); | ||
| }); |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
@copilot address review feedback |
Addressed in Updated both |
|
| 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
Smoke Test Results✅ GitHub MCP: Connected (PR #3782: "feat: document model alias logging and wire debugTokens through config") Status: ❌ FAIL Triggered by
|
BYOK Offline Mode Smoke Test Results✅ GitHub MCP: PR #3790 - [WIP] Standardize JSONL log record format Mode: COPILOT_OFFLINE=true (BYOK offline mode) PR Context: #3786 by
|
Smoke Test Codex: FAILPRs reviewed: feat: document model alias logging and wire debugTokens through config; Increase smoke-claude max-turns from 2 to 10 Warning Firewall blocked 1 domainThe following domain was blocked by the firewall during workflow execution:
network:
allowed:
- defaults
- "registry.npmjs.org"See Network Configuration for more information.
|
Service Connectivity Test Results❌ Redis: Connection timeout/failed Result: FAIL — Unable to reach
|
🧪 Chroot Runtime Version Test ResultsCompared runtime versions between host and chroot environments:
Overall Result: Some version mismatches detected Analysis
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.
|
|
Smoke Test FAIL. MCP: ❌, Conn: ❌, File: ✅, Bash: ✅ Warning Firewall blocked 1 domainThe following domain was blocked by the firewall during workflow execution:
network:
allowed:
- defaults
- "localhost"See Network Configuration for more information.
|
🏗️ Build Test Suite ResultsAll build tests completed successfully! ✅
Overall: 8/8 ecosystems passed — ✅ PASS Summary
|
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
src/dlp.ts:ghs_[a-zA-Z0-9]{36}→ghs_[A-Za-z0-9._]{36,}ghs_detection with the new token shape/length expectations.Secret redaction update
src/redact-secrets.tsGitHub token matcher:\b(gh[pousr]_[a-zA-Z0-9]{36,255})→\b(gh[pousr]_[A-Za-z0-9._]{36,})Focused test coverage
src/dlp.test.tsexpectedghs_regex string and added a stateless JWT-styleghs_case with long segments plus./_.src/cli.test.tscase verifying redaction of long JWT-styleghs_tokens.