Interlace ESLint
ESLint Interlace

no-decode-without-verify

**Severity:** � High

Disallow trusting decoded JWT payload without signature verification

Severity: 🟠 High
CWE: CWE-345

Error Message Format

The rule provides LLM-optimized error messages (Compact 2-line format) with actionable security guidance:

🔒 CWE-345 OWASP:A08 CVSS:7.5 | Insufficient Verification of Data Authenticity detected | HIGH
   Fix: Review and apply the recommended fix | https://owasp.org/Top10/A08_2021/

Message Components

ComponentPurposeExample
Risk StandardsSecurity benchmarksCWE-345 OWASP:A08 CVSS:7.5
Issue DescriptionSpecific vulnerabilityInsufficient Verification of Data Authenticity detected
Severity & ComplianceImpact assessmentHIGH
Fix InstructionActionable remediationFollow the remediation steps below
Technical TruthOfficial referenceOWASP Top 10

Rule Details

This rule detects usage of jwt.decode() or the jwt-decode library. Decoded JWTs can be tampered with by attackers since decode() never verifies the signature.

Examples

❌ Incorrect

// jwt.decode() returns unverified data
const payload = jwt.decode(token);
const userId = jwt.decode(token).sub;

// jwt-decode library
import jwtDecode from 'jwt-decode';
const claims = jwtDecode(token);

✅ Correct

// jwt.verify() validates signature
const payload = jwt.verify(token, secret);
const { sub } = jwt.verify(token, secret, { algorithms: ['RS256'] });

// jose library with verification
import { jwtVerify } from 'jose';
const { payload } = await jwtVerify(token, key);

Options

{
  "jwt/no-decode-without-verify": ["error", {
    "allowHeaderInspection": false,
    "trustedAnnotations": ["@decoded-header-only", "@verified-separately"]
  }]
}

When Not To Use It

Use @decoded-header-only annotation when inspecting token headers before verification routing.

Known False Negatives

The following patterns are not detected due to static analysis limitations:

Manual Base64 Decoding

Why: The rule tracks jwt.decode() and jwt-decode imports, not raw Base64 parsing.

// ❌ NOT DETECTED - Direct base64 decode of JWT payload
const [header, payload, signature] = token.split('.');
const decoded = JSON.parse(Buffer.from(payload, 'base64url').toString());
// ^^ Attacker bypasses decode detection entirely

Mitigation: Code review for manual JWT parsing. Consider using no-restricted-syntax to ban base64 decode patterns.

Aliased Decode Function

Why: The rule matches specific function names; renamed imports are not tracked.

// ❌ NOT DETECTED - Aliased import
import { decode as parseToken } from 'jsonwebtoken';
const payload = parseToken(token); // 'parseToken' not recognized

Mitigation: Use consistent import naming. Add custom matchers via configuration.

Dynamic Method Access

Why: Computed property access cannot be resolved statically.

// ❌ NOT DETECTED - Dynamic method call
const method = 'decode';
jwt[method](token); // Method name unknown at lint time

Mitigation: Avoid dynamic property access on security-sensitive APIs.

Verified-Then-Decoded Pattern

Why: The rule cannot track control flow to determine if verify was called first.

// ❌ FALSE POSITIVE RISK - May flag despite prior verification
async function getPayload(token: string) {
  await jwt.verify(token, secret); // Verification here...
  return jwt.decode(token); // ...but decode flagged anyway
}

Mitigation: Use @verified-separately annotation for legitimate patterns.

Further Reading

On this page