How to check a repo for secrets before you make it public
Deleting an API key from a file does not delete it from your repository. It is still in the object store, it is still reachable, and the moment you flip that repo to public it is indexed by people whose entire job is finding it. This is the single most common way a student project leaks a credential, and it takes about ten minutes to prevent.
I wrote a security scanner to automate this for myself, so this checklist is what my own tool actually does, in order, explained so you can do it by hand if you would rather.
The five-minute version
- Search the working tree for secrets.
- Search the git history for secrets, which is a different search.
- Check what is actually tracked, not what you think is ignored.
- Check dependencies for known vulnerabilities.
- Check the deployed thing, because half of real exposure is config, not code.
Most people do step one, feel relieved, and skip the other four. Step two is the one that bites.
1. The working tree
Start with the obvious pass, but widen the net past the patterns you already know:
- Provider key shapes: AWS, Google, Stripe, Slack, GitHub, OpenAI, Anthropic. These are all recognizable prefixes and they are the easy ones.
- Private key blocks. Anything beginning
-----BEGIN. - Connection strings with credentials inline.
postgres://user:pass@hostis a secret even though it does not look like a key. - Long random-looking strings assigned to a variable whose name contains token, secret, password, passwd, apikey, auth, or bearer.
- Base64 blobs. People encode a secret, decide that counts as hiding it, and commit it.
.envfiles. Also.env.local,.env.backup,.env.old, and the one someone renamedenv.txtso it would stop being ignored.- SQLite databases. A committed
.dbfile can contain live user data, and it will not match any secret pattern at all.
2. The history, which is the one that gets people
This is the step that matters. Your commit that removed the key added a new commit. The old blob containing the key is still in the repository and anyone who clones it gets it.
To see for yourself, this walks every blob that has ever existed rather than only the current checkout:
git rev-list --objects --all
Piping that through a pattern search across the blob contents is what history scanning actually is. Doing it by hand on a repo with any real history is slow, which is exactly why I automated it.
And the part people do not want to hear: if a real credential was ever committed, rotate it. Do not rewrite history and call it handled. Rewriting is worth doing, but the moment a secret hits a remote you must assume it is compromised, because forks, caches, CI logs, and mirrors do not care about your rebase. Rotate first, clean second.
3. What is actually tracked
A .gitignore entry does nothing for a file that was already tracked before you
added the rule. Git keeps tracking it. This surprises people constantly.
git ls-files shows you the truth. Read it. Look specifically for anything ending
in .env, .pem, .key, .p12,
.sqlite, .db, or .pfx, plus anything in a folder called
config, secrets, or credentials.
Also check whether your repo should be public at all. If it contains anything from an employer, an internal schema, or a customer's data, the answer is no, and no amount of scanning changes that.
4. Dependencies
Your lockfile is a list of other people's code that runs with your permissions. You can check it against the OSV database, which is free, public, and needs no account. Query it with your package name and version and it tells you the known vulnerabilities.
Two things worth flagging while you are in there. First, transitive dependencies are where the surprises live, so check the resolved lockfile, not your manifest. Second, check licenses at the same time. A GPL dependency deep in a product you intend to sell is a business problem wearing a technical costume, and it is much cheaper to find now.
5. The deployed thing
Code scanning cannot see your headers. Once the project is live, check the boring config layer:
- TLS present, valid, and not expiring in nine days.
- Security headers actually set. Content Security Policy, HSTS, frame options, referrer policy, and X-Content-Type-Options.
- CORS not set to a wildcard because you were debugging at 2am and never changed it back. This one is extremely common.
- No debug or verbose error mode in production. Stack traces are a map of your application.
- Storage buckets not publicly listable.
The category most checklists miss: LLM integration
If your project calls a model, you have surface that did not exist in older checklists and that most scanners still say nothing about:
- Prompt injection. If untrusted text reaches a model whose output then triggers an action, you have a path from a stranger's input to your system doing something. Treat model output as untrusted input, always.
- Keys in the client. A model API key in frontend JavaScript is public. It does not matter that it is minified.
- Logging whole prompts. Convenient in development, and then your logs contain everything a user ever typed, including things they would not have typed if they knew you were keeping them.
- Unbounded spend. No rate limit on an endpoint that calls a paid model is a way to receive an invoice.
Automating the whole thing
I built DIRA because every scanner I found wanted me to install a large dependency tree in order to go looking for supply chain risk, which struck me as funny in a way that is not funny. It has zero dependencies, it is MIT licensed, it is free, and it runs everything above: secrets, git history, dependency CVEs through OSV, config and infrastructure-as-code rules including the LLM ones, license risk, and live TLS and header checks.
It outputs SARIF so it drops into GitHub code scanning, and it has a diff mode so you can gate pull requests on it. There is more on how and why in the write-up.
If you run it on something real and it misses something it should have caught, that is the feedback I want most.
Scan the tree. Scan the history separately. Read git ls-files with your own
eyes. Rotate anything that was ever committed, even if you removed it. Check the lockfile
against OSV. Then check the deployed config, because that is where the other half of real
exposure lives.
Free tools, three installable offline apps, and two SIGCSE 2026 papers. See the work or read the facts. Open to Summer 2027 software engineering, AI/ML, and quant internships.