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​