eslint-plugin-jwt
v2.0.3
Published
Security-focused ESLint plugin for JWT operations. Detects algorithm confusion (CVE-2022-23540), weak secrets, missing validation, and library-specific vulnerabilities across jsonwebtoken, jose, express-jwt, @nestjs/jwt, jwks-rsa, and jwt-decode.
Maintainers
Readme
eslint-plugin-jwt
🔐 Security-focused ESLint plugin for JWT authentication. Detects algorithm confusion (CVE-2022-23540), replay attacks, weak secrets, and library-specific vulnerabilities with AI-optimized fix guidance.
💡 What You Get
- 13 Security Rules - Algorithm attacks, replay prevention, claim validation
- 6 JWT Libraries - jsonwebtoken, jose, express-jwt, @nestjs/jwt, jwks-rsa, jwt-decode
- 2025 Research - "Back to the Future" replay attack prevention (LightSEC 2025)
- AI-Optimized - Structured messages for GitHub Copilot, Cursor, Claude assistance
- CWE References - Every rule maps to Common Weakness Enumeration
📦 Installation
npm install --save-dev eslint-plugin-jwt
# or
pnpm add -D eslint-plugin-jwt🚀 Quick Start
Flat Config (ESLint 9+)
// eslint.config.js
import jwt from 'eslint-plugin-jwt';
export default [
jwt.configs.recommended,
// or jwt.configs.strict for maximum security
];Custom Configuration
import jwt from 'eslint-plugin-jwt';
export default [
{
plugins: { jwt },
rules: {
// Critical - Algorithm attacks
'jwt/no-algorithm-none': 'error',
'jwt/no-algorithm-confusion': 'error',
// High - Verification and secrets
'jwt/require-algorithm-whitelist': 'warn',
'jwt/no-decode-without-verify': 'warn',
'jwt/no-weak-secret': 'error',
'jwt/no-hardcoded-secret': 'error',
// Medium - Best practices
'jwt/require-expiration': 'warn',
'jwt/require-issuer-validation': 'warn',
'jwt/require-audience-validation': 'warn',
},
},
];🔐 Rules
💼 = Set in recommended | 🔧 = Auto-fixable | 💡 = Has suggestions
Critical Severity (Algorithm Attacks)
| Rule | CWE | OWASP | Description | 💼 | 🔧 | 💡 |
| -------------------------------------------------------------- | ------- | -------- | -------------------------------------------- | --- | --- | --- |
| no-algorithm-none | CWE-347 | A02:2021 | Prevent alg:"none" attack (CVE-2022-23540) | 💼 | | 💡 |
| no-algorithm-confusion | CWE-347 | A02:2021 | Prevent RS256→HS256 key confusion | 💼 | | 💡 |
High Severity (Verification & Secrets)
| Rule | CWE | OWASP | Description | 💼 | 🔧 | 💡 |
| ------------------------------------------------------------------------ | ------- | -------- | ---------------------------------------- | --- | --- | --- |
| require-algorithm-whitelist | CWE-757 | A02:2021 | Require explicit algorithm specification | 💼 | | 💡 |
| no-decode-without-verify | CWE-345 | A04:2021 | Prevent trusting decoded payloads | 💼 | | 💡 |
| no-weak-secret | CWE-326 | A02:2021 | Require 256-bit minimum secrets | 💼 | | 💡 |
| no-hardcoded-secret | CWE-798 | A05:2021 | Prevent secrets in source code | 💼 | | 💡 |
| no-timestamp-manipulation | CWE-294 | A05:2021 | Prevent disabling automatic iat | 💼 | | 💡 |
Medium Severity (Claims & Best Practices)
| Rule | CWE | OWASP | Description | 💼 | 🔧 | 💡 |
| ------------------------------------------------------------------------ | ------- | -------- | ------------------------------------ | --- | --- | --- |
| require-expiration | CWE-613 | A04:2021 | Require exp claim or expiresIn | 💼 | | 💡 |
| require-issued-at | CWE-294 | A04:2021 | Require iat claim for freshness | 💼 | | 💡 |
| require-issuer-validation | CWE-287 | A01:2021 | Require issuer validation | 💼 | | 💡 |
| require-audience-validation | CWE-287 | A01:2021 | Require audience validation | 💼 | | 💡 |
| require-max-age | CWE-294 | A04:2021 | Require maxAge for replay prevention | 💼 | | 💡 |
| no-sensitive-payload | CWE-359 | A01:2021 | Prevent PII in token payload | 💼 | | 💡 |
🔐 OWASP Top 10 2021 Coverage
| OWASP Category | Rules | Coverage |
| -------------------------------------- | :--------------------------------------------------------------------------------------------: | :------: |
| A01:2021 Broken Access Control | require-audience-validation, require-issuer-validation | ✅ |
| A02:2021 Cryptographic Failures | no-algorithm-none, no-algorithm-confusion, no-weak-secret, require-algorithm-whitelist | ✅ |
| A04:2021 Insecure Design | no-decode-without-verify, require-expiration, require-max-age | ✅ |
| A05:2021 Security Misconfiguration | no-hardcoded-secret, no-timestamp-manipulation | ✅ |
| A07:2021 Identification Failures | require-issuer-validation, require-audience-validation | ✅ |
| A08:2021 Software/Data Integrity | no-algorithm-none, no-algorithm-confusion, no-decode-without-verify | ✅ |
CWE Coverage Summary
| CWE | Description | Rules |
| ------- | ---------------------------------------------- | ------------------------------------------------------------------- |
| CWE-287 | Improper Authentication | require-issuer-validation, require-audience-validation |
| CWE-294 | Authentication Bypass by Capture-Replay | require-issued-at, no-timestamp-manipulation, require-max-age |
| CWE-326 | Inadequate Encryption Strength | no-weak-secret |
| CWE-345 | Insufficient Verification of Data Authenticity | no-decode-without-verify |
| CWE-347 | Improper Cryptographic Signature Verification | no-algorithm-none, no-algorithm-confusion |
| CWE-359 | Exposure of Private Information | no-sensitive-payload |
| CWE-613 | Insufficient Session Expiration | require-expiration |
| CWE-757 | Selection of Less-Secure Algorithm | require-algorithm-whitelist |
| CWE-798 | Use of Hard-coded Credentials | no-hardcoded-secret |
🛡️ Security Research Coverage
CVE-2022-23540 (jsonwebtoken Algorithm None)
The no-algorithm-none rule detects attempts to use alg:"none" which bypasses signature verification entirely.
// ❌ Vulnerable - Accepts unsigned tokens
jwt.verify(token, secret, { algorithms: ['none'] });
// ✅ Safe - Explicit secure algorithm
jwt.verify(token, secret, { algorithms: ['RS256'] });LightSEC 2025 "Back to the Future" Attack
The no-timestamp-manipulation and require-max-age rules prevent replay attacks where tokens are captured and replayed years later.
// ❌ Vulnerable - Disables timestamp, enables replay
jwt.sign(payload, secret, { noTimestamp: true });
// ✅ Safe - Automatic iat, maxAge validation
jwt.sign(payload, secret, { expiresIn: '1h' });
jwt.verify(token, secret, { maxAge: '1h' });Algorithm Confusion Attack
The no-algorithm-confusion rule detects when symmetric algorithms (HS256) are used with asymmetric keys (public keys).
// ❌ Vulnerable - Public key as HMAC secret
jwt.verify(token, publicKey, { algorithms: ['HS256'] });
// ✅ Safe - Asymmetric algorithm with public key
jwt.verify(token, publicKey, { algorithms: ['RS256'] });⚙️ Configuration Presets
| Preset | Description | Rules |
| ------------- | -------------------------------- | ------------------------- |
| recommended | Balanced security | Critical=error, High=warn |
| strict | Maximum security (2025 research) | All 13 rules=error |
| legacy | Migration mode | Critical rules only |
📚 Supported Libraries
| Library | npm | Detection |
| ------------ | -------------------------------------------------- | --------- |
| jsonwebtoken | | ✅ Full |
| jose |
| ✅ Full |
| express-jwt |
| ✅ Full |
| @nestjs/jwt |
| ✅ Full |
| jwks-rsa |
| ✅ Full |
| jwt-decode |
| ✅ Full |
🤖 AI-Optimized Messages
Every rule uses formatLLMMessage for structured output:
🔒 CWE-347 OWASP:A02-Crypto CVSS:9.8 | Using alg:"none" bypasses signature verification
Fix: Remove "none" and use RS256, ES256, or other secure algorithms
https://nvd.nist.gov/vuln/detail/CVE-2022-23540📖 References
- RFC 8725 - JWT Best Current Practices
- CVE-2022-23540 - jsonwebtoken algorithm none
- CVE-2025-30204 - golang-jwt DoS
- LightSEC 2025 - "Back to the Future" Attack
- PortSwigger - JWT Algorithm Confusion
- OWASP JWT Security Cheat Sheet
🔗 Related ESLint Plugins
Part of the Interlace ESLint Ecosystem — AI-native security plugins with LLM-optimized error messages:
| Plugin | Downloads | Description | Rules |
| ---------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------: | ------------------------------------------------------------ | :---: |
| eslint-plugin-secure-coding | | Universal security (OWASP Top 10 Web + Mobile) | 89 |
|
eslint-plugin-crypto | | Cryptographic best practices (weak algorithms, key handling) | 24 |
|
eslint-plugin-pg | | PostgreSQL/node-postgres security | 13 |
|
eslint-plugin-express-security | | Express.js security (CORS, cookies, CSRF, helmet) | 8 |
|
eslint-plugin-nestjs-security | | NestJS security (guards, validation pipes, throttler) | 5 |
|
eslint-plugin-lambda-security | | AWS Lambda/Middy security (API Gateway, CORS, secrets) | 5 |
|
eslint-plugin-browser-security | | Browser security (XSS, postMessage, storage, cookies) | 21 |
|
eslint-plugin-vercel-ai-security | | Vercel AI SDK security (OWASP LLM + Agentic Top 10) | 19 |
|
eslint-plugin-import-next | | High-performance import linting | 12 |
📄 License
MIT © Ofri Peretz
