Library / SDK
eslint-community/eslint-plugin-security avatar
eslint-community/eslint-plugin-security

eslint-plugin-security: Static Security Hotspots for Node.js Code

ESLint rules for Node Security

2,379 stars114 forksJavaScriptApache-2.0

At a glance

What is it?
eslint-plugin-security adds fifteen ESLint rules that flag risky Node.js patterns such as eval, non-literal require, and child_process use. It is a triage aid, not a vulnerability scanner, and it warns you about its own false positives.
Who is it for?
Adopt eslint-plugin-security if your team already runs ESLint 8.23 or later, works in CommonJS or TypeScript via @types/eslint-plugin-security, and can absorb the triage cost of a rule set the README itself says finds many false positives. Do not adopt it as a replacement for a SAST tool or dependency scanner, and do not expect it to reason about data flow: detect-object-injection flags any computed property access, which in a typical codebase means a large warning volume.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 3, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What eslint-plugin-security flags in a Node.js codebase

This is an ESLint plugin, so it runs inside the linter you already invoke, on the same AST, in the same pass. It does not instrument your application, follow tainted input, or scan dependencies. Its fifteen rules look for syntactic shapes that correlate with security bugs: eval() called with a variable, require() with a non-literal argument, fs calls whose filename is a variable, child_process usage, new Buffer(argument), RegExp(variable), pseudoRandomBytes(), comparisons written with == or === that may leak timing information, Express csrf middleware registered before method-override, object.escapeMarkup = false, and two rules aimed at source-level attacks, detect-bidi-characters and detect-invisible-characters, which catch trojan-source style unicode injection.

The intended audience is a Node.js team that wants a cheap, editor-integrated signal during code review. The README is explicit about the trade-off: the project "will help identify potential security hotspots, but finds a lot of false positives which need triage by a human." That sentence should shape how you deploy it. Treat the output as a list of places worth a second look, not as a list of confirmed defects.

How the rules are wired: AST patterns, not taint analysis

The plugin ships an index.js entry point, a rules/ directory with one module per rule, and a utils/ directory for shared helpers. Each rule exports the standard ESLint create(context) shape and inspects node types as the traversal reaches them. detect-non-literal-require, for example, only needs to see a CallExpression whose callee is require and whose first argument is not a Literal. detect-object-injection matches computed member access, variable[key], on either side of an assignment.

Because the checks are local, they are fast and predictable, and they are also blind to context. A computed property access on a frozen object built two lines earlier looks identical to one built from req.query. The one rule that does more than pattern matching is detect-unsafe-regex, which depends on the safe-regex package (^2.1.1 in dependencies) to score a regular expression for catastrophic backtracking potential. That is the only external runtime dependency the package declares.

Installing eslint-plugin-security and running it once

Install it as a development dependency. The README gives both package managers:

bash
npm install --save-dev eslint-plugin-security

The flat-config path requires ESLint 8.23.0 or newer. Add the recommended config to eslint.config.js:

js
const pluginSecurity = require('eslint-plugin-security');

module.exports = [pluginSecurity.configs.recommended];

After that, running your normal lint command surfaces the rules. The README also documents the older eslintrc form, marked deprecated, which extends plugin:security/recommended-legacy. If you write TypeScript, type definitions live in DefinitelyTyped rather than in this package:

bash
npm install --save-dev @types/eslint-plugin-security

You should expect a burst of warnings on first run in an existing codebase, concentrated in detect-object-injection and detect-non-literal-fs-filename. The rules table in the README marks every rule as set in the recommended configuration.

The false-positive problem is documented, not incidental

Most security lint rules are quiet by design and rely on data-flow engines to stay quiet. This plugin has no data-flow engine, so the false-positive rate is structural rather than a bug to be fixed in a later release. detect-object-injection is the clearest case: any obj[key] where key is not a literal is reported, whether key came from an HTTP request or from a constant map. In a codebase that uses computed access for configuration, i18n dictionaries, or Redux reducers, that rule alone can produce hundreds of warnings that are all benign.

The practical consequence is that enabling the recommended config as errors will stall CI on day one. Teams typically start with warnings, triage, and then disable the noisiest rules per directory. That is a legitimate use, but it means the plugin's value depends on someone actually reading the output. A team that wires it in and then adds an ignore comment to every hit has bought nothing. The detect-child-process rule has a similar shape: it flags the import of child_process and non-literal exec() calls, which is correct in a library and noise in a CLI tool whose entire purpose is spawning processes.

