"How do you address the OWASP Top 10?" is now a line item on every enterprise
security questionnaire. The honest answer is more useful than a "100% covered"
checkbox — because static analysis genuinely catches 8 of the 10 web
categories at the source, and the other 2 are not source patterns at all.
Knowing which is which is the difference between a control you can audit and a
compliance-theater slide.
No single plugin covers it. SQL injection needs database-aware rules; JWT
attacks need token-aware rules; DOM XSS needs browser-aware rules. So the map
below spans ten domain-security plugins (part of the 19-plugin
Interlace ecosystem). Every rule carries a
CWE, and most findings carry the classic OWASP category the CWE rolls up to, so
the evidence is greppable, not hand-waved.
This is the web OWASP Top 10 (2021). The AI/LLM list is mapped separately
in the OWASP LLM Top 10 piece —
also honestly, 8 of 10.
The map: OWASP Top 10 (2021) → plugins → rules
| # | Category | Plugins | Representative rules |
|---|---|---|---|
| A01 | Broken Access Control |
secure-coding, nestjs-security, lambda-security
|
no-missing-authentication, require-guards, no-missing-authorization-check
|
| A02 | Cryptographic Failures |
node-security, jwt
|
no-weak-hash-algorithm, no-ecb-mode, no-weak-secret
|
| A03 | Injection |
pg, mongodb-security, secure-coding, browser-security
|
no-unsafe-query, no-operator-injection, no-innerhtml
|
| A04 | Insecure Design (partial) |
secure-coding, nestjs-security
|
require-secure-defaults, no-missing-validation-pipe
|
| A05 | Security Misconfiguration |
express-security, browser-security, pg
|
require-helmet, require-csp-headers, no-unsafe-search-path
|
| A06 | Vulnerable Components (partial) | node-security |
detect-suspicious-dependencies, require-dependency-integrity, lock-file
|
| A07 | Authentication Failures |
jwt, secure-coding, express-security
|
no-algorithm-none, no-algorithm-confusion, no-insecure-cookie-options
|
| A08 | Data Integrity Failures |
secure-coding, node-security
|
no-unsafe-deserialization, no-zip-slip, no-unsafe-dynamic-require
|
| A09 | Logging Failures |
secure-coding, lambda-security
|
no-pii-in-logs, no-env-logging, no-error-swallowing
|
| A10 | SSRF |
node-security, lambda-security, browser-security
|
no-ssrf, no-user-controlled-requests, require-url-validation
|
Each row lists representative rules, not the whole set — A03 alone spans SQL,
NoSQL, LDAP, XPath, and DOM injection across the four plugins above, plus
command/eval (node-security) and prompt injection (the AI layer) elsewhere in
the ecosystem.
A04 and A06: where source analysis hands off to another control
This is where "100% OWASP coverage" decks lie. Two of the ten are not source
patterns at a call site, so no source linter — this one included — fully
covers them. Naming the right control instead of faking a rule is what a
security reviewer actually wants:
-
A04 Insecure Design — a missing rate limit on a money-moving endpoint, a
trust boundary in the wrong place, a workflow that can be replayed. That's an
architectural problem. A few rules nudge toward safe defaults
(
require-secure-defaults,no-missing-validation-pipe), but the real control is threat modeling and design review, not a linter. -
A06 Vulnerable & Outdated Components — a transitive dependency with a
published CVE. That's a Software Composition Analysis problem.
node-securitycovers the source-hygiene slice — suspicious install scripts (detect-suspicious-dependencies), integrity/lockfile drift (require-dependency-integrity,lock-file) — but the CVE graph itself belongs tonpm audit, Dependabot, or Snyk. Use both; they answer different questions.
Anyone selling you "automated 100% OWASP" is mapping a defaults rule to
"Insecure Design" and hoping you don't open the OWASP page. You should.
What a finding looks like
Findings are deterministic strings — each carries the CWE, the OWASP category
the CWE rolls up to, a CVSS, the severity, and the compliance tags, then the
fix:
src/db/tenants.ts
8:15 error 🔒 CWE-426 OWASP:A05-Security CVSS:7.5 | Unsafe "SET search_path" detected. | CRITICAL [SOC2,PCI-DSS]
Fix: Do not use dynamic values for search_path. Use static strings or strict validation.
src/app/chat/route.ts
6:11 error 🔒 CWE-74 OWASP:A03-Injection CVSS:9 | User input "userMessage" passed directly to generateText prompt without validation | CRITICAL [SOC2,GDPR]
Fix: Validate input before use: generateText({ prompt: validateInput(userInput) })
The inline OWASP:Axx tag is the classic web category the rule's CWE maps to
(CWE-74 → A03 Injection). Because it's a stable token, you can turn a lint run
into audit evidence — a coverage count per OWASP category:
// npx eslint . --format json > security-report.json
const report = require("./security-report.json");
const byCategory = report
.flatMap((file) => file.messages)
.map((m) => m.message.match(/OWASP:(A\d+)/)?.[1])
.filter(Boolean)
.reduce((acc, cat) => ((acc[cat] = (acc[cat] || 0) + 1), acc), {});
console.log("OWASP findings by category:", byCategory);
// → { A03: 12, A05: 4, A02: 2, ... }
That JSON is the artifact you hand the auditor — not a slide.
Build your config, layer by layer
Don't install everything. Start with the core, then add the plugins that match
your stack. configs is a named export on every plugin (the default export
is the plugin object):
# core + the specialized layers you actually run — pick your manager
npm install --save-dev eslint-plugin-secure-coding eslint-plugin-node-security eslint-plugin-jwt eslint-plugin-pg
yarn add -D eslint-plugin-secure-coding eslint-plugin-node-security eslint-plugin-jwt eslint-plugin-pg
pnpm add -D eslint-plugin-secure-coding eslint-plugin-node-security eslint-plugin-jwt eslint-plugin-pg
bun add -d eslint-plugin-secure-coding eslint-plugin-node-security eslint-plugin-jwt eslint-plugin-pg
// eslint.config.js — flat config
import { configs as secureCoding } from "eslint-plugin-secure-coding";
import { configs as nodeSecurity } from "eslint-plugin-node-security";
import { configs as jwt } from "eslint-plugin-jwt";
import { configs as pg } from "eslint-plugin-pg";
export default [
secureCoding.recommended, // A01/A03/A04/A08/A09 — general source patterns
nodeSecurity.recommended, // A02/A06/A08/A10 — crypto, supply-chain, SSRF
jwt.recommended, // A02/A07 — token & signature security
// scope database rules to where queries live
{ files: ["**/db/**", "**/repositories/**"], ...pg.recommended },
];
Name the file
eslint.config.mjsif yourpackage.jsonisn't
"type": "module"— theimportsyntax above needs ESM (the plugins
themselves are CommonJS and load fine either way via Node's CJS↔ESM interop).
Add eslint-plugin-browser-security (frontend, A03/A05/A07), then
eslint-plugin-express-security / eslint-plugin-nestjs-security /
eslint-plugin-lambda-security / eslint-plugin-mongodb-security as your
backend stack dictates. eslint-plugin-vercel-ai-security adds the LLM layer —
see its honest OWASP-LLM map.
eslint-plugin-cryptois deprecated — its weak-algorithm / insecure-random
rules were consolidated intoeslint-plugin-node-security. Install
node-security, not crypto.
# CI — fail the PR on any new OWASP-tagged finding
- run: npx eslint . --max-warnings 0
Compatibility
Every plugin in the map ships the same contract:
| Surface | Support |
|---|---|
| Package managers | npm, yarn, pnpm, bun — plain dev dependencies |
| Node | >= 18.0.0 |
| ESLint | `^8.0.0 \ |
| Module system | CommonJS — loads from {% raw %}eslint.config.js or .mjs
|
| Targets | AST-based — they read your source; the framework/driver peer is optional, never a runtime dep |
| Oxlint | flagship rules wired via the interlace-* ports with ESLint↔Oxlint parity gated in CI; full sets run ESLint-first |
Where this fits
This is the ecosystem-level OWASP view. Each plugin has a deep-dive that walks
its full rule set and the attacks behind them:
-
eslint-plugin-jwt— thealg:nonebypass and 12 more auth rules -
eslint-plugin-pg— SQL injection, connection leaks, the N+1 insert loop -
search_pathhijacking — the A05 attack most teams have never heard of - OWASP LLM Top 10 — the AI list, mapped just as honestly
Links
- 📦 npm: eslint-plugin-secure-coding (core) · node-security · jwt · pg
- 📖 Full rule docs (per-rule CWE + OWASP)
- 🔐 OWASP Top 10 (2021)
- 💻 Source on GitHub — the 19-plugin ecosystem
⭐ Star on GitHub if "how do you cover the OWASP Top 10?" has ever landed in your inbox.
I'm Ofri Peretz, a security engineering leader and the author of the
Interlace ESLint ecosystem — domain-specific static analysis for security,
reliability, and performance on the Node.js stack.
Top comments (0)