Ask an AI assistant to "wire up Stripe" or "connect to the database" and watch
what it produces:
const stripe = new Stripe("sk_live_51H8xY2eZvKf..."); // demo key it left in
const db = new Pool({ password: "changeme" }); // placeholder it forgot to remove
const JWT_SECRET = "your-secret-key"; // the classic
Coding assistants optimize for "runs on the first try," and the fastest path to
runnable code is a literal in place. So they hardcode demo keys,
placeholder credentials, and bare config literals — at the speed they
generate everything else. That's CWE-798 (Use of Hard-coded Credentials),
and it now enters codebases faster than any human ever added it.
Here's the twist that makes this fixable rather than just alarming: the same
property that makes AI a prolific source of these bugs — it reads and writes
structured text — makes it a capable fixer. eslint-plugin-secure-coding's
no-hardcoded-credentials rule emits a finding that carries the CWE, CVSS,
compliance tags, and the exact fix. Feed that back to the assistant and it
remediates its own output. This is the agentic-CI loop: AI writes → linter
flags in machine-readable form → AI fixes.
TL;DR
- AI assistants introduce hardcoded secrets (CWE-798) at scale — bare demo keys, placeholder passwords, and config literals left in source.
-
no-hardcoded-credentials(ineslint-plugin-secure-coding) catches them and emits a structured, CWE-tagged finding an AI agent can parse and auto-fix. - The detector is two-mode (registered key prefixes fire anywhere; generic secrets need a credential-named identifier) so it's quiet enough to run as a CI error. Full mechanism in the secure-coding deep-dive.
Why the lint error is written for the machine
A human reads error: hardcoded credential and sighs. An AI agent reads the
structure and acts. Run npx eslint . and a finding looks like this:
src/payments.ts
4:36 error 🔒 CWE-798 OWASP:A04-Cryptographic CVSS:9.8 | Hard-coded API key detected | CRITICAL [SOC2,PCI-DSS,HIPAA,GDPR]
Fix: Use environment variable: process.env.STRIPE_SECRET_KEY or secret management service
Every token is a machine signal:
-
CWE-798— a stable, machine-readable vulnerability class the model has seen thousands of times in training; it knows the remediation pattern. -
CVSS:9.8+CRITICAL— lets an agent prioritize this over a style nit. -
[SOC2,PCI-DSS,HIPAA,GDPR]— the compliance frameworks the finding maps to, for an audit trail the agent can cite. -
Fix:— the exact transformation (→ process.env.…), so the edit is deterministic, not a guess.
Drop that into Cursor/Copilot/Claude (or an autonomous CI agent) and the fix is
mechanical: hoist the literal to an environment variable or a secret manager.
The rule turns a vague "be secure" instruction into a closed, verifiable loop.
The fix the rule wants
// ✅ no literal in source; the secret comes from the environment
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
const db = new Pool({ password: process.env.DATABASE_PASSWORD });
// for higher assurance: fetch from AWS Secrets Manager / Vault at runtime
The rule flags the bare literal — new Stripe("sk_live_…"),
password: "changeme", const JWT_SECRET = "…" — and its suggested fix hoists
it to a process.env reference, the exact shape the agent should have produced.
(One deliberate nuance: a value that's already an env read with a literal
fallback — process.env.X || "dev-default" — is treated as already
remediated, since the real secret lives in the environment; that form is the
rule's accepted output, not a finding. So the thing it catches is the bare,
env-less literal.)
How it stays quiet enough to be an error
A naive secret scanner drowns you in false positives, which trains everyone
(human and agent) to ignore it. no-hardcoded-credentials makes two
different decisions: registered vendor key prefixes (sk_live_, AKIA…)
fire anywhere because they're unambiguous, while a generic high-entropy string
is only flagged when the surrounding identifier names a credential
(apiKey, password, token) and clears a length floor. That low
false-positive rate is what lets you run it as a blocking CI error — and what
makes an agent trust the signal instead of suppressing it. The
secure-coding getting-started
walks the full two-mode mechanism and the other 26 rules in the plugin.
Install
# npm
npm install --save-dev eslint-plugin-secure-coding
# yarn
yarn add --dev eslint-plugin-secure-coding
# pnpm
pnpm add --save-dev eslint-plugin-secure-coding
# bun
bun add --dev eslint-plugin-secure-coding
// eslint.config.js — `configs` is a NAMED export
import { configs } from "eslint-plugin-secure-coding";
export default [configs.recommended];
Tune it for your repo (e.g. allow fixtures in tests):
import { configs } from "eslint-plugin-secure-coding";
export default [
configs.recommended,
{
rules: {
"secure-coding/no-hardcoded-credentials": [
"error",
{ allowInTests: true },
],
},
},
];
Compatibility
| Surface | Support |
|---|---|
| Package managers | npm, yarn, pnpm, bun |
| Node | >= 18.0.0 |
| ESLint | `^8.0.0 \ |
| Module system | CommonJS — {% raw %}eslint.config.js or .mjs
|
| AI assistants | the CWE/CVSS/compliance/fix message is plain text in the lint output — consumable by Cursor, Copilot, Claude Code, or an autonomous CI agent with no extra integration |
Honest scope
-
It catches the literal in source, not key validity. It flags
sk_live_…; it can't tell a revoked test key from a live one. Rotate anything that was ever committed. -
Auto-fix needs a human gate for the secret value. The agent can hoist the
literal to
process.env.Xdeterministically, but where the real secret lives (env, Secrets Manager, Vault) is an architectural decision — the rule points at it, you choose it. - One rule, not a secret-scanning platform. Pair it with a history/secret scanner (commits already pushed) and rotation; this is the pre-merge gate that stops new ones — including the ones your AI just wrote.
Where this sits
This is one rule in eslint-plugin-secure-coding (27 framework-agnostic
"pure coding security" rules; see the
full getting-started).
It's part of the Interlace ecosystem —
domain-specific static analysis whose findings are deliberately structured for
both humans and the agents now writing most of the code.
⭐ Star on GitHub if your AI assistant has ever left a "your-secret-key" literal in your source.
I'm Ofri Peretz, a security engineering leader and the author of the
Interlace ESLint ecosystem — domain-specific static analysis for security,
reliability, and performance on the Node.js stack, with findings structured for
the AI agents now writing the code.
Top comments (0)