DEV Community

Cover image for 8 of the OWASP Top 10 Are ESLint Rules. 2 Aren't — and That's the Honest Audit Answer.
Ofri Peretz
Ofri Peretz

Posted on • Edited on • Originally published at ofriperetz.dev

8 of the OWASP Top 10 Are ESLint Rules. 2 Aren't — and That's the Honest Audit Answer.

"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-security covers the source-hygiene slice — suspicious install scripts (detect-suspicious-dependencies), integrity/lockfile drift (require-dependency-integrity, lock-file) — but the CVE graph itself belongs to npm 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) })
Enter fullscreen mode Exit fullscreen mode

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, ... }
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
// 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 },
];
Enter fullscreen mode Exit fullscreen mode

Name the file eslint.config.mjs if your package.json isn't
"type": "module" — the import syntax 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-crypto is deprecated — its weak-algorithm / insecure-random
rules were consolidated into eslint-plugin-node-security. Install
node-security, not crypto.

# CI — fail the PR on any new OWASP-tagged finding
- run: npx eslint . --max-warnings 0
Enter fullscreen mode Exit fullscreen mode

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:


Links

⭐ 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.

ofriperetz.dev · LinkedIn · GitHub

Top comments (0)