TL;DR
I pushed to the wrong repository. A private repo's code went to a public one. The commit contained database credentials, API keys, and a JWT secret. GitHub's secret scanning caught most of it, but the 15 minutes before I noticed felt like hours. Everything had to be rotated, and I learned to never have both public and private remotes configured.
The Setup
I maintain both open source projects and private client work. For convenience, I had multiple git remotes configured on my machine. One project had both a private backup remote and a public fork.
You can probably see where this is going.
The Mistake
I was working late, finishing a feature for a client project. The codebase included a config.js file with database credentials. Yes, I know. Environment variables. I was going to fix it "soon."
I typed what I thought was a push to my private backup:
git push origin main
But my current directory wasn't what I thought it was. I was in the wrong project folder. And "origin" in that folder pointed to a public repository.
The push completed. I didn't notice for about 10 minutes.
The Discovery
I got an email from GitHub: "Secret scanning alert for repository name."
My heart stopped. I opened the email and saw it had detected:
- A database connection string with username and password
- An AWS access key
- A Stripe secret key
I sprinted to my computer and checked the repository. There it was. My client's entire codebase, including the config file with credentials, was now public.
The Scramble
The next 30 minutes were a blur of activity:
- Made the repo private (this doesn't undo the exposure, but stops new access)
- Rotated every credential in the config file
- Changed the database password
- Revoked and regenerated the AWS keys
- Rotated the Stripe API key
- Changed the JWT secret (which invalidated all user sessions)
Then came the embarrassing part: telling the client.
The Conversation
I called the client immediately. I explained exactly what happened, what was exposed, and what I'd already done to fix it.
"Look, I made a mistake. I pushed to the wrong repository. Your database credentials were briefly public. I've already rotated everything, but I wanted you to know immediately."
The client was understandably concerned. We reviewed the logs together. There was no evidence of unauthorized access, but we couldn't be 100% certain. The credentials had been public for about 15 minutes.
15 minutes is plenty of time. Automated bots scan GitHub constantly. Research shows credentials can be discovered and tested within minutes of being pushed. Even brief exposure is exposure.
What I Did Wrong
1. Credentials in Code
The fundamental problem. Even in a "private" project, credentials shouldn't be in code. Environment variables exist for a reason.
2. Multiple Remotes Without Safeguards
Having both public and private remotes configured is risky. It's too easy to push to the wrong one.
3. No Pre-push Hook
A pre-push hook could have scanned for secrets and blocked the push. I had one set up on my main projects but not on this one.
4. Not Double-Checking the Directory
I assumed I was in the right folder. A quick pwd or git remote -v would have shown me the mistake.
Changes I Made
Global Git Hooks
I set up global pre-commit and pre-push hooks that scan for common secret patterns. They run on every repository, not just the ones I remember to configure.
# ~/.config/git/hooks/pre-push
# Scans for common secret patterns before any push
if git diff --cached | grep -qE "(sk_live|AKIA|password\s*=)"; then
echo "Potential secret detected. Push blocked."
exit 1
fi
Removed Public Remotes from Private Projects
If a project is private, it has no public remotes. Period. If I need to create a public version, I do it in a separate, clean repository.
Prompt Shows Current Directory
I modified my shell prompt to prominently display the current directory and git remote. I can see at a glance where I am and where I'm pushing to.
Client Work on Separate Machine
Client work now happens on a separate development machine with no personal projects. Eliminates the chance of cross-contamination.
The lesson: Multiple layers of defense matter. Any single safeguard can fail. When you have credentials in code AND multiple remotes AND no pre-push hooks AND you're tired, disaster happens. Remove as many failure points as possible.
What GitHub Did Right
GitHub's secret scanning detected the exposed credentials within minutes and sent me alerts. For some services (Stripe, AWS), they also notified the providers, who revoked the credentials automatically.
This is a safety net, not a solution. But it limited the damage significantly.
Can I remove secrets from git history?
Yes, using tools like BFG Repo-Cleaner or git filter-branch. But this doesn't help if the secret was already copied by bots or attackers. Always assume an exposed secret is compromised and rotate it.
Does making a repo private remove the exposure?
No. Making a repo private stops future access, but anyone who already cloned the repo or copied the secrets still has them. Additionally, search engines and archive services may have cached the content.
How do I prevent pushing to the wrong remote?
Remove unnecessary remotes from your git config. Use unique, descriptive remote names. Add confirmation prompts with pre-push hooks. Display current remote in your shell prompt.
What if I can't rotate a credential immediately?
Contact the service provider. Many have emergency processes for compromised credentials. At minimum, add additional authentication (IP restrictions, MFA) while you work on rotation.
Scan for Exposed Secrets
Check your repositories for accidentally committed credentials.
Start Free Scan