On this page
I ran into three key-leak situations this week. None of them was the classic “pasted it somewhere public” mistake. They all came out of places I thought were safe: packaging, a masking command, and a build config. This post covers the mechanisms only, with no values.
Want AI working in your own job, not just in demos? See the 3-month 1-on-1 program →
Entry one: packaging
My harness has a skill, and I made a separate version of it for uploading through the web. That web-upload version had TikHub and Apify keys embedded in it, and it had already gone to a private remote across two commits.
I assumed the backup key scan would catch it. It couldn’t match the apify_api_ prefix. A green scan doesn’t mean there are no keys inside. It only means there are none in the formats the scan knows. I’ve added that rule.
A private remote is not the same as no leak. Once it’s pushed, it’s pushed.
Entry two: the masking command itself
While dealing with that, I masked the key with sed, but BSD sed on macOS doesn’t support \s.
So the pattern didn’t match, the substitution never happened, and the full value was printed straight into the conversation. The tool reported no error and the output looked normal. The thing that was supposed to be hidden just wasn’t.
Entry three: the build config
The third one is the front end of my own product. vite.config.ts has a define block that bakes the local .env’s GEMINI_API_KEY straight into the front-end bundle.
CI doesn’t have that environment variable, so the production build CI produces is clean. The key only goes out with the bundle when I build locally and deploy from my machine.
I’ve opened a to-do to fix the config. Until it’s fixed, the rule is that every manual deploy starts by clearing that variable.
Two days later I checked: that key was already invalid, it had seen no meaningful usage for six weeks, and the current bundle no longer carries it.
Rotate, or just warn
With all three handled, I went back to my own rule for deciding whether to rotate a key. I check first and decide second, instead of going on gut feeling:
- Run
git log --all -S "<prefix>", then look atgit remote -v. - If it only exists locally, or only appeared in this conversation, and never entered git history or got pushed to a remote, I just warn and clear the records. No rotation.
- If it was pushed to a remote, private GitHub repos included, I treat it as leaked and rotate. Deleting history can’t recall values that were already cloned or cached.
- When reporting an exposure, always mask it: first 6 characters plus the last 2, never the full key.