← Toutes les actualités
security4 min de lecture

Hardcoded secrets are the most common finding in vibe-coded repos

Par Seaworthy · 9 mars 2026

An open combination padlock resting on a computer keyboard, photographed in close-up

Photo by Sasun Bughdaryan on Unsplash

Run a static analysis scan over a hundred AI-assisted repos and one finding comes up more than any other: secrets hardcoded directly in source. Not in obscure edge cases. In the main branch, committed weeks ago, sitting in a public repository where any automated scanner can pick them up within minutes.

This is not a story about careless developers. It is a story about how LLMs work.

Why the model keeps doing this

When you prompt an LLM for a working example, it optimises for a response that runs. The fastest path to a running example is to fill in concrete values. Ask for a Stripe integration and you will get const stripe = new Stripe('sk_live_...') before you have time to blink. Ask for a database connection and you get a connection string with a real-looking password baked in. The model is not being negligent; it genuinely does not know whether you are going to copy-paste this into production or read it as pseudocode.

The problem is that most people copy-paste it into production.

There is a secondary issue: AI-assisted development moves fast. You are iterating quickly, trying to get something working, and the environment variable plumbing feels like friction you will sort out later. Later never comes. The commit lands, the push goes out, and the secret is now in git history permanently, even if you delete the file in the next commit.

The lifecycle of a leaked secret

GitHub's secret scanning and tools like truffleHog run continuously across public repositories. The window between a secret being pushed and being found by an automated scanner is measured in minutes, sometimes seconds. Once it is in git history, removing the file is not enough. The object is still in the pack, reachable via git log -p or by anyone who cloned the repo before you realised.

From there the path is predictable. Stripe keys get used for fraudulent charges. AWS keys spin up mining instances. Database passwords get exfiltrated. JWT signing secrets mean an attacker can forge any session token in your system without ever touching the database.

The fix is not just rotating the secret, although you must do that immediately. The fix is making the pattern impossible to repeat.

Scanning what is already there

Before you can fix the problem you have to know its extent. Two tools are worth running right now.

truffleHog scans your entire git history, not just the current working tree:

# Scan the full git history of the current repo
trufflehog git file://. --only-verified

# Or point it at a remote repo
trufflehog github --repo https://github.com/your-org/your-repo --only-verified

The --only-verified flag calls the relevant API to confirm the secret is still active. Dead keys are noise; live keys are incidents.

git-secrets from AWS Labs is lighter-weight and better suited to local enforcement:

brew install git-secrets
git secrets --install        # installs hooks into current repo
git secrets --register-aws   # adds AWS key patterns
git secrets --scan-history   # scans all commits

If scan-history returns hits, treat each one as a live incident until you have confirmed the secret has been rotated.

Migrating without breaking things

The mechanical fix is straightforward. Replace the hardcoded value with process.env.YOUR_SECRET, add YOUR_SECRET to your .env file, and add .env to .gitignore. Here is what that looks like for a common pattern:

// Before: what the LLM gave you
const stripe = new Stripe('sk_live_AbCdEfGhIjKlMnOpQrStUvWx123456');

// After: what actually belongs in source control
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY ?? (() => {
  throw new Error('STRIPE_SECRET_KEY is not set');
})());

The explicit throw matters. Silent failures, where the key is undefined and the SDK initialises with an empty string, are harder to debug than a startup crash with a clear message.

For production deployments, .env files are a stepping stone, not a destination. Move secrets into a proper secret manager: AWS Secrets Manager, HashiCorp Vault, Doppler, or whatever your platform provides. The pattern is the same from the application's perspective, reading from process.env, but the secrets are injected at runtime by your infrastructure rather than stored in a file that someone will eventually commit.

Blocking secrets before they land

Rotating secrets and scanning history is remediation. Prevention is a pre-commit hook that refuses to let secrets reach the remote in the first place.

detect-secrets from Yelp is the most robust option for this:

pip install detect-secrets

# Generate a baseline of known false positives in your repo
detect-secrets scan > .secrets.baseline
# .pre-commit-config.yaml
repos:
  - repo: https://github.com/Yelp/detect-secrets
    rev: v1.4.0
    hooks:
      - id: detect-secrets
        args: ['--baseline', '.secrets.baseline']

The baseline file is important. It lets you acknowledge existing false positives, like example API key formats in documentation, without suppressing real detections. Commit the baseline and review changes to it in pull requests.

One thing to be clear-eyed about: pre-commit hooks only run locally. A developer on a machine without the hooks installed, or someone pushing directly via the API, bypasses them entirely. Pair local hooks with a server-side check in CI. GitHub Actions has a secret scanning step you can add in minutes, and it runs on every push regardless of local tooling.

The pattern you are fighting

Every time you or your team asks an LLM to generate a working integration, assume the output will contain hardcoded credentials. Not because the model is broken, but because that is what "working" looks like to it. Build the habit of treating any string that looks like a secret as a placeholder, not a value to keep.

A .env.example file committed to the repo, with placeholder values and a comment explaining where to get real credentials, goes a long way. It makes the intended workflow obvious to anyone who clones the repo, including future you.

Seaworthy flags hardcoded secrets automatically, scanning both your working tree and git history as part of each analysis run.

Cet article a été généré par IA et résume des sources publiquement disponibles.

Par les créateurs de Seaworthy

Seaworthy analyse votre dépôt pour détecter les problèmes abordés dans des articles comme celui-ci.

Failles de sécurité, secrets exposés et mauvaises configurations détectés avant le déploiement. Gratuit, aucun compte requis.

$ npx seaworthycode