Production got breached last month. The team was shocked—they'd been running ESLint, SonarQube, and Semgrep on every commit for three years. Clean builds. Green checkmarks everywhere.
The attack? SQL injection via a query builder pattern that shipped in the ORM update from 2024. Their static analysis config was from 2021.
Nobody updated the rules. Nobody even knew they needed to.
The Copy-Paste Trap
Most teams start their static analysis config the same way: copy someone else's. Maybe from a tutorial. Maybe from that "awesome-security-tools" repo with 40k stars. Looks comprehensive. Has comments explaining each rule.
Perfect.
Three years later, that config is a liability. The codebase evolved. Dependencies changed. Attack patterns shifted. But the .eslintrc.json sitting in the repo root? Untouched since the initial commit.
// .eslintrc.json from 2021
{
"rules": {
"no-eval": "error",
"no-implied-eval": "error"
// 47 more rules...
}
}
// What's missing in 2026:
// - Prototype pollution checks (CVE-2022-xxxx)
// - Server-side template injection patterns
// - New RegExp DoS vectors
// - Async race condition detection
The worst part? It still passes. Your CI shows green. The security dashboard says "0 critical issues." You're protected, right?
Wrong. You're just not detecting the things you should be looking for.
When "Disabled for Now" Becomes Forever
Seen this pattern before?
// TODO: fix these warnings before re-enabling
"@typescript-eslint/no-explicit-any": "off",
"security/detect-object-injection": "off",
"security/detect-non-literal-regexp": "off"
Someone hit a deadline. The linter was screaming about 200+ violations. Easiest fix? Disable the rules. "We'll clean this up in the next sprint."
That was eight months ago. The rule is still off. The team forgot why they disabled it. New developers join and assume those patterns are acceptable—the linter doesn't complain.
I've seen detect-object-injection disabled in a codebase that later shipped a prototype pollution bug. The scanner would've caught it. But the rule was off because it flagged 40 false positives in legacy code back in 2022.
Cost of fixing those false positives then: maybe 2 hours.
Cost of the breach: you can guess.
Dependency Updates Break Everything (So You Stop Updating)
You bump eslint-plugin-security from 1.4.0 to 2.0.0. Suddenly 73 new errors. Most are legitimate issues—new rules catching real problems you didn't know existed.
But you're two days from a release. Can't fix 73 issues now.
Rollback the plugin. Ship the release. Make a mental note to deal with it later.
Six months later, eslint-plugin-security is pinned to 1.4.0 in your package.json. Meanwhile, version 2.3.0 has detection for three CVEs that are actively being exploited in frameworks you use.
The plugin maintainers published those rules for a reason. Your codebase probably has the pattern. But you'll never know, because you're stuck on an old version to avoid "breaking" the build.
The Baseline Workaround That Doesn't Work
Some teams try to solve this with baselines: "Ignore all current issues, only flag new ones."
Sounds smart. In practice, it means you're shipping known vulnerabilities forever. The baseline freezes your technical debt in place. New code stays clean (maybe). But that 80,000-line service with six different SQL injection vectors? Still in production. Still exploitable. Just not "new" enough to fail the build.
Baselines are useful for migration. But if your baseline is two years old and you haven't chipped away at it, you're not migrating—you're just ignoring problems with extra steps.
Framework Shifts Leave Rules Obsolete
Your team moved from Express to Fastify last year. Or from React class components to hooks. Or from REST to GraphQL.
Did you update your static analysis config?
Probably not. Because nobody thinks to. The rules still run. They still pass. But they're checking for patterns that don't exist in your codebase anymore, while completely missing the new attack surface.
Example: You've got a Semgrep rule that detects unsafe res.send() usage in Express. Smart—prevents XSS from unescaped responses.
rules:
- id: express-xss-res-send
pattern: res.send($VAR)
message: Direct res.send with variable - potential XSS
severity: ERROR
Cool. But you migrated to Fastify six months ago. Now you're using reply.send(). The rule doesn't trigger. You've got the exact same XSS bug, just with a different API.
Zero alerts. Breach in three weeks when someone ships unsanitized user input to a new endpoint.
Multi-Tool Chaos and Coverage Gaps
Here's a fun one: You run ESLint for code quality, Semgrep for security, and SonarQube for... honestly, nobody remembers. Three different tools. Three different configs. Three different rule update cycles.
ESLint gets updated every few months because the frontend team cares about linting. Semgrep? Last updated 14 months ago by someone who left the company. SonarQube? Running on defaults from the Docker image you pulled in 2020.
Each tool thinks another one is covering certain checks. None of them actually are.
I've seen teams running both ESLint's security/detect-unsafe-regex and Semgrep's ReDoS rules. Sounds redundant, but one catches complexity-based DoS, the other catches backtracking explosions. If you disable one assuming the other has it covered, you just opened a gap.
Worse: nobody documented what each tool is responsible for. So when you add CodeQL to the pipeline (because GitHub said you should), you have four overlapping tools and no idea which rules to keep or disable.
The "We'll Fix It In Code Review" Delusion
Some teams straight-up disable noisy rules and rely on code review to catch issues.
Doesn't work. Humans miss stuff. Especially when the PR has 47 files changed and you're reviewing it at 4pm on a Friday before a deploy.
Static analysis is supposed to be the safety net under code review. When you disable the net because it occasionally catches non-issues, you're betting your security posture on caffeine levels and attention spans.
Bad bet.
How to Actually Keep Config in Sync
Stop treating your static analysis config like a set-it-and-forget-it firewall rule. It's a living thing. Needs maintenance.
1. Schedule Config Audits
Every quarter, actually look at your rules. Not just "does it pass," but:
- Are there new rules we should enable?
- Did our stack change? (new framework, new libraries, new patterns)
- Are we still ignoring things we shouldn't?
Put it on the calendar. Make someone responsible. Otherwise it won't happen.
2. Track Why Rules Are Disabled
If you turn off a rule, document why and when you'll re-enable it.
// Disabled 2024-03-12: 40 false positives in legacy auth code
// Re-evaluate after auth rewrite (Q3 2024)
"security/detect-object-injection": "off"
Make it uncomfortable to leave things off forever. If Q3 2024 is long gone and the rule is still disabled, either fix the underlying issue or admit you're never turning it back on.
3. Update Plugins With Dependencies
When you update your framework or major libraries, update your static analysis plugins at the same time. If you're bumping Next.js to a new major version, check if eslint-plugin-react or your Semgrep rules need updates too.
New framework version = new patterns = new potential issues. Your rules should evolve with your code.
4. Test Config Changes in Branches First
Don't merge a plugin upgrade that adds 100 new errors into main. Create a branch, see what breaks, fix or suppress the legitimate issues, then merge.
Gradual updates prevent the "too many errors, rollback everything" panic that leaves you stuck on old tooling forever.
Automated Security Scanning
Your static analysis setup might be outdated, over-tuned, or missing entire categories of issues. ScanMyCode.dev runs multiple up-to-date engines (Semgrep, CodeQL, custom rules) and cross-references findings to avoid gaps from configuration drift.
You get a report showing exactly what your current setup is missing, with severity ratings and fix guidance—no config archaeology required.
Stop Trusting Yesterday's Config
That clean CI build doesn't mean your code is secure. It means your code passes the rules you wrote two years ago for a codebase that doesn't exist anymore.
Frameworks evolve. Attack patterns evolve. Your static analysis config should too.
Otherwise you're just running very expensive theater. Green checkmarks that mean nothing. Run a current-gen security audit and see what you're actually missing.