
Oct 13, 2026
Nobody Reads Config Diffs With Feeling
Deleting a leaked secret doesn’t delete the key. Where hardcoded secrets hide, why rotation comes first, and why the pull request is the cheapest place to stop them.
Hardcoded secrets, leaked API keys, and why the pull request is the cheapest place to stop them. A key you delete is a key you can no longer see.
Two months.
That’s how long a live AWS access key sat in a config file at one of the startups we scanned this spring.
It was committed during an environment migration. Two months later, it still worked. Our scan flagged it. The team rotated it within the hour, which is the good part of the story.
The two months are the other part.
We weren’t there when it went in. But we’ve seen the pattern enough times to narrate it. Someone needs the deploy to work. The key goes in locally, “just to test.” The file gets staged with everything else, committed in one batch, pushed late. The PR looks fine. CI passes, because CI passes everything it wasn’t asked about.
And nobody reads a config diff with feeling.
What counts as a hardcoded secret?
Any credential that lives in your code instead of being injected when the code runs.
API keys. Database URLs with the password inline. OAuth client secrets. Private keys. Signing tokens. Webhook secrets. The token your newest agent needed to talk to the issue tracker.
It has a formal name: CWE-798, Use of Hard-coded Credentials. It also has an informal one: the cheapest breach an attacker will ever get.
How common is it, really?
More common than anyone’s .gitignore would like.
This spring we scanned 10 early-stage startups. All voluntary, all anonymized, each scan run in an isolated sandbox. Six had a secret that had already touched a production branch. Not an old feature branch. Main.
Here’s what that looked like:
1. Sitting in HEAD. A database URL with the admin password, in the current code, in plain sight.
2. Buried in history. Keys inside squash-merged commits nobody had opened since the merge.
3. Rotated, not removed. One team had rotated a live Stripe secret key and left it in the repo. Half the job. The important half, at least.
4. Still alive. The AWS key from the top of this post. Two months. Live.
A standard linter would have caught none of these. Something had to be looking for them on purpose.
- 6 of 10
- startups we scanned had a secret on a production branch
Source: Autter codebase scans, spring 2026. A small, voluntary sample, not an industry-wide estimate.
Where do hardcoded secrets actually hide?
Everyone checks the crate marked .env. Here are the seven nobody opens.
1. Source files. A key pasted inline “for a quick test.” The test passed. The key stayed.
2. Config and infrastructure-as-code. Database passwords in Terraform variables, Helm values, and Compose files. Config diffs get skimmed. Everyone knows it. Including the key.
3. Test fixtures and mocks. The “sandbox” key that, on closer inspection, is production.
4. Notebooks. Credentials saved in a cell’s output and committed with the notebook.
5. MCP and agent configs. Every agent you wire up wants a token. The tokens end up in mcp.json and tool definitions, and those get committed with the project.
6. CI logs. A debug step that prints the environment. Helpful once. Then permanent.
7. Git history. The key you “deleted” three commits ago.

Why doesn’t deleting the line fix it?
Because git rm is a very confident way to be wrong.
Once a secret is pushed, it’s compromised. Deleting it only hides the evidence from you. Git remembers everything. So do forks, mirrors, cached CI artifacts, and every teammate’s local clone. Public repositories are watched by automated scrapers looking for exactly this.
Removing the line is hygiene. Rotating the key is the fix.
Teams do the hygiene and skip the fix, for understandable reasons. Rotation means finding who owns the key, touching production, and accepting a few nervous minutes. Deleting a line takes ten seconds and feels like progress.
It isn’t progress. It’s closing the curtains on a house that’s already on fire.
Why do coding agents make it worse?
Not because they’re careless. Because they’re fast, and nobody briefed them.
An agent asked to make a webhook work will make the webhook work. If the env var won’t load, the value itself is right there. Then it writes the commit message. fix: webhook handler. Accurate. Also entirely silent about the key.
Three things change once agents write a real share of your pull requests:
More diffs. Every PR is another place a key can slip in. Human attention didn’t scale with the PR count.
Bigger diffs. A secret in file 31 of 40 is a secret nobody reads.
New kinds of files. Agent and MCP configs are now part of the repo, and most of them want a token.
“Agents don’t leak secrets because they’re careless. They leak them because they’re fast, and nobody told them the key was special.”
We covered the wider list of agent risks in Claude Code, Cursor, Codex: Fast Hands, Zero Fear of Consequences. Secrets are the one that’s cheapest to stop.
Where should you catch a secret?
As early as possible, and somewhere that can’t be waved through with one click.
Detection is mostly a solved problem. Good scanners match known key formats for the major cloud, payment, and AI providers, then flag long, random-looking strings that behave like tokens. The hard part is what happens after the scanner speaks.
| Control | When it fires | Good at | Where it slips |
|---|---|---|---|
| Pre-commit hook | On the developer’s machine, before the commit | Fastest feedback | Has to be installed everywhere. Skippable with --no-verify. New laptops and agent sandboxes often run without it |
| Push protection on your Git host | When the push reaches the host | Stops well-known key formats before they land | Usually bypassable by the person pushing. Strongest on formats it already knows |
| PR scanner that comments | When the PR opens | Visible to reviewers | A comment on a green PR gets resolved with one click |
| Merge gate | Before the PR can merge | Enforcement. The merge stays blocked until the key is gone | Only sees what’s in the repo |
| Scheduled repo sweep | Weekly, monthly, quarterly | Finding what’s already in history | Late. The key has been live the whole time |
Run more than one. Just make sure one of them can actually say no. That’s why we block the merge button instead of posting a comment.
What does a gate actually look like?
Illustrative example. The team, file paths, and timings are made up.
A developer is testing a payments webhook. The env var won’t load, so the key goes inline. The agent tidies up and opens a 14-file PR.
Without a gate. Two approvals. The key is on line 212, in file 11. Merged. A minute later it’s in main, in history, and in every clone made from here on. Week 6, a scheduled sweep finds it. Someone deletes the line. Ticket closed. The key still works.
With a gate. The PR opens. The gate flags a live payments key in src/webhooks/handler.ts, line 212. Merge blocked. Ten minutes later, the developer revokes the exposed key at the provider, stores its replacement in the secrets manager, and pushes the fix. Autter re-reviews just the change. Clear. The key never reached main and never shipped. (It did touch a branch, so rotation was still required.) But nobody spent six weeks not knowing.

