Skip to content

feat(security): GitHub App integration with token disclosure hardening#27

Merged
electronicBlacksmith merged 6 commits intomainfrom
feat/github-app-integration
Apr 15, 2026
Merged

feat(security): GitHub App integration with token disclosure hardening#27
electronicBlacksmith merged 6 commits intomainfrom
feat/github-app-integration

Conversation

@electronicBlacksmith
Copy link
Copy Markdown
Owner

@electronicBlacksmith electronicBlacksmith commented Apr 14, 2026

Summary

GitHub App integration for phantom_gh_exec with comprehensive security hardening addressing three critical vulnerabilities:

  • Token disclosure via stdout - Blocked gh auth token, gh secret, and git -c/--config commands that could print GH_TOKEN
  • Private key exposure - Created buildAgentEnv() to strip secrets (GITHUB_APP_PRIVATE_KEY_B64, SLACK_BOT_TOKEN, etc.) from SDK subprocess environment
  • Pipe deadlock - Replaced sequential stdout/stderr reads with concurrent drain using drainProcessWithLimits() with 2-minute timeout and 1MB output cap

Defense-in-depth layers

Layer Attack Vector Blocked
Subcommand blocklist gh auth, gh secret, gh config, gh ssh-key, gh gpg-key
Git flag block git -c, git --config (both = and space-separated forms)
Output redaction Any ghs_*, ghp_*, gho_*, ghu_*, github_pat_* tokens in stdout/stderr
Env stripping GITHUB_APP_PRIVATE_KEY_B64 and other secrets excluded from subprocess
Timeout + caps 2-minute timeout, 1MB output limit per stream

Key files

  • src/agent/gh-exec.ts - GitHub exec logic with security validation
  • src/agent/subprocess-env.ts - buildAgentEnv() for secret filtering
  • src/agent/runtime.ts - Use filtered env for SDK subprocess
  • src/utils/process.ts - Shared drainProcessWithLimits() utility
  • src/integrations/github-app.ts - Token broker with caching

Test coverage

  • 70+ new security tests
  • All 1258 tests pass
  • Lint and typecheck clean

Test plan

  • gh auth token returns blocked error
  • git -c alias.x=!env x returns blocked error
  • git --config alias.x=!env x returns blocked error
  • Agent subprocess env excludes GITHUB_APP_PRIVATE_KEY_B64
  • Tokens in output redacted to [REDACTED]
  • Commands timeout after 2 minutes
  • Large output truncated at 1MB
  • Full test suite passes

@electronicBlacksmith electronicBlacksmith self-assigned this Apr 14, 2026
Add src/integrations/github-app.ts with:
- Zod-validated env var loading (GITHUB_APP_ID, GITHUB_APP_CLIENT_ID,
  GITHUB_APP_INSTALLATION_ID, GITHUB_APP_PRIVATE_KEY_B64)
- Installation token caching with automatic refresh 5 min before expiry
- @octokit/auth-app for GitHub App authentication
- validateGitHubAppEnv() for doctor checks without token minting

Design: Lazy loading means Phantom boots without GitHub access. First
call to getInstallationToken() fails with a clear error if env vars
are missing.
Add phantom_gh_exec tool for authenticated GitHub operations:
- Runs gh/git commands with GH_TOKEN injected via subprocess env
- Token never appears in model context or tool results
- Shell metacharacter validation prevents injection attacks
- Args passed as array to Bun.spawn (no shell interpretation)

Extend buildSafeEnv() to accept explicit env vars (GH_TOKEN).
Wire createGitHubToolServer() into index.ts MCP factories.
Add checkGitHubApp() to phantom doctor:
- Checks for GITHUB_APP_ID, GITHUB_APP_CLIENT_ID,
  GITHUB_APP_INSTALLATION_ID, GITHUB_APP_PRIVATE_KEY_B64
- Returns WARN if any are missing (not FAIL - GitHub access is optional)
- Non-blocking: Phantom boots without GitHub, fails lazily at first use
Add documentation for GitHub App authentication:
- Environment variables table updated with GitHub App vars
- Token rotation playbook (when to rotate, how to rotate)
- Audit trail information
- Security notes (never commit keys, use .env.local)

Also update constitution principle count from 8 to 9 (workflow
principle added in Phase 1+2).
@electronicBlacksmith electronicBlacksmith force-pushed the feat/github-app-integration branch from 4183a87 to e44301e Compare April 14, 2026 02:51
Addresses three critical security vulnerabilities found in code review:

1. Token disclosure via stdout - Model could run `gh auth token` or
   `git -c alias.x=!env x` to print GH_TOKEN. Fixed by:
   - Adding validateGhSubcommand() to block gh auth/secret/config/ssh-key/gpg-key
   - Blocking git -c flag entirely (can execute arbitrary commands)

2. Private key exposure - runtime.ts passed ...process.env to SDK subprocess,
   exposing GITHUB_APP_PRIVATE_KEY_B64 to shell commands. Fixed by:
   - Creating buildAgentEnv() that strips secrets before passing to subprocess
   - Updating runtime.ts and judge-query.ts to use filtered env

3. Pipe deadlock - Sequential stdout/stderr reads could deadlock on large
   output. Fixed by:
   - Extracting drainProcessWithLimits() to shared utils/process.ts
   - Using concurrent Promise.all() drain with timeout and output caps
   - 2 minute timeout, 1MB output limit per stream

Defense-in-depth: Added redactTokensFromOutput() to scan output for GitHub
token patterns (ghs_*, ghp_*, github_pat_*) and replace with [REDACTED].

New tests: 70+ tests covering all security layers.
Review fixes:

1. Block git --config (space-separated form)
   - Added `arg === "--config"` to catch `git --config alias.x=!env x`
   - Previous check only caught `--config=` form

2. Split in-process-tools.ts to meet 300-line limit
   - Extracted GitHub exec logic to src/agent/gh-exec.ts (255 lines)
   - in-process-tools.ts now 128 lines
   - Re-exports from gh-exec.ts for backwards compatibility

Added test for --config space-separated bypass vector.
@electronicBlacksmith electronicBlacksmith changed the title feat: GitHub App token broker and phantom_gh_exec tool (Phase 4) feat(security): GitHub App integration with token disclosure hardening Apr 15, 2026
@electronicBlacksmith electronicBlacksmith merged commit 74a2cbd into main Apr 15, 2026
1 check passed
@electronicBlacksmith electronicBlacksmith deleted the feat/github-app-integration branch April 15, 2026 02:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant