no-hardcoded-credentials
Detects hardcoded passwords, API keys, tokens, and other sensitive credentials in source code
CWE: CWE-522
OWASP Mobile: M1: Improper Credential Usage
Detects hardcoded passwords, API keys, tokens, and other sensitive credentials in source code. This rule is part of eslint-plugin-secure-coding and provides LLM-optimized error messages that AI assistants can automatically fix.
💼 This rule errors by default in the recommended config.
Quick Summary
| Aspect | Details |
|---|---|
| CWE Reference | CWE-798 (Use of Hard-coded Credentials) |
| Severity | Critical (security vulnerability) |
| Auto-Fix | ✅ Yes (suggests environment variables or secret managers) |
| Category | Security |
| ESLint MCP | ✅ Optimized for ESLint MCP integration |
| Best For | All applications handling sensitive data, API integrations, database connections |
Value & investment case
Why this rule pays for itself. Framework:
cicd-impact/philosophy.md.
| Dimension | Value |
|---|---|
| CWE | CWE-798 — Use of Hard-coded Credentials |
| Feedback-loop tier | Editor / pre-commit (sub-second) — cheapest layer per the feedback-loop hierarchy |
| Defensive-layer leverage | ~10× cheaper than unit-test · ~1,000× cheaper than production rollback · 10,000+× cheaper than customer disclosure — secrets in code are the textbook long-tail disclosure event (cost-ratio anchors) |
| Niche relevance | Critical: fintech, healthtech, cybersecurity (mandatory disclosure on breach + regulatory penalty) · High: B2B SaaS, infra/devtools · Medium: B2C, marketplaces · Lower (still important): gaming |
| Investor-frame impact | Hardcoded credentials → no rotation possible → on detection, full breach disclosure cycle. IBM Cost of a Data Breach 2024: median credentials-related breach $4.5M; healthcare-specific $9.8M. One catch at lint-time prevents the entire cycle. |
Read also: philosophy.md §investor-frame · niche-presets.json · analyzer-evaluation-framework.md
Vulnerability and Risk
Vulnerability: Embedding sensitive credentials (like passwords, API keys, or database connection strings) directly in the source code.
Risk: This leads to credential exposure in version control systems, making them accessible to any developer with repository access or attackers if the code is leaked. It also makes credential rotation difficult and error-prone.
Error Message Format
The rule provides LLM-optimized error messages (Compact 2-line format) with actionable security guidance:
🔒 CWE-798 OWASP:A04 CVSS:9.8 | Hardcoded Credentials detected | CRITICAL [SOC2,PCI-DSS,HIPAA,GDPR,ISO27001,NIST-CSF]
Fix: Review and apply the recommended fix | https://owasp.org/Top10/A04_2021/Message Components
| Component | Purpose | Example |
|---|---|---|
| Risk Standards | Security benchmarks | CWE-798 OWASP:A04 CVSS:9.8 |
| Issue Description | Specific vulnerability | Hardcoded Credentials detected |
| Severity & Compliance | Impact assessment | CRITICAL [SOC2,PCI-DSS,HIPAA,GDPR,ISO27001,NIST-CSF] |
| Fix Instruction | Actionable remediation | Follow the remediation steps below |
| Technical Truth | Official reference | OWASP Top 10 |
Rule Details
Hardcoded credentials are one of the most common security vulnerabilities. This rule detects passwords, API keys, tokens, and other sensitive values that are directly embedded in source code, which can be exposed in version control systems.
Why This Matters
| Issue | Impact | Solution |
|---|---|---|
| 🔒 Security | Credentials exposed in git history | Use environment variables |
| 🐛 Data Breach | API keys can be stolen from code | Secret management services |
| 🔐 Access Control | Passwords visible to all developers | AWS Secrets Manager, Vault |
| 📊 Compliance | Violates security best practices | CI/CD secret injection |
Detection Patterns
The rule decides on the VALUE's shape, never on the key name alone. A
credential-shaped name (password, apiKey, secret) is necessary-but-not-
sufficient: it can promote an ambiguous value, but it can never turn a message
constant into a finding.
That distinction is the whole rule. Name-driven matching reported
errors: { password: 'incorrectPassword' } — an i18n error key — at CVSS 9.8,
and on a 1,470-file corpus (webpack, lodash, eslint-plugin-import, two NestJS
boilerplates) that single pattern was 5 of 10 findings. The genuinely committed
50-character API secret in the same corpus was found by shape.
Tier 1 — structural, reported on shape alone
- Prefixed API keys: Stripe (
sk_live_…,pk_test_…), GitHub OAuth (ghp_,gho_,ghu_,ghs_,ghr_), AWS (AKIA…) - JWTs:
eyJ…with three dot-separated base64 parts - Database connection strings:
protocol://user:pass@host - Random blobs: 32+ contiguous alphanumeric characters, mixed case, with
digits, Shannon entropy ≥ 3.5 bits/char, and no ascending character run.
The charset is strict — punctuation rules a value out, because generated-code
strings (
installedChunkData[1](error);), comma-separated keyword lists and Postgres constraint names (PK_b36bcfe02fc8de3c57a8b2391c2) all carry punctuation that no API key does. The ascending-run check excludes charset constants such as'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', which are maximally high-entropy and the exact opposite of a secret.
Tier 2 — shape AND a credential-named slot
- Random blobs of 20–31 characters, e.g.
{ key: 'fyFGb7ywyM37TqDY8nuhAmGW5' }. Shape alone is not enough at this length:CreateUser1715028537217, a TypeORM migration class name, passes every shape test there is. - Long base64 / hex strings (32+), which also appear as hashes and IDs
- Common weak passwords (
password,admin,123456) - Any secret-shaped value in a credential-named slot: at least two character
classes (or a 20+ high-entropy single-charset blob), no whitespace, and not a
"natural word string". That last test is what rejects
incorrectPassword,SessionCacheProviderandexperimental_onToolExecutionStart— strings made only of pronounceable, dictionary-shaped tokens joined by camelCase or_,-and.separators, with no digits and no symbols.aaAA@123has four character classes and is reported;Please enter your passwordhas whitespace and is not.
key / keys are treated as weak names — they label cache keys, map keys and
i18n keys far more often than API keys — so they only count as credential
context when the value is already a random blob by shape.
Examples
❌ Incorrect
// Hardcoded API key
const apiKey = 'sk_live_FAKE_LIVE_KEY_FOR_TESTING_PURPOSES_ONLY_1234567890';
// Hardcoded password
const password = 'admin123';
// Hardcoded JWT token
const token =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';
// Database connection string with credentials
const dbUrl = 'mysql://user:password@localhost:3306/dbname';
// AWS access key
const awsKey = 'AKIAIOSFODNN7EXAMPLE';✅ Correct
// Environment variable
const apiKey = process.env.API_KEY;
// Secret manager
const password = await getSecret('database-password');
// Configuration service
const token = configService.get('JWT_TOKEN');
// Environment variable for database
const dbUrl = process.env.DATABASE_URL;
// AWS SDK with IAM roles (no keys needed)
const s3 = new AWS.S3(); // Uses IAM role✅ Also correct — message constants in credential-named slots
These are the false positives the shape gate exists to prevent. The key is
named password; the value is an i18n key, a label, or a sentence.
throw new UnprocessableEntityException({
errors: { password: 'incorrectPassword' }, // ✅ i18n error key, not a secret
});
const errors = { token: 'notFoundToken', secret: 'missingSecret' }; // ✅
const password = 'Please enter your password'; // ✅ sentence
export const SessionCacheProvider = 'SessionCacheProvider'; // ✅ DI token
const secret = 'experimental_onToolExecutionStart'; // ✅ identifier✅ Also correct — self-evident placeholders
A value the developer is visibly expected to replace is not a leaked
credential. Skipped by default; set allowPlaceholders: false to report them.
const TEST_CREDENTIALS = {
apiKey: 'test-api-key',
token: 'xxxxxxxxxxxx', // ✅ one character repeated
password: 'changeme', // ✅ placeholder word
secret: '<your-secret-here>', // ✅ bracketed template slot
};
const key = '{{API_SECRET}}'; // ✅ also `${…}` and `[…]`The allowlist applies only to non-structural findings. A JWT, an sk_live_
key, or a postgres://user:pass@host string keeps its shape whatever words it
contains, so those still report.
Configuration
{
rules: {
'secure-coding/no-hardcoded-credentials': ['error', {
ignorePatterns: ['^test-'], // Ignore test credentials
allowInTests: true, // Skip .test./.spec./__tests__ paths
minLength: 8, // Minimum credential length
detectApiKeys: true, // Detect API keys
detectPasswords: true, // Detect passwords
detectTokens: true, // Detect tokens
detectDatabaseStrings: true, // Detect database strings
allowPlaceholders: true // Skip <your-secret-here>, changeme, xxxxxxxx
}]
}
}Options
| Option | Type | Default | Description |
|---|---|---|---|
ignorePatterns | string[] | [] | Regex patterns to ignore |
allowInTests | boolean | true | Skip credentials in test files |
minLength | number | 8 | Minimum length for credential detection |
detectApiKeys | boolean | true | Detect API keys |
detectPasswords | boolean | true | Detect passwords |
detectTokens | boolean | true | Detect tokens |
detectDatabaseStrings | boolean | true | Detect database connection strings |
customPatterns | object[] | [] | Custom credential patterns to detect |
strategy | "env" | "config" | "vault" | "auto" | "auto" | Strategy for fixing hardcoded credentials (auto = smart detection) |
allowPlaceholders | boolean | true | Skip self-evident placeholder values (<your-secret-here>, changeme, xxxxxxxx) |
Ignoring Test Credentials
{
rules: {
'secure-coding/no-hardcoded-credentials': ['error', {
ignorePatterns: ['^test-', '^mock-', '^fake-']
}]
}
}Reporting Credentials in Test Files
Test-file credentials are skipped by default. A corpus scan found 17 of 18
findings on a real repository were fixtures in integration/auth.test.js, and
a credential in a fixture is not an exploitable finding for this rule —
committed real secrets are a secret-scanning concern (gitleaks, trufflehog),
which scan history and rotate keys. Set allowInTests: false to report them
anyway.
{
rules: {
'secure-coding/no-hardcoded-credentials': ['error', {
allowInTests: false // Report credentials in .test.ts and .spec.ts too
}]
}
}Rule Logic Flow
Best Practices
1. Use Environment Variables
// ✅ Good
const apiKey = process.env.STRIPE_API_KEY;
if (!apiKey) {
throw new Error('STRIPE_API_KEY is required');
}2. Use Secret Management Services
// ✅ Good - AWS Secrets Manager
import { SecretsManager } from '@aws-sdk/client-secrets-manager';
const client = new SecretsManager({ region: 'us-east-1' });
const secret = await client.getSecretValue({ SecretId: 'api-keys' });
const apiKey = JSON.parse(secret.SecretString).stripeKey;3. Use Configuration Services
// ✅ Good - Config service
import { ConfigService } from '@nestjs/config';
@Injectable()
export class ApiService {
constructor(private config: ConfigService) {}
getApiKey() {
return this.config.get<string>('API_KEY');
}
}4. Never Commit Credentials
echo "API_KEY=sk_live_FAKE_KEY_FOR_TESTING" >> .env
echo ".env" >> .gitignoreKnown False Negatives
The following patterns are not detected due to static analysis limitations:
Credentials from Config
Why: Config values not traced.
// ❌ NOT DETECTED - From config
const password = config.dbPassword;Mitigation: Use proper secrets management.
Environment Variables
Why: Env var content not analyzed.
// ❌ NOT DETECTED - Env var
const secret = process.env.API_KEY;Mitigation: Never hardcode or expose secrets.
Dynamic Credential Access
Why: Dynamic property access not traced.
// ❌ NOT DETECTED - Dynamic
const cred = credentials[type];Mitigation: Audit all credential access patterns.
Related Rules
no-sql-injection- Detects SQL injection vulnerabilitiesdatabase-injection- Comprehensive database securitydetect-eval-with-expression- Code injection detection
Resources
- CWE-798: Use of Hard-coded Credentials
- OWASP: Hardcoded Credentials
- 12 Factor App: Config
- AWS Secrets Manager
- HashiCorp Vault
Version History
- 1.3.0 - Initial release with comprehensive credential detection patterns
If this rule caught a real vulnerability in your codebase, ⭐ star the repo — it keeps the detection logic maintained.
Did this rule catch something? Star the repo to get new CWE coverage as we ship it — or follow the AI-code-security benchmarks behind these rules.