Where eslint-plugin-security is the wrong tool

It will not find injection flaws that require tracking a value across function boundaries, and it will not tell you whether a flagged sink is reachable from untrusted input. It does not read your dependency tree, so it says nothing about a vulnerable version of a package you import. It does not understand your framework's routing, so it cannot tell whether a handler is authenticated. And it has no notion of secrets, so hardcoded credentials pass through untouched.

If your threat model is primarily third-party supply chain, a dependency scanner is the right category and this plugin is not a substitute. If it is injection in a large Express application, a taint-tracking SAST tool covers ground this plugin cannot reach. The honest framing is that eslint-plugin-security catches a specific, narrow class of mistakes at the moment they are typed, at essentially zero runtime cost, and that is the whole of its value.

How it compares with other ESLint security plugins

The related searches around this project repeatedly surface eslint-plugin-no-unsanitized, eslint-plugin-sonarjs and eslint-plugin-unicorn. The difference is scope and method. eslint-plugin-no-unsanitized targets DOM sinks such as innerHTML and the bypassSecurityTrustHtml family, which makes it relevant to browser code and largely irrelevant to a Node service. eslint-plugin-sonarjs ports a broad set of code-quality and bug-pattern rules from Sonar, of which security is one category among many. eslint-plugin-unicorn is a general style and correctness plugin with no security focus at all.

Against those, eslint-plugin-security is narrow and Node-specific: its rules name Node APIs directly (child_process, fs, require, Buffer, pseudoRandomBytes). If your codebase is a Node backend, that specificity is the reason to pick it. If your codebase is primarily frontend, no-unsanitized addresses a class of bug this plugin does not look at.

Maintenance, licensing and the upgrade path

The repository is not archived and the last push was on 2026-09-27, the same day as the v4.1.0 release. Releases are cut with release-please, and the changelog is generated by a script (npm run changelog). The package is published under Apache-2.0, which permits commercial use and modification and includes an explicit patent grant; the LICENSE file is at the repository root. That is a permissive licence, and it carries no copyleft obligation on your own code. Nothing here is legal advice, and if your organisation has a licence policy, the identifier to check is Apache-2.0.

Upgrade cost is low but not zero. The move to flat config in v4 means the eslintrc snippet in the README is now labelled deprecated, so projects still on .eslintrc should plan the migration rather than copy the legacy extends line into new work. The plugin's own development workflow is documented as npm test for the mocha suite and npm run-script cont-int to run tests plus lint.

Editorial conclusion

Adopt eslint-plugin-security if your team already runs ESLint 8.23 or later, works in CommonJS or TypeScript via @types/eslint-plugin-security, and can absorb the triage cost of a rule set the README itself says finds many false positives. Do not adopt it as a replacement for a SAST tool or dependency scanner, and do not expect it to reason about data flow: detect-object-injection flags any computed property access, which in a typical codebase means a large warning volume. Before enabling the recommended config repo-wide, run it once on a branch and count how many detect-object-injection and detect-non-literal-fs-filename warnings you actually get, then decide which rules to keep as errors and which to turn off.

Frequently asked questions

What does eslint-plugin-security do?

It adds fifteen ESLint rules that flag risky Node.js patterns such as eval with a variable, non-literal require and fs paths, child_process use, new Buffer(argument) and potentially unsafe regular expressions. The README describes the output as security hotspots that need human triage.

What is one downside of using eslint-plugin-security?

The README states the project finds a lot of false positives which need triage by a human. Rules like detect-object-injection match any computed property access, so a codebase using obj[key] for configuration or dictionaries will produce a large warning volume.

Does eslint-plugin-security support TypeScript?

Type definitions are managed by DefinitelyTyped rather than shipped in the package. The README says to install @types/eslint-plugin-security as a development dependency alongside the plugin.

Which ESLint version does eslint-plugin-security need for flat config?

The README states the flat config example requires eslint >= v8.23.0. The older eslintrc form still exists but is marked deprecated in the README.

Is eslint-plugin-security a replacement for a dependency scanner?

No. The plugin inspects your own source through the ESLint AST and has no knowledge of your dependency tree, so it cannot report a vulnerable version of a package you import.

Official sources

  1. eslint-community/eslint-plugin-security on GitHub
  2. Issues
  3. License: Apache-2.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/eslint-community-eslint-plugin-security.svg)](https://hysenlabs.com/projects/eslint-community-eslint-plugin-security)