DEV Community

Cover image for The False Positive Tax: a 1:1 TP:FP analysis of eslint-plugin-security
Ofri Peretz
Ofri Peretz

Posted on • Originally published at ofriperetz.dev

The False Positive Tax: a 1:1 TP:FP analysis of eslint-plugin-security

Skip to: Results Table | eslint-plugin-security | SonarJS | Microsoft SDL | Interlace | Methodology

This is the false-positive deep dive companion to I Benchmarked 17 ESLint Security Plugins. That overview ranks plugins by recall; this one drills into the FP code samples that drive alert fatigue.

TL;DR

I built a comprehensive benchmark with 40 vulnerable code patterns across 14 security categories and 38 safe patterns that should NOT trigger warnings. Then I ran six ESLint security plugins against them.

The Headline Numbers

Plugin download counts cited throughout this article are weekly figures snapshotted on 2026-02-08 from npm-stat.com.

Plugin Rules TP (Detections) FP (False Alarms) Precision Recall F1 Score ESLint 9
Interlace Ecosystem 201 40/40 0 100.0% 100.0% 100.0% ✅ Works
eslint-plugin-sonarjs 269 14/40 5 73.7% 35.0% 47.5% ✅ Works
eslint-plugin-security 13 11/40 11 50.0% 27.5% 34.4% ❌ Broken
eslint-plugin-security-node 22 7/40 4 63.6% 17.5% 27.4% ✅ Works
@microsoft/eslint-plugin-sdl 17 4/40 1 80.0% 10.0% 17.8% ✅ Works
eslint-plugin-no-unsanitized 2 2/40 1 66.7% 5.0% 9.3% ⚠️ Limited

eslint-plugin-security crashes on ESLint 9. Its results are from ESLint 8.57.0. All other plugins were tested on ESLint 9.39.2.

Key Findings:

  • eslint-plugin-security has a 1:1 true positive to false positive ratio — for every real issue it catches, it incorrectly flags a safe pattern
  • eslint-plugin-sonarjs has 269 rules but only detects 35% of vulnerabilities — most rules target code quality, not security
  • eslint-plugin-security-node (the "successor" to eslint-plugin-security) still misses 82.5% of vulnerabilities
  • The Interlace ecosystem achieved a perfect score: 40/40 detections with zero false positives

Why This Benchmark Matters

Security linters exist to catch vulnerabilities before they reach production. But two failure modes undermine this mission:

False Negatives (missed vulnerabilities) create a dangerous illusion of security. Your CI pipeline passes, your code looks "clean," but invisible vulnerabilities ship to production.

False Positives (incorrectly flagged safe code) create alert fatigue. Developers start ignoring warnings, disabling rules, or worse—bypassing security checks entirely.

