Interlace ESLint
ESLint Interlace

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

ComponentPurposeExample
Risk StandardsSecurity benchmarksCWE-798 OWASP:A04 CVSS:9.8
Issue DescriptionSpecific vulnerabilityHardcoded Credentials detected
Severity & ComplianceImpact assessmentCRITICAL [SOC2,PCI-DSS,HIPAA,GDPR,ISO27001,NIST-CSF]
Fix InstructionActionable remediationFollow the remediation steps below
Technical TruthOfficial referenceOWASP 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 resolved

Mitigation: 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 time

Mitigation: 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 safe

Mitigation: 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 safe

Mitigation: Use const bindings and avoid variable reassignment for secrets.

Further Reading

On this page