--- name: github-secret-scanning description: "Force-pushing doesn't delete commits from GitHub. Dangling commits persist, and leaked keys are findable. Here's how I scan for them." allowed-tools: - Bash - Read - Grep - Glob - Agent --- ## Git Secret Scanner with Dangling Commit Recovery Scan a GitHub repository (or entire org) for leaked secrets in both reachable history and force-pushed dangling commits that persist on GitHub. Detects 20+ prefixed token types, crypto keys, PEM keys, webhook URLs, database credentials, and JWTs. ## Signals to apply - User asks to scan a repo for leaked keys, secrets, or credentials - User mentions force-pushed secrets or dangling commits - Security audit of a GitHub repository or organization - User wants to check if a previous secret cleanup was effective ## Implementation steps 1. Verify prerequisites: git, gh (authenticated), optionally cast (Foundry) for address derivation 2. Clone the target repo and fetch all refs including hidden PR refs: git clone /tmp/secret-scan-/ git fetch origin '+refs/*:refs/remotes/origin-all/*' 3. Scan reachable history using diff-based searching (git log -p --all -G ''). Run these pattern groups, excluding node_modules/vendor/dist/build/lib: - Env variable assignments: PRIVATE_KEY, SECRET_KEY, API_KEY, MNEMONIC, DATABASE_URL, AWS_SECRET_ACCESS_KEY, etc. - Crypto private keys: 0x + 64 hex chars in variable assignments, string literals, config (not ABI/bytecode) - Prefixed service tokens (zero false positives): ghp_, gho_, ghs_, github_pat_, sk_live_, sk_test_, rk_live_, whsec_, xoxb-, xoxp-, sk-ant-api03-, sk-proj-, SG., AKIA, AIzaSy, glpat-, npm_, pypi-, hf_, dop_v1_ - PEM private keys: -----BEGIN (RSA|EC|DSA|OPENSSH) PRIVATE KEY----- - Webhook URLs: hooks.slack.com/services/, discord.com/api/webhooks/ - Database connection strings: postgres/mongodb+srv/mysql/redis with embedded credentials - JWT tokens: eyJ... (decode payload, check for service_role or admin claims) Filter out placeholders: empty, your_, _here, example, REDACTED, \${...}, process.env 4. Find dangling commits using three methods: a. GitHub Events API (last 30 days): extract SHAs from PushEvent payloads, especially zero-commit pushes (force-push fingerprint) b. GH Archive (back to 2015, public repos only): download hourly files from data.gharchive.org, filter for zero-commit PushEvents c. GitHub Actions runs, PR review comments, and deployments API for additional historical SHAs 5. Fetch each dangling commit (git fetch origin , REST API, or .patch URL). Scan using both diff scanning (git show ) AND file enumeration (git ls-tree) for all pattern groups above. 6. Verify discovered secrets: - Crypto keys: derive address with cast wallet address, check balance/nonce via RPC, check recent transaction history via Blockscout API (curl -s https://base.blockscout.com/api/v2/addresses//transactions). Flag if wallet was active within the last week. - AWS keys: verify with aws sts get-caller-identity (read-only) - GitHub tokens: verify with gh api /user using the found token - Other tokens: flag for manual rotation 7. Clean up: rm -rf /tmp/secret-scan-/ ## Key constraints - Never output full private keys in reports. Use truncated form (first 6, last 4 chars) or refer by commit SHA and file path - Always print full public addresses so they are easy to look up - Never store discovered keys to disk. All key material stays in memory only - Events API retains only 30 days of history and caps at 300 events - GH Archive only covers public repos. For private repos, Events API is the only automated discovery method - Skip blobs from node_modules/, vendor/, .yarn/, dist/, build/ paths - Prefixed service tokens are always real findings, no context filtering needed