The ideal security linter has high recall (catches most vulnerabilities) and high precision (doesn't cry wolf).


The Benchmark Suite

Vulnerable Patterns (40 cases across 14 categories)

Category Test Cases CWEs
SQL Injection 4 CWE-89
Command Injection 4 CWE-78
Path Traversal 4 CWE-22
Hardcoded Credentials 4 CWE-798
JWT Vulnerabilities 3 CWE-757, CWE-347
XSS / Code Execution 4 CWE-79, CWE-94
Prototype Pollution 3 CWE-1321
Insecure Randomness 2 CWE-330
Weak Cryptography 3 CWE-328, CWE-327
Timing Attacks 2 CWE-208
NoSQL Injection 2 CWE-943
SSRF 2 CWE-918
Open Redirect 1 CWE-601
ReDoS 2 CWE-1333

Safe Patterns (38 cases)

Secure implementations that should NOT trigger any warnings:

  • Parameterized SQL queries (Prisma, TypeORM, pg)
  • execFile with validated arguments
  • path.resolve with startsWith validation
  • Environment variables for credentials
  • JWT with explicit algorithm restriction
  • DOMPurify sanitization
  • Allowlist validation before object access
  • crypto.randomBytes for tokens
  • crypto.timingSafeEqual for comparisons
  • URL allowlists for SSRF prevention

The Results

Detection Summary

Vulnerable Code Detections (out of 40 patterns):

Interlace Ecosystem:         ████████████████████████████████████████  40 (100%)
eslint-plugin-sonarjs:       ██████████████░░░░░░░░░░░░░░░░░░░░░░░░░░  14 (35%)
eslint-plugin-security:      ███████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░  11 (27.5%)
eslint-plugin-security-node: ███████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░   7 (17.5%)
@microsoft/eslint-plugin-sdl:████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░   4 (10%)
eslint-plugin-no-unsanitized:██░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░   2 (5%)
Enter fullscreen mode Exit fullscreen mode

The Leaderboard

Rank Plugin Version Rules TP FP FN Precision Recall F1
🥇 Interlace Ecosystem 3.0.2 201 40 0 0 100.0% 100.0% 100.0%
🥈 eslint-plugin-sonarjs 3.0.6 269 14 5 26 73.7% 35.0% 47.5%
🥉 eslint-plugin-security† 2.1.1 13 11 11 29 50.0% 27.5% 34.4%
4 eslint-plugin-security-node 1.1.4 22 7 4 33 63.6% 17.5% 27.4%
5 @microsoft/eslint-plugin-sdl 1.1.0 17 4 1 36 80.0% 10.0% 17.8%
6 eslint-plugin-no-unsanitized 4.1.4 2 2 1 38 66.7% 5.0% 9.3%

† Tested on ESLint 8.57.0 — crashes on ESLint 9 with TypeError: context.getScope is not a function


Plugin Deep Dives

eslint-plugin-security: The Incumbent

Weekly Downloads: 1.5M+ | Rules: 13 | Last Updated: 2024 | ESLint 9: ❌ Broken

What It Detected (11 violations)

Rule Count Lines
detect-non-literal-fs-filename 4 106, 115, 124, 134
detect-child-process 2 64, 73
detect-object-injection 2 264, 276
detect-eval-with-expression 1 243
detect-unsafe-regex 1 432
detect-non-literal-regexp 1 441

What It Missed (29 patterns = 72.5% False Negative Rate)

Category Detected Missed
SQL Injection 0/4 ❌ All
Hardcoded Credentials 0/4 ❌ All
JWT Vulnerabilities 0/3 ❌ All
Weak Cryptography 0/3 ❌ All
NoSQL Injection 0/2 ❌ All
SSRF 0/2 ❌ All
Open Redirect 0/1 ❌ All
Timing Attacks 0/2 ❌ All
Command Injection 2/4 ❌ Template literals
Path Traversal 4/4 ✅ Good
XSS / eval 1/4 ❌ innerHTML, document.write
Prototype Pollution 2/3 ⚠️ Partial
ReDoS 2/2 ✅ Good

The plugin has ZERO coverage for: SQL injection, hardcoded credentials, JWT attacks, weak crypto, NoSQL injection, SSRF, open redirects, and timing attacks.

The False Positive Problem (11 FPs = 100% of detections!)

For every vulnerability eslint-plugin-security catches, it also incorrectly flags a safe pattern:

FP #1-8: detect-object-injection (8 false positives)

// ✅ SAFE: Key validated against allowlist
const VALID_KEYS = ["name", "email", "age"];
if (VALID_KEYS.includes(key)) {
  return obj[key]; // ⚠️ Flagged as "Generic Object Injection Sink"
}
Enter fullscreen mode Exit fullscreen mode

The rule flags any bracket notation with a variable, regardless of validation. It cannot recognize allowlist checks, hasOwnProperty guards, or Object.hasOwn() checks.

FP #9-11: detect-non-literal-fs-filename (3 false positives)

// ✅ SAFE: Path validated with startsWith
const safePath = path.resolve(baseDir, path.basename(filename));
if (!safePath.startsWith(baseDir + path.sep)) {
  throw new Error("Path traversal detected");
}
fs.readFileSync(safePath); // ⚠️ Flagged anyway
Enter fullscreen mode Exit fullscreen mode

The rule cannot recognize path validation patterns.

ESLint 9 Compatibility: ❌ BROKEN

TypeError: context.getScope is not a function
Rule: "security/detect-child-process"
Enter fullscreen mode Exit fullscreen mode

This is a breaking API change in ESLint 9. The plugin hasn't been updated, making it unusable with modern ESLint flat config.


eslint-plugin-sonarjs: The 269-Rule Giant

Weekly Downloads: 3M+ | Rules: 269 | Last Updated: 2025 (active) | ESLint 9: ✅ Works

Detection Results: 14/40 (35% Recall)

Despite having the most rules of any plugin tested, SonarJS missed 65% of vulnerabilities. The majority of its 269 rules target code quality (complexity, duplication, cognitive load), not security.

Category SonarJS What It Missed
SQL Injection 2/4 Template literal patterns
Command Injection 2/4 execSync, spawn with shell
XSS 2/4 document.write, new Function
Hardcoded Credentials 2/4 AWS keys, JWT secrets
Prototype Pollution 2/3 Nested merge patterns
Weak Crypto 2/3 MD4, custom hash selection
ReDoS 1/2 Complex catastrophic patterns
Path Traversal 0/4 ❌ All
JWT 0/3 ❌ All
Timing Attacks 0/2 ❌ All
NoSQL Injection 0/2 ❌ All
SSRF 0/2 ❌ All
Open Redirect 0/1 ❌ All
Insecure Random 1/2 Token generation patterns

False Positives: 5

SonarJS had a 73.7% precision rate — better than eslint-plugin-security, but still means roughly 1 in 4 security warnings is noise.

📖 Deep dive: SonarJS vs Interlace: 269 Rules, 65% Missed


eslint-plugin-security-node: The Successor

Weekly Downloads: ~30K | Rules: 22 | Last Updated: 2023 | ESLint 9: ✅ Works

Created as a modern alternative to eslint-plugin-security, this plugin adds SQL injection and NoSQL injection detection rules that the original lacks. However, it still misses the majority of our test suite.

Detection Results: 7/40 (17.5% Recall)

Category security-node What It Caught
SQL Injection 2/4 Basic concatenation patterns
Command Injection 2/4 exec with string interpolation
XSS / eval 1/4 eval with expression
NoSQL Injection 1/2 Direct $where usage
Timing Attacks 1/2 Basic === comparison on secrets
Path Traversal 0/4 ❌ All
Hardcoded Credentials 0/4 ❌ All
JWT 0/3 ❌ All
Weak Crypto 0/3 ❌ All
SSRF 0/2 ❌ All

False Positives: 4

A 63.6% precision rate — better than eslint-plugin-security's 50%, but still noisy.


@microsoft/eslint-plugin-sdl: Enterprise Security

Weekly Downloads: ~100K | Rules: 17 | Last Updated: 2024 (active) | ESLint 9: ✅ Works

Microsoft's Security Development Lifecycle plugin has the highest precision of any non-Interlace plugin (80%), but its scope is extremely narrow — focused almost entirely on browser-side injection patterns.

Detection Results: 4/40 (10% Recall)

Category Microsoft SDL What It Caught
XSS 2/4 innerHTML, document.write
Code Execution 2/4 eval, setTimeout with exprs
Everything else 0/32 ❌ All

False Positives: 1 (Microsoft SDL)

High precision, but extremely limited coverage. Its 17 rules focus narrowly on XSS patterns — it has zero rules for SQL injection, command injection, path traversal, JWT attacks, or any server-side vulnerability.

📖 Deep dive: Microsoft SDL vs Interlace: Enterprise Security Benchmark


eslint-plugin-no-unsanitized (Mozilla)

Weekly Downloads: ~500K | Rules: 2 | Focus: XSS via DOM manipulation | ESLint 9: ⚠️ Limited

Detection Results: 2/40

Rule Count What It Caught
no-unsanitized/property 1 innerHTML = userContent
no-unsanitized/method 1 insertAdjacentHTML

False Positives: 1 (no-unsanitized)

// ✅ SAFE: Content sanitized with DOMPurify
const sanitized = DOMPurify.sanitize(userContent);
element.innerHTML = sanitized; // ⚠️ Flagged anyway
Enter fullscreen mode Exit fullscreen mode

Very narrow scope. Useful as a supplement for XSS, but covers only 2 of 14 categories.


Interlace Ecosystem

Weekly Downloads: ~5K | Rules: 201 (10 specialized plugins) | ESLint 9: ✅ Works

Detection Results: 40/40 (100% Recall, 0 False Positives)

The Interlace ecosystem achieved a perfect score — detecting every vulnerability with zero false positives across all 14 categories.

Sample detections:

🔒 CWE-798 OWASP:A04-Cryptographic CVSS:9.8 | Hard-coded API key detected | CRITICAL
   Fix: Use environment variable: process.env.API_KEY

🔒 CWE-347 | Including "none" in algorithms array allows unsigned tokens | CRITICAL
   Fix: Remove "none" from the algorithms array

🔒 CWE-95 OWASP:A05-Injection CVSS:9.8 | eval() can be refactored to safer alternative | HIGH
   Fix: Remove eval entirely
Enter fullscreen mode Exit fullscreen mode

The reason for 100% coverage is specialization. Instead of one monolithic plugin, the ecosystem uses purpose-built plugins for each domain: SQL (eslint-plugin-pg), JWT (eslint-plugin-jwt), browser XSS (eslint-plugin-browser-security), and weak crypto / randomness (consolidated into eslint-plugin-node-security on 2026-05-10), and more.


Category-by-Category Breakdown

Category security† security-node sonarjs MS SDL no-unsanitized Interlace
SQL Injection (4) ❌ 0/4 ⚠️ 2/4 ⚠️ 2/4 ❌ 0/4 ❌ 0/4 ✅ 4/4
Command Injection (4) ⚠️ 2/4 ⚠️ 2/4 ⚠️ 2/4 ❌ 0/4 ❌ 0/4 ✅ 4/4
Path Traversal (4) ✅ 4/4 ❌ 0/4 ❌ 0/4 ❌ 0/4 ❌ 0/4 ✅ 4/4
Hardcoded Creds (4) ❌ 0/4 ❌ 0/4 ⚠️ 2/4 ❌ 0/4 ❌ 0/4 ✅ 4/4
JWT (3) ❌ 0/3 ❌ 0/3 ❌ 0/3 ❌ 0/3 ❌ 0/3 ✅ 3/3
XSS / eval (4) ⚠️ 1/4 ⚠️ 1/4 ⚠️ 2/4 ⚠️ 2/4 ⚠️ 2/4 ✅ 4/4
Prototype Poll. (3) ⚠️ 2/3 ❌ 0/3 ⚠️ 2/3 ❌ 0/3 ❌ 0/3 ✅ 3/3
Insecure Random (2) ❌ 0/2 ❌ 0/2 ⚠️ 1/2 ❌ 0/2 ❌ 0/2 ✅ 2/2
Weak Crypto (3) ❌ 0/3 ❌ 0/3 ⚠️ 2/3 ❌ 0/3 ❌ 0/3 ✅ 3/3
Timing Attacks (2) ❌ 0/2 ⚠️ 1/2 ❌ 0/2 ❌ 0/2 ❌ 0/2 ✅ 2/2
NoSQL Injection (2) ❌ 0/2 ⚠️ 1/2 ❌ 0/2 ❌ 0/2 ❌ 0/2 ✅ 2/2
SSRF (2) ❌ 0/2 ❌ 0/2 ❌ 0/2 ❌ 0/2 ❌ 0/2 ✅ 2/2
Open Redirect (1) ❌ 0/1 ❌ 0/1 ❌ 0/1 ❌ 0/1 ❌ 0/1 ✅ 1/1
ReDoS (2) ✅ 2/2 ❌ 0/2 ⚠️ 1/2 ❌ 0/2 ❌ 0/2 ✅ 2/2
TOTAL 11/40 7/40 14/40 4/40 2/40 40/40

† ESLint 8 results (crashes on ESLint 9)


What This Means for Your Team

The Math of Missing Vulnerabilities

If your codebase has 100 potentially vulnerable patterns:

Plugin Detected Missed In Production
eslint-plugin-security 28 72 72 vulnerabilities
eslint-plugin-sonarjs 35 65 65 vulnerabilities
eslint-plugin-security-node 18 82 82 vulnerabilities
@microsoft/eslint-plugin-sdl 10 90 90 vulnerabilities
Interlace Ecosystem 100 0 0 vulnerabilities

The Alert Fatigue Cycle

When false positive rates are too high:

  1. Developer sees detect-object-injection on config[key] where key is validated
  2. Developer adds // eslint-disable-next-line
  3. Repeat 50 times across codebase
  4. Developer starts ignoring all security warnings
  5. Real vulnerability slips through disabled rule
  6. Breach
Plugin FP Rate Developer Impact
eslint-plugin-security 50.0% Every other warning is wrong
eslint-plugin-sonarjs 26.3% 1 in 4 is noise
eslint-plugin-security-node 36.4% 1 in 3 is noise
@microsoft/eslint-plugin-sdl 20.0% Tolerable, but very limited
Interlace 0.0% Every warning is actionable

Methodology

Test Environment

Component Version
Node.js v20.19.5
ESLint 9.39.2 (8.57.0†)
Platform macOS (darwin/arm64)
Date February 8, 2026

† ESLint 8.57.0 used for eslint-plugin-security only (crashes on ESLint 9)

Fixture Design

All fixtures are:

  • Realistic: Patterns from actual codebases, not contrived examples
  • Reproducible: Published to GitHub with exact versions
  • Comprehensive: All OWASP Top 10 with detectable patterns

Reproducibility

Full bench setup (fixtures, scripts, methodology) is documented in the companion article: I Benchmarked 17 ESLint Security Plugins. The FP samples in this article come from the same suite:

git clone https://github.com/ofri-peretz/eslint-benchmark-suite
cd eslint-benchmark-suite
npm install
npm run benchmark:fn-fp

# ESLint 8 benchmark (eslint-plugin-security only — required because it crashes on ESLint 9)
cd benchmarks/fn-fp-comparison/eslint8-compat
npm install
npm run benchmark
Enter fullscreen mode Exit fullscreen mode

Every claim in this article can be independently verified.


Conclusions

  1. eslint-plugin-security is hard to recommend for ESLint 9 codebases. A 72.5% false negative rate and a 1:1 TP:FP ratio on ESLint 8, plus a hard crash on ESLint 9. Teams on flat-config are left with no signal at all.

  2. eslint-plugin-sonarjs is a quality tool, not a security tool. Despite 269 rules and 3M+ downloads, it misses 65% of security vulnerabilities. Its strength is code quality enforcement.

  3. eslint-plugin-security-node is broader but still partial. It covers more categories than its predecessor, but still misses 82.5% of vulnerabilities.

  4. @microsoft/eslint-plugin-sdl is high precision, low coverage. Strong for browser XSS, but provides zero server-side security coverage.

  5. The Interlace ecosystem delivers comprehensive coverage. 100% detection rate with zero false positives. Domain-specific plugins ensure deep coverage across all vulnerability categories.

  6. Security tooling requires active maintenance. The OWASP landscape evolves. Plugins from 2020 don't cover JWT algorithm confusion, AI prompt injection, or modern SSRF patterns.


Migrating Off eslint-plugin-security

Full migration steps (uninstall + install + config) are in the companion article's 60-second migration block. The short version:

npm uninstall eslint-plugin-security
npm install -D eslint-plugin-secure-coding eslint-plugin-node-security \
  eslint-plugin-browser-security \
  eslint-plugin-pg eslint-plugin-jwt eslint-plugin-mongodb-security
Enter fullscreen mode Exit fullscreen mode

Note: weak-crypto and randomness rules were consolidated into eslint-plugin-node-security on 2026-05-10. The previously separate eslint-plugin-crypto package is deprecated.


Related deep dives


Explore the Full Ecosystem

201 security rules. 10 specialized plugins. 100% OWASP Top 10 coverage.

The Interlace ESLint Ecosystem provides comprehensive security static analysis for modern Node.js applications.

📖 Documentation | ⭐ GitHub | 📦 NPM


Build Securely.

I'm Ofri Peretz, a Security Engineering Leader and the architect of the Interlace Ecosystem. I build static analysis standards that automate security and performance for Node.js fleets at scale.

ofriperetz.dev | LinkedIn | GitHub

Top comments (0)