← All news
configuration5 min read

Your AI Assistant Just Committed Your Staging API Key

By Seaworthy · November 7, 2026

Multiple web browser icons displayed on a desktop screen, including Microsoft Edge, Firefox, Google Chrome, Opera, and Brave

Photo by Denny Müller on Unsplash

A developer asks an AI assistant to wire up a payment client. The assistant does the helpful thing: it writes the API key right into the config file so the code runs on the first try. The developer commits. Three weeks later the staging key is in a public fork, and nobody knows how long it has been there.

I keep seeing this pattern, and I have stopped treating it as a one-off mistake. It is a predictable outcome of how these tools work. An assistant optimized to produce code that runs will inline whatever values make that code run. It does not know that the key belongs in an environment variable. It does not know that the repository is public, or that a contractor cloned it last month. It just knows the function needs a string.

Why the shortcut feels safe

Inlining a secret makes the code self-contained. There is no setup step, no missing variable, no confusing error about undefined on startup. That is exactly why it slips past review. The diff looks clean, the tests pass, and the value is typo-free because it was pasted from a dashboard. The danger is invisible at the moment of the commit. It only shows up later, in history, in forks, in logs, and in whatever scans a dependency tree.

Many readers of this blog will say they would catch it. Some will not, and the gap is wider when the person pasting is an assistant rather than a person. Assistants do not hesitate. They do not feel the small pause a human feels before writing a live credential into a file. They just produce plausible code.

The pattern to watch for

Any secret baked into a tracked file is the problem, whether it is a token, a password, or a connection string.

# Bad: the secret is in source, and git will remember it
PAYMENTS_API_KEY = "sk_live_51H8xQ2..."

def client():
    return PaymentClient(api_key=PAYMENTS_API_KEY)

Deleting the line in a later commit does not remove it. Git keeps every version, and clones and forks keep copies. A secret that was ever committed should be treated as exposed, full stop.

The same trap shows up in less obvious spots. A test fixture with a real token so the suite passes. A Dockerfile with credentials in an ENV line. A notebook cell that gets committed with output still attached. Each of these feels like a local convenience, and each one ships the value to every machine that ever pulls the branch. When an assistant generates any of them, it is usually because the surrounding code gave it no other way to get a working value, so the fix belongs in the project as much as in the prompt.

The fix, and why it is not enough on its own

The fix is to read configuration at runtime and keep the value out of the repository.

# Better: the value comes from the environment
import os

PAYMENTS_API_KEY = os.environ["PAYMENTS_API_KEY"]

def client():
    return PaymentClient(api_key=PAYMENTS_API_KEY)

Add a .env file for local work, list it in .gitignore, and provide a committed .env.example with placeholder names only. That gets you most of the way. It is not the whole answer. A variable name in source still tells an attacker what to look for, and a misconfigured deploy will fail loudly at runtime rather than at commit time. Both are acceptable tradeoffs. Silent success with a leaked key is worse.

What I would change about the workflow

My reaction is that we are asking the wrong party to be careful. The assistant cannot know your deployment model, so give it instructions. Put a line in your project rules or system prompt: never write credentials into source, always read them from environment variables, always update .env.example when adding a new one. It is a small change and it removes most of the failure mode.

Second, make the repository defend itself. A pre-commit hook that scans staged changes for high-entropy strings and known key prefixes catches the paste before it lands. Enable secret scanning on the hosting provider. Rotate anything that got through instead of deleting the line and hoping.

Third, review AI-generated diffs the way you review a stranger's pull request. The code may be correct and still be wrong. Configuration changes deserve the same skepticism as authentication code, and they usually get less.

There is also a reporting habit worth building. When a scan or a hook does catch a key, write down what produced it: which assistant, which prompt, which file. A pattern of leaks from one tool or one template is a signal to fix the template, not just the leak. Teams that track this stop repeating the same mistake in a new folder every sprint.

Secrets in source are an old problem wearing new clothes. The assistant makes it faster and more common, not fundamentally different. Treat every generated file as untrusted input, keep credentials in the environment, and assume that anything committed once is already gone.

Seaworthy is a developer security company that reviews how teams configure and store credentials.

This article was generated by AI and summarises publicly available sources.

From the makers of Seaworthy

Seaworthy scans your repo for the issues covered in articles like this one.

Security gaps, exposed secrets, and misconfigurations — caught before you deploy. Free to run, no account needed.

$ npx seaworthycode