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
Metadata
Release files for secret-time-machine 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| secret_time_machine-1.0.0.tar.gz | 21.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| secret_time_machine-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 38.1 kB
Release files / secret_time_machine-1.0.0.tar.gz
| Download URL | secret_time_machine-1.0.0.tar.gz |
|---|---|
| Size | 21.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5d623aaf153858db66e07461881afaff3aaec15e14dd029cc5cc7f03f55dc5c7
|
|
BLAKE2b-256 checksum How to use checksums |
a5d1664ce572cfb734593a64a51aea563a397245574cdd9e22cce70827b61302
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.6
|
Release files / secret_time_machine-1.0.0-py3-none-any.whl
| Download URL | secret_time_machine-1.0.0-py3-none-any.whl |
|---|---|
| Size | 16.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e723dfd7106c1f31e1105a1352cdd496bf97be58829849454972870302b97764
|
|
BLAKE2b-256 checksum How to use checksums |
d1884ecd4230c6ce4d900e9512fbd7c3309909783ba5f2de350f57b6022322a8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.6
|