Add GitHub personal access token validity check
What does this MR do and why?
This adds a validity check for classic GitHub personal access tokens (starting with prefix ghp_).
A user can then tell whether the exposed credential is still live rather than only that it was leaked.
Implementation
This follows ADR-006 and uses the registry directly to map the token straight to the verifier class/client, i.e. GithubClient.
The verifier calls GET https://api.github.com/user with the token as a Bearer credential.
We map the responses to the following statuses/outcomes:
| Response | Outcome |
|---|---|
200 |
active |
401 |
inactive |
403, 429 |
RateLimitError |
500, 502, 503, 504 |
NetworkError |
| anything else | unknown |
Please note that aside from 401, no other status falls to inactive.
Reporting a live token as dead is worse than reporting nothing, so unmapped statuses are always unknown.
Stack Position
We have 8 MRs for all new validity checks generated during Sec Challenge #6, with the base targeting master.
Each MR in the stack adds exactly one validity check: the client class, its spec, the registry entry, and the rate limit rule.
Stack (in review order)
| # | Check | Token type |
|---|---|---|
| 1 | GitHub PAT | Github Personal Access Token |
| 2 | OpenAI project key | OpenAiProjectKey |
| 3 | Anthropic API key | anthropic_key |
| 4 | Slack access tokens | Slack token |
| 5 | Stripe live secret key | StripeLiveSecretKey |
| 6 | Datadog API key | DataDogAPIKey |
| 7 | SendGrid API token | Sendgrid API token |
| 8 | Heroku API key | Heroku API Key |
Notes
- Token type and pattern were verified against the authoritative rule in
secret-detection-rules. - Rate limit: 60 checks per hour per project, matching the 60 requests per hour GitHub allows for unauthenticated requests, which is where a revoked token's rejection counts.
- Unmapped statuses are covered by the shared example
a partner token client, which this stack adds once and every verifier reuses. - Verifier code originates from the Sec Challenge #6 PoC branch
ghavenga-summit-ch6-validity-checks.
How to set up and validate locally
-
Run the specs:
bundle exec rspec ee/spec/lib/security/secret_detection/partner_tokens/github_client_spec.rb \ ee/spec/services/security/secret_detection/partner_tokens/registry_spec.rb -
Confirm the registry resolves the token type and that the rate limit rule exists:
Security::SecretDetection::PartnerTokens::Registry.client_for('Github Personal Access Token') # => #<Security::SecretDetection::PartnerTokens::GithubClient> Gitlab::ApplicationRateLimiter.period_for(:partner_github_api) # => 3600 seconds -
Optional, with a real credential — a revoked token should come back
inactiveand a live oneactive:Security::SecretDetection::PartnerTokens::Registry .client_for('Github Personal Access Token').verify_token(ENV['TOKEN']).status
MR acceptance checklist
I have evaluated this MR against the MR acceptance checklist.