---
title: Nobody Reads Config Diffs With Feeling - Autter Blog
description: 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.
url: https://autter.dev/blog/nobody-reads-config-diffs-with-feeling
date: 2026-10-13
author: Sagnik
tags: Security, AI Safety, Merge Gate
reading_time: 8 min
site: Autter - Autter is your AI teammate for code review and testing: it reviews code, tests product impact, checks security, governs releases, and closes the loop from production failure to verified fix.
---

[← All posts](https://autter.dev/blog)

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.

Sagnik, Founder, autter.dev 8 min read

- Security
- AI Safety
- Merge Gate

*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.

The short answer

A hardcoded secret is any credential written into your code instead of loaded at runtime. Once it’s been pushed, treat it as compromised. Rotate it at the provider first, then remove it from the code and, if it was exposed, from Git history. Deleting the line on its own fixes nothing. The cheapest fix sits upstream: check every pull request and block the merge when a live credential shows up, so the key never reaches main.

## 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.

Lesson

If a string would let a stranger act as you, it doesn’t belong in a file Git can see.

## 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](https://autter.dev/blog/we-scanned-10-startup-codebases). A small, voluntary sample, not an industry-wide estimate.*

Lesson

“It’s a private repo” is not a control. Private repos get cloned to laptops, CI runners, and contractor machines.

## 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.

![Blackboard illustration of exposed keys hidden in source files, configuration, test fixtures, notebooks, agent settings, CI logs, and Git history.](https://autter.dev/blog/hardcoded-secrets-locations-v2.webp)

*The obvious environment file is only one place to look. Check source, infrastructure, fixtures, notebooks, agent configs, logs, and history too.*

## 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.

Lesson

Rotate first. Clean up second. In that order, every time.

## 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](https://autter.dev/blog/ai-coding-assistant-security-risks). 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](https://autter.dev/blog/why-we-block-the-merge-button-instead-of-posting-a-comment).

Lesson

A check you can ignore is just advice.

## 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.

![Blackboard illustration of a pull-request diff with an exposed key under a magnifying glass, stopped by a merge barrier before the main repository.](https://autter.dev/blog/hardcoded-secrets-merge-gate-v2.webp)

*Illustrative example: a gate keeps the key out of main. Rotation still matters when it has touched a branch.*

Want to see the real thing? The [live demo](https://app.autter.dev/demo-access) 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.

![Blackboard illustration of a revoked broken key, its replacement stored in a secure vault, and access logs inspected with a magnifying glass.](https://autter.dev/blog/hardcoded-secrets-playbook-v2.webp)

*Rotate first. Clean up second.*

## 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](https://autter.dev/features/code-review) 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](https://autter.dev/features/mcp), 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.**

[Start Now](https://app.autter.dev/) · [Book a demo](https://autter.dev/contact)

*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](https://autter.dev/blog/we-scanned-10-startup-codebases) · [CWE-798: Use of Hard-coded Credentials](https://cwe.mitre.org/data/definitions/798.html) · [OWASP Secrets Management Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html) · [GitHub Docs: About secret scanning](https://docs.github.com/en/code-security/secret-scanning/introduction/about-secret-scanning)

**Keep reading:** [We scanned 10 startup codebases](https://autter.dev/blog/we-scanned-10-startup-codebases) · [Why We Block the Merge Button Instead of Posting a Comment](https://autter.dev/blog/why-we-block-the-merge-button-instead-of-posting-a-comment) · [How to Prioritize Vulnerabilities: CVSS, EPSS, KEV, and Reachability](https://autter.dev/blog/how-to-prioritize-vulnerabilities)

### See Autter on your own code.

A 30-minute walkthrough with the founders. Bring a repo and watch Autter review a live pull request.

[Book a demo](https://autter.dev/contact)

## Put an assurance layer on every release.

Autter reviews every pull request before it merges, backed by humans. Connect a repo and see your first findings today.

[Get started](https://app.autter.dev/login)

## Keep reading

[Oct 10, 2026 · 6 min read The PR Queue Wasn’t Built for This Much Code AI made code abundant. Human attention did not scale with it. Why we built Autter Triage around the changes that actually deserve deeper review.](https://autter.dev/blog/the-pr-queue-wasnt-built-for-this-much-code)
