no-hardcoded-secret
**Severity:** � High
Disallow hardcoded secrets in JWT sign/verify operations
Severity: 🟠 High
CWE: CWE-798
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
This rule detects hardcoded secrets in source code. Secrets committed to repositories can be extracted by anyone with code access.
Examples
❌ Incorrect
// Hardcoded string
jwt.sign(payload, 'my-secret-key');
jwt.verify(token, 'my-secret-key');
// Template literal
jwt.sign(payload, `static-secret`);✅ Correct
// Environment variable
jwt.sign(payload, process.env.JWT_SECRET);
// Config object
jwt.sign(payload, config.jwtSecret);
// Function call
jwt.sign(payload, getSecretFromVault());Known False Negatives
The following patterns are not detected due to static analysis limitations:
Dynamic Secret Construction
Why: The rule checks for literal strings but cannot evaluate runtime string operations.
// ❌ NOT DETECTED - String built at runtime
const prefix = 'secret';
const suffix = '-key-123';
jwt.sign(payload, prefix + suffix); // Concatenation not statically resolvedMitigation: Use ESLint's prefer-template and code review. Consider runtime secret detection.
Encoded/Obfuscated Secrets
Why: The rule matches plain text literals, not decoded values.
// ❌ NOT DETECTED - Base64 encoded secret
const secret = Buffer.from('c2VjcmV0LWtleQ==', 'base64').toString();
jwt.sign(payload, secret);Mitigation: Use pre-commit hooks that scan for base64-encoded secrets. Consider tools like git-secrets.
Cross-Module Secret Passing
Why: ESLint analyzes one file at a time; imported values cannot be traced.
// ❌ NOT DETECTED - Secret imported from another file
// secrets.ts: export const JWT_SECRET = 'hardcoded-secret';
import { JWT_SECRET } from './secrets';
jwt.sign(payload, JWT_SECRET); // Value unknown at lint timeMitigation: Apply the rule to all files. Use secret scanning tools across the entire repository.
Object Property Secrets
Why: When identifier references are treated as safe (could be config), deeply nested literals are missed.
// ❌ NOT DETECTED - Nested in object created elsewhere
const config = createConfig(); // Returns { secret: 'hardcoded' }
jwt.sign(payload, config.secret); // Member expression treated as safeMitigation: Enable strictMode: true option to treat all non-env sources as suspicious.
Variable Re-assignment
Why: The rule checks the immediate argument, not variable history.
// ❌ NOT DETECTED - Indirect reference
let key = 'my-secret-key';
const actualKey = key;
jwt.sign(payload, actualKey); // Identifier treated as safeMitigation: Use const bindings and avoid variable reassignment for secrets.