TL;DR
An indie SaaS developer 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 he noticed felt like hours. Everything had to be rotated, and he learned to never have both public and private remotes configured.
The Setup
The developer maintained both open source projects and his own SaaS product. For convenience, he had multiple git remotes configured on his machine. One project had both a private backup remote and a public fork.
You can probably see where this is going.
The Mistake
He was working late, finishing a feature for a client integration in his SaaS app. The codebase included a config.js file with database credentials. Yes, environment variables exist. He was going to fix it "soon."
He typed what he thought was a push to his private backup:
git push origin main
But his current directory wasn't what he thought it was. He was in the wrong project folder. And "origin" in that folder pointed to a public repository.
The push completed. He didn't notice for about 10 minutes.
The Discovery
He got an email from GitHub: "Secret scanning alert for repository name."
His heart stopped. He opened the email and saw it had detected:
- A database connection string with username and password
- An AWS access key
- A Stripe secret key
He sprinted to his computer and checked the repository. There it was. His SaaS product'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 uncomfortable part: figuring out if any of his paying customers had been affected.
The Aftermath
He checked his logs immediately. He reviewed every database query and API call from the window of exposure.
"I had about 200 paying users at that point. I kept refreshing the access logs, looking for anything out of the ordinary. The thought of someone accessing my users' data because of my mistake was gut-wrenching."
There was no evidence of unauthorized access, but he 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 He 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. He had one set up on his main projects but not on this one.
4. Not Double-Checking the Directory
He assumed he was in the right folder. A quick pwd or git remote -v would have shown the mistake.
Changes He Made
Global Git Hooks
He set up global pre-commit and pre-push hooks that scan for common secret patterns. They run on every repository, not just the ones he remembers 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 he needs to create a public version, he does it in a separate, clean repository.
Prompt Shows Current Directory
He modified his shell prompt to prominently display the current directory and git remote. He can see at a glance where he is and where he's pushing to.
Separate Workspaces for Client and Personal Projects
Client work and his SaaS product now live in separate workspace directories. No more mixing open source forks with production codebases on the same machine.
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 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.