Want to see the real thing? The live demo workspace has a payments-service change flagged critical for an exposed API key in environment config. Sample data, real product.
A key already leaked. Now what?
1. Treat it as compromised. Private repo, “only up for an hour,” doesn’t matter.
2. Rotate at the provider. Now. Revoking the value is what actually stops the risk. This stays a human step. The owner should cycle a production credential, not a bot.
3. Move it out of the code. Load it from a secrets manager or inject it at runtime. Not another .env waiting to be committed.
4. Purge history if it was public or widely shared. Then check the provider’s logs for anything that used the key in the gap.

Where does Autter fit (and where doesn’t it)?
Every PR that ships is a vessel requesting entry. Autter decides what clears the harbour.
It’s the assurance layer between every pull request and main, whoever opened it. Every PR executes in an isolated sandbox against your base branch. Dependencies are vetted against real registries. Secrets and CVEs are caught at the gate, and the merge button stays blocked until the checklist clears. Then it unlocks with the evidence attached.
Findings land on the exact line, ranked by severity, with a proposed fix. Push the fix and Autter re-reviews just the change.
Every change is classified human or AI, and generated code earns extra scrutiny.
Your agents can see the findings too. Through the Autter MCP server, the agent that added the key can be the one that removes it.
Your standards stay yours. Point Autter at your team’s rules and exclusions in a config file you own.
Everything ships with a receipt: the exact run, the failing case, the trace.
And for what’s already sitting in main, there are Codebase Scans: on demand, on a schedule, or at release time.
The honest part:
Autter won’t rotate your keys. Revoking a production credential is a human call, and it should stay one.
Autter only sees the repo. A key pasted into Slack, a ticket, or a shared doc is out of reach.
Autter can’t un-leak history. If a key already shipped, the playbook above comes first. We stop the next one.
Autter isn’t a secrets manager. It makes sure keys end up in one. It doesn’t store them.
Autter will sometimes be wrong. If a finding doesn’t hold up, you dismiss it. Nothing blocks your merge on a hunch.
Frequently asked questions
Why isn’t deleting the line enough?
Git history, forks, and every clone still hold the value. Rotate the key at the provider, remove it from the code, then purge history if it was exposed.
Is secret scanning the same as SAST?
No. Secret scanning finds committed credentials. SAST finds insecure code patterns, like injection or a missing auth check. You want both on every pull request.
How does a scanner tell a secret from a random string?
Two signals. Known formats, which are high-confidence patterns for specific providers. And entropy: long, random-looking strings that behave like tokens even when the format is unfamiliar. Entropy finds more and guesses more, which is why every finding needs context, not just a regex match.
Is a private repository safe enough?
No. Private code still lands on laptops, CI runners, and contractor machines, and six of the ten startups we scanned had a secret on a production branch. “Private” describes who’s supposed to see it. Not who can.
Do AI coding agents leak more secrets?
They write more code, faster, in bigger diffs, and they bring new config files that want tokens. More chances for a key to slip in, fewer human eyes per line. The fix isn’t slowing the agent down. It’s putting a gate after it.
Our read
Back to that config file.
Two months. One live AWS key. A team that did everything right the moment they knew. They just knew two months late.
Hardcoded secrets are the rare security problem that’s cheap to prevent and expensive to ignore. AI didn’t invent it. It multiplied the number of commits where it can happen.
So stop treating a found key as cleanup. It’s an incident. And stop treating the pull request as a place for suggestions. For secrets, it’s a gate.
Calm seas ahead. Hire Autter for a peaceful weekend.
P.S.
Sagnik: I found a key in the repo. Rotated it. Deleted it. Wrote a postmortem.
Tanvi: Good.
Sagnik: Then I found the same key in the postmortem.
Tanvi: Sagnik.
Sagnik: It was a very thorough postmortem.
(currently: the key is dead, the postmortem is redacted, and Tanvi reads every postmortem now)
Sources: Autter: We scanned 10 startup codebases · CWE-798: Use of Hard-coded Credentials · OWASP Secrets Management Cheat Sheet · GitHub Docs: About secret scanning
Keep reading: We scanned 10 startup codebases · Why We Block the Merge Button Instead of Posting a Comment · How to Prioritize Vulnerabilities: CVSS, EPSS, KEV, and Reachability

