Skip to main content

GitHub

scan4secrets detects leaked GitHub PATs (classic and fine-grained), OAuth tokens, App installation tokens, refresh tokens, and deploy keys.

Token formats

Token typePrefixShape
Classic PATghp_ghp_[A-Za-z0-9]{36}
Fine-grained PATgithub_pat_github_pat_[A-Za-z0-9_]{82}
OAuth tokengho_gho_[A-Za-z0-9]{36}
App installation tokenghs_ghs_[A-Za-z0-9]{36}
App user-to-server tokenghu_ghu_[A-Za-z0-9]{36}
Refresh tokenghr_ghr_[A-Za-z0-9]{36}
Deploy key (private)n/a-----BEGIN OPENSSH PRIVATE KEY----- block

Detection rules

- id: github-pat-classic
description: GitHub Personal Access Token (Classic)
severity: critical
keywords: ["ghp_"]
regex: '\b(ghp_[A-Za-z0-9]{36})\b'
verify:
method: GET
url: https://api.github.com/user
header_name: Authorization
header_value: "token {{value}}"
success_status: 200

- id: github-pat-fine-grained
description: GitHub Fine-Grained Personal Access Token
severity: critical
keywords: ["github_pat_"]
regex: '\b(github_pat_[A-Za-z0-9_]{82})\b'
verify:
method: GET
url: https://api.github.com/user
header_name: Authorization
header_value: "Bearer {{value}}"
success_status: 200

- id: github-oauth
description: GitHub OAuth Token
severity: high
keywords: ["gho_"]
regex: '\b(gho_[A-Za-z0-9]{36})\b'

- id: github-app-token
description: GitHub App Installation Token (server-to-server)
severity: high
keywords: ["ghs_"]
regex: '\b(ghs_[A-Za-z0-9]{36})\b'

- id: github-refresh-token
description: GitHub OAuth Refresh Token
severity: high
keywords: ["ghr_"]
regex: '\b(ghr_[A-Za-z0-9]{36})\b'

Live verification

Built-in. --verify issues:

GET /user HTTP/1.1
Host: api.github.com
Authorization: token ghp_xxx

The response X-OAuth-Scopes header reveals exactly what the token grants. Capture this and quote it verbatim in any bug-bounty report. Example: X-OAuth-Scopes: repo, workflow, admin:org = full RCE on hosted runners.

Impact when verified live

Token + scopesWorst-case impact
ghp_… + repoRead/write all private repos the user can see
ghp_… + repo, workflowTrigger / inject malicious workflows → secret exfil, RCE on runners
ghp_… + admin:orgAdd/remove org members, settings, transfer repos
github_pat_… (FGPAT)Limited to declared repos and resources. Still high if it touches secrets
ghs_… (App install)Whatever the App's permission set allows
Deploy key (write)Push to one repo without going through CI

Revocation

TokenAction
Classic PATSettings → Developer settings → Personal access tokens (classic) → Delete
Fine-grained PATSettings → Developer settings → Personal access tokens → Fine-grained → Revoke
OAuthSettings → Applications → Authorized OAuth Apps → Revoke
App installationOrg / repo Settings → Installations → Suspend

After revocation, audit the org Audit Log and any affected repos' Actions runs for use between issue and revocation.

Common leak surfaces scan4secrets catches

  1. .env, .env.local, .env.production committed (SAST).
  2. package.json's scripts calling curl -H "Authorization: token ${TOKEN}" with the token inline (SAST).
  3. ~/.gitconfig, ~/.config/gh/hosts.yml mistakenly bundled into a Docker image.
  4. Public Gist with a forgotten PAT (DAST against a Gist URL).
  5. CI log echo: echo "Token: $TOKEN" (JSONL into SIEM).
  6. Source-map of internal admin SPA leaking a build-time GitHub access token (only scan4secrets parses .js.map).

References