DEV Community

Cover image for I Inherited a 3,000-Line Codebase. One ESLint Run Found 26 Critical Security Bugs.
Ofri Peretz
Ofri Peretz

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

I Inherited a 3,000-Line Codebase. One ESLint Run Found 26 Critical Security Bugs.

You just inherited a codebase — an acquisition, a departing senior engineer, or
you're the new lead and nobody can explain the 3,000-line utils/legacy_auth.js.
The only question that matters on day one is: how bad is it?

A traditional audit takes weeks, a consultant, and a 200-page PDF you'll file and
forget. You don't have weeks. You have one ESLint run — and it returns a
measurable risk heatmap you can put in front of a board. Here's the exact
process, and the kind of result a representative inherited service produces:
26 critical-severity findings — 15 SQL injections, 8 hardcoded credentials,
3 broken hashes — before the first standup.

Step 1 — install the layers (2 min)

Three plugins cover the highest-yield server-side risks: injection, secrets, and
crypto.

# npm (yarn: yarn add -D … · pnpm: pnpm add -D … · bun: bun add -d …)
npm install --save-dev eslint-plugin-secure-coding eslint-plugin-pg eslint-plugin-node-security
Enter fullscreen mode Exit fullscreen mode

Step 2 — configure for maximum detection (3 min)

// eslint.config.mjs — `configs` is a NAMED export on every plugin
import { configs as secureCoding } from "eslint-plugin-secure-coding";
import { configs as pg } from "eslint-plugin-pg";
import { configs as nodeSecurity } from "eslint-plugin-node-security";

export default [
  secureCoding.strict, // the full secure-coding set, as errors — maximal for a scan
  pg.recommended,
  nodeSecurity.recommended,
];
Enter fullscreen mode Exit fullscreen mode

strict turns the whole secure-coding rule set on as errors — exactly what you
want for a first pass, where false positives are cheaper than missed risk.

Step 3 — run it to JSON (5 min)

npx eslint . --format=json > security-audit.json
Enter fullscreen mode Exit fullscreen mode

A finding carries the CWE, the OWASP category, a CVSS, the severity, and the
compliance tags — the audit evidence, in the message:

src/utils/crypto.js
  42:18  error  🔒 CWE-327 OWASP:A04-Cryptographic CVSS:7.5 | Use of weak hash algorithm: md5. md5 is cryptographically broken and unsuitable for security purposes. | CRITICAL [PCI-DSS,HIPAA,ISO27001,NIST-CSF]
               Fix: Replace with sha256: crypto.createHash("sha256").update(data)
Enter fullscreen mode Exit fullscreen mode

(The CLI also appends the rule's doc URL to the Fix: line; trimmed here.)

Step 4 — build the heatmap (20 min)

Rank the findings by rule. This one line is the whole heatmap:

jq -r '.[].messages[].ruleId' security-audit.json | sort | uniq -c | sort -rn
Enter fullscreen mode Exit fullscreen mode

A real run looks like this — and the frequency is the signal:

Count Rule Severity Reads as
15 pg/no-unsafe-query 🔴 Critical systemic SQL injection — no query layer
8 secure-coding/no-hardcoded-credentials 🔴 Critical secrets in source — rotate now
3 node-security/no-weak-hash-algorithm 🔴 Critical MD5/SHA1 in crypto paths

15 injections isn't 15 bugs — it's a team that never had a query layer. That's
the real finding.

What one run buys you

  • The attack surface — group by OWASP category to see what's most exposed: jq -r '.[].messages[].message' security-audit.json | grep -o 'OWASP:[^ ]*' | sort | uniq -c | sort -rn
  • The hotspots — group by file instead of rule to find the worst modules: jq -r '.[].filePath' security-audit.json | sort | uniq -c | sort -rn
  • The culture — did the previous team have any guardrails? The heatmap answers honestly.

It's not a penetration test. It's a data-driven first slide — and unlike the
consultant's PDF, you can re-run it weekly to measure remediation velocity.

Then make it permanent

# CI — the audit becomes a gate; errors fail the build, and --max-warnings 0
# also blocks any warning-level rule
- run: npx eslint . --max-warnings 0
Enter fullscreen mode Exit fullscreen mode

The same [PCI-DSS,HIPAA,ISO27001,…] tags in each finding become your audit
evidence, and the structured messages are built for AI assistants to action.


Compatibility

All three plugins ship the same contract:

Surface Support
Package managers npm, yarn, pnpm, bun
Node >= 18.0.0
ESLint `^8.0.0 \
Module system Plugins ship CommonJS; your config can be {% raw %}eslint.config.js or .mjs
Oxlint flagship rules wired via the interlace-* ports, parity-gated in CI

Links

⭐ Star on GitHub if you've ever inherited a codebase and had no idea how bad it was.


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 (6)

Collapse
 
sloan profile image
Sloan the DEV Moderator

We loved your post so we shared it on social.

Keep up the great work!

Collapse
 
ofri-peretz profile image
Ofri Peretz

Thanks for the share 🧡

Collapse
 
jankapunkt profile image
Jan Küster 🔥

Thanks, I used the security plugin and it prevented quite a few traps like prototype pollution. However at that time it did not report the owasp IDs and cwes.

Collapse
 
ofri-peretz profile image
Ofri Peretz

Hi @jankapunkt Appreciate the feedback, can you provide specific examples that you've experienced false negatives, so I will be able to improve the plugin/s?

Collapse
 
jankapunkt profile image
Jan Küster 🔥

@ofri-peretz sorry for the confusion. I used eslint-plugin-security in the past. The ones you propose look like a massive improvement!

Thread Thread
 
ofri-peretz profile image
Ofri Peretz

What nice feedback to receive! Feel free to share any type of feedback you have. I'm here to iterate on these plugins fast. If you have ideas for more useful rules, lmk.