Scan git history for secrets, API keys, and passwords — even ones deleted long ago.
Project description
secret-time-machine
"The Past is Not Private"
A Python CLI tool that scans the entire git history of a repository for secrets, API keys, passwords, and tokens — including ones that were "deleted" long ago. git rm does not erase history. Every secret ever committed is still there.
Why this exists
Developers often commit secrets accidentally and then delete them with git rm or a follow-up commit. The secret looks gone from the working tree, but it is permanently embedded in git history. Anyone who clones the repo — now or in the future — can read every commit, including the deleted one.
secret-time-machine time-travels through every commit and every diff to surface those forgotten credentials.
Installation
pip install secret-time-machine
Or from source:
git clone https://github.com/yourusername/secret-time-machine
cd secret-time-machine
pip install -e .
Quick start
# Scan the current directory's git history
secret-time-machine
# Scan a specific repo
secret-time-machine --repo /path/to/repo
# Output JSON for CI pipelines
secret-time-machine --json
# Show full secret values (default is to redact)
secret-time-machine --show-secrets
# Get remediation instructions
secret-time-machine --remediation
All options
Usage: secret-time-machine [OPTIONS]
Options:
--repo PATH Path to the git repository to scan.
--branch TEXT Scan only commits on this branch (default: all).
--last N Scan only the last N commits.
--additions-only Only report secrets in addition lines (+ lines).
--json Output results as JSON.
--show-secrets Show full secret values (default: redact).
--redact Explicitly redact secret values (this is the default).
--remediation Show instructions for cleaning secrets from history.
--version Show the version and exit.
--help Show this message and exit.
What it detects
| Pattern | Severity |
|---|---|
AWS Access Key (AKIA...) |
HIGH |
| AWS Secret Key | HIGH |
GitHub Token (ghp_...) |
HIGH |
GitHub OAuth (gho_...) |
HIGH |
OpenAI API Key (sk-...) |
HIGH |
Anthropic API Key (sk-ant-...) |
HIGH |
Stripe Secret Key (sk_live_...) |
HIGH |
Google API Key (AIza...) |
HIGH |
Slack Token (xox...) |
HIGH |
| Private Keys (RSA, EC, OPENSSH) | HIGH |
| SendGrid API Key | HIGH |
HuggingFace Token (hf_...) |
HIGH |
NPM Token (npm_...) |
HIGH |
| Database URLs (postgres, mongo, redis) | HIGH |
Generic Password (password = "...") |
MEDIUM |
Generic Secret (api_key = "...") |
MEDIUM |
| JWT Tokens | MEDIUM |
| Heroku API Keys | MEDIUM |
| And more... |
Example output
╔══════════════════════════════════════════╗
║ SECRET TIME MACHINE ║
║ "The Past is Not Private" ║
╚══════════════════════════════════════════╝
Scanning: /Users/user/myproject
Commits scanned: 1,247 (across all branches)
Time range: 2021-03-14 to 2024-11-22
SECRETS FOUND IN HISTORY: 2
x OpenAI API Key HIGH
Commit: a4f8c2d (2023-08-14 by jane@example.com)
File: config/settings.py
Status: DELETED in commit b9e3f1a — still in history, key may be compromised
Value: sk-••••••••••••••••••••••••XKBP
x AWS Access Key HIGH
Commit: 3c7d9a1 (2022-11-30 by bob@example.com)
File: deploy/terraform.tf
Status: Still present in current codebase!
Value: AKIA••••••••••••MNPQ
How it works
- Runs
git log --all --format="%H %ae %aI"to collect every commit hash across all branches. - For each commit, runs
git diff-tree --no-commit-id -p -r {hash}to get the full patch. - Scans every line starting with
+(additions) against all secret patterns. - Checks whether each found secret is still present in
HEADor was later deleted. - Reports findings sorted by severity, with optional JSON output for CI integration.
Remediation
Run secret-time-machine --remediation for the full guide. Short version:
- Use
git filter-repoto purge secrets from history (or BFG Repo Cleaner for large repos). - Force-push the rewritten history.
- Rotate every exposed credential immediately.
- Add
.envfiles to.gitignoreand install pre-commit hooks to prevent future leaks.
CI integration
Use the --json flag and check the exit code. The tool exits with code 1 if any secrets are found, 0 if the history is clean.
# GitHub Actions example
- name: Scan git history for secrets
run: secret-time-machine --json --additions-only > scan-results.json
continue-on-error: true
- name: Fail if secrets found
run: |
count=$(jq '.summary.total_findings' scan-results.json)
if [ "$count" -gt 0 ]; then exit 1; fi
Development
# Install dev dependencies
pip install -e ".[dev]"
# Run tests
pytest -v
# Type checking
mypy src/
# Formatting
black .
isort .
Security note
This tool calls git via subprocess with a list of arguments (never shell=True) and uses shlex.quote() for any user-provided paths, preventing shell injection attacks.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file secret_time_machine-1.0.0.tar.gz.
File metadata
- Download URL: secret_time_machine-1.0.0.tar.gz
- Upload date:
- Size: 21.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5d623aaf153858db66e07461881afaff3aaec15e14dd029cc5cc7f03f55dc5c7
|
|
| MD5 |
8f7bf550c83e0a4d20f07b2e29c7f5cc
|
|
| BLAKE2b-256 |
a5d1664ce572cfb734593a64a51aea563a397245574cdd9e22cce70827b61302
|
File details
Details for the file secret_time_machine-1.0.0-py3-none-any.whl.
File metadata
- Download URL: secret_time_machine-1.0.0-py3-none-any.whl
- Upload date:
- Size: 16.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e723dfd7106c1f31e1105a1352cdd496bf97be58829849454972870302b97764
|
|
| MD5 |
e8aa566471d2a0828a50a9b587b1c928
|
|
| BLAKE2b-256 |
d1884ecd4230c6ce4d900e9512fbd7c3309909783ba5f2de350f57b6022322a8
|