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
| Component | Purpose | Example |
|---|---|---|
| Risk Standards | Security benchmarks | CWE-345 OWASP:A08 CVSS:7.5 |
| Issue Description | Specific vulnerability | Insufficient Verification of Data Authenticity detected |
| Severity & Compliance | Impact assessment | HIGH |
| Fix Instruction | Actionable remediation | Follow the remediation steps below |
| Technical Truth | Official reference | OWASP 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 entirelyMitigation: 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 recognizedMitigation: 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 timeMitigation: 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.