guide: wire GitHub PATs + Stripe keys into the guided worklist

GitHub PATs and Stripe secret keys have no scriptable rotation API (GitHub's
create-token API was removed in 2020; Stripe has no create-key endpoint), so
they belong in the guided change-password layer, not as Rotators. But they
arrive from the env/file scanner as generic Source="env" tokens with no host,
so the worklist showed them as "manual — no web page".

Recognise them by their well-known PUBLIC value prefixes (ghp_/gho_/ghs_/
github_pat_/…, sk_live_/sk_test_/rk_live_/…) at scan time and record a
non-secret Meta["service"] hint — a fixed service NAME, never the secret bytes.
discover.ServiceForSecret does the detection; env.go attaches it for generic
tokens, file.go before Store wipes the buffer. links.HostFor consults the hint
and maps github→github.com / stripe→stripe.com, so the curated change-password
URLs (already in the table) now light up for these credentials.

Leak-safe (service name is derived from a public prefix, not the secret) and
verified by the existing assertNoLeak checks. go build/vet clean; full suite
179 tests pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
leetcrypt
2026-06-20 15:35:42 -07:00
parent 88e603f3c0
commit d237098d83
7 changed files with 158 additions and 1 deletions
+10
View File
@@ -74,6 +74,15 @@ func (e *envScanner) Scan(ctx context.Context, v *vault.Vault) ([]Credential, er
} else if !looksSecret(k, val) {
continue
}
// For a generic env token (not a driver-ready DB URL / appsecret), record
// a non-secret service hint when the value carries a well-known provider
// prefix, so the worklist can offer a guided change-password page.
var meta map[string]string
if source == e.Name() {
if svc := ServiceForSecret([]byte(val)); svc != "" {
meta = map[string]string{MetaService: svc}
}
}
creds = append(creds, Credential{
Source: source,
Kind: kind,
@@ -81,6 +90,7 @@ func (e *envScanner) Scan(ctx context.Context, v *vault.Vault) ([]Credential, er
Location: p,
Modified: fi.ModTime(),
Secret: v.Store([]byte(secretBlob)),
Meta: meta,
})
}
f.Close()