Skip to content

@noctcore/eslint-plugin-security

Injection, path traversal, SSRF, open redirect, unsanitized HTML and timing-unsafe comparison: high-precision syntactic sinks only.

A small set of sinks where a user-controlled string becomes a vulnerability: a shell command built by interpolation, a fetch URL whose host comes from a request, a redirect to an arbitrary origin, a path joined from req.params, HTML handed to dangerouslySetInnerHTML unsanitized, an HMAC signature compared with ===, a 'use server' action exported without its action client.

It will yell at you rarely, and on purpose: precision is the point. Each rule matches a narrow syntactic shape and stays silent when it cannot prove the value is dynamic, so it will miss taint that flows through a helper. Treat it as a tripwire, not a security audit, and keep a real SAST tool. Three rules are exported but left out of the preset: require-path-containment because it has a high false-positive rate, require-sanitized-html because a real codebase has trusted HTML only you can list, and server-action-through-client because it needs your action-client names. It is a bad fit if you want broad taint tracking; this plugin trades recall for reports you can trust.

Install
npm install --save-dev @noctcore/eslint-plugin-security @typescript-eslint/parser
# or: bun add -D / pnpm add -D @noctcore/eslint-plugin-security @typescript-eslint/parser
eslint.config.js
// eslint.config.js
import tsParser from '@typescript-eslint/parser';
import security from '@noctcore/eslint-plugin-security';
export default [
{
...security.configs.recommended,
files: ['**/*.{ts,tsx}'],
languageOptions: { parser: tsParser },
},
];
// Rules outside the preset are enabled one by one, e.g.
// { rules: { 'noctcore-security/require-path-containment': 'error' } }

The recommended preset enables 4 of 7 rules.

Each rule links to its page, with the options it takes and Incorrect and Correct examples. A good first read is no-shell-interpolation.

RuleWhat it reportsPresetFixTypes
no-shell-interpolationA dynamically-interpolated command string must not flow into a shell runner (exec/execSync, or spawn/execFile with shell: true). Pass the program and arguments separately.error
no-timing-unsafe-compareAn HMAC digest or signature must not be compared with === / !== / == / != or Buffer#equals; use crypto.timingSafeEqual.error
no-user-controlled-fetch-urlDisallow HTTP requests whose origin is not fixed at authoring time: a runtime-controlled host enables SSRF.error
no-user-controlled-redirectDisallow redirects whose target origin is not fixed at authoring time: a user-controlled target is an open redirect.error
require-path-containmentRequest-shaped input (req.*) passed directly into path.join / path.resolve without a containment guard is a path-traversal sink.not listed
require-sanitized-htmlHTML reaching dangerouslySetInnerHTML, innerHTML, outerHTML or insertAdjacentHTML must be static markup or pass through a configured sanitizer.not listed
server-action-through-clientIn a 'use server' module every exported action must be built from a configured action client; a raw exported function bypasses input validation, error shaping and middleware.not listed

Preset: severity in configs.recommended; off means the preset registers the rule switched off, not listed means it leaves the rule out; both are opt-in, so you turn the rule on yourself. Fix: whether the rule ships an autofix or an editor suggestion. Types: whether the rule needs a type-checked program (parserOptions.projectService).