Changelog
Release history and version updates for eslint-plugin-secure-coding
Live from GitHub
This changelog is fetched directly from CHANGELOG.md on GitHub and cached for 2 hours.
3.4.2
Patch Changes
-
#365
e9bc812Thanks @ofri-peretz! - Stopno-insecure-comparisonmangling== nullunder--fixThe rule offered the
==→===rewrite as an auto-appliedfix, soeslint --fixrewrote this:if (body == null) return 0; // matches null AND undefinedinto this:
if (body === null) return 0; // no longer matches undefinedundefined == nullistrue;undefined === nullisfalse. The fix changed runtime behaviour and introduced bugs in consumer code. It is now a suggestion rather than an auto-applied fix — the rewrite is not guaranteed to preserve behaviour when the operands differ in type, not only for null.Separately,
x == null/x != nullis no longer reported at all. It is the idiomatic nullish check, deliberately matching both null and undefined, which is why coreeqeqeqexempts it undersmart/allow-null. Reporting it as CWE-697 was a false positive — and one carrying CVSS and SOC2/PCI-DSS metadata.Measured over
express,axiosandsequelize: 73 of the rule's 161 reports were this pattern. After the change the same corpus yields 8 reports, all genuine type-mismatched loose equality. -
#364
86baa02Thanks @ofri-peretz! - Add the ecosystem and oxlint marks to the README logo row. Each plugin now leads with Interlace -> its ecosystem (node, nestjs, express, react, mongodb, postgresql, mysql, sqlite, prisma, drizzle, knex, typeorm, sequelize, lambda, vercel, jwt) -> oxlint -> ESLint; the generic quality plugins carry the row without an ecosystem mark. README-only change - no rule behaviour is affected. The patch bump is what carries the new README onto npm, which only refreshes a package README on publish.
3.4.1
Patch Changes
-
#338
dc25c81Thanks @ofri-peretz! - Re-publish every package so npm carries the optimised artifactNo source changed. This is a no-op patch whose entire purpose is to ship the artifact the current build already produces.
Manifests.
scriptsanddevDependenciesare now stripped from every publishedpackage.json. Neither can do anything in a consumer’s node_modules — npm never runs one and never installs the other — but they shipped in all 27 manifests, cluttered the npm page, and were read by SCA tools scanning installed manifests. No package declares a lifecycle hook, so nothing observable changes. Every published package is bumped so this applies uniformly rather than to a subset.Tarballs. 20 packages were last published before the build pipeline changed and still ship
AGENTS.md,CHANGELOG.md, JSDoc in the emitted.js, and the full generated.d.tstree:package published rebuilt saving eslint-plugin-react-features547 kB 320 kB −227 kB eslint-plugin-secure-coding653 kB 477 kB −176 kB eslint-plugin-conventions241 kB 116 kB −125 kB eslint-plugin-browser-security380 kB 291 kB −89 kB eslint-plugin-maintainability178 kB 116 kB −62 kB eslint-plugin-react-a11y232 kB 173 kB −59 kB eslint-plugin-reliability148 kB 90 kB −58 kB eslint-plugin-vercel-ai-security187 kB 130 kB −57 kB eslint-plugin-operability90 kB 43 kB −47 kB eslint-plugin-jwt140 kB 95 kB −45 kB eslint-plugin-modularity98 kB 58 kB −40 kB eslint-plugin-nestjs-security122 kB 86 kB −36 kB eslint-plugin-sqlite-security54 kB 20 kB −34 kB eslint-plugin-sequelize-security54 kB 21 kB −34 kB eslint-plugin-prisma-security52 kB 19 kB −33 kB eslint-plugin-mysql-security52 kB 19 kB −33 kB eslint-plugin-typeorm-security52 kB 19 kB −33 kB eslint-plugin-drizzle-security52 kB 19 kB −33 kB eslint-plugin-knex-security51 kB 19 kB −32 kB eslint-plugin-modernization45 kB 38 kB −7 kB Those 20 go from 3428 kB to 2169 kB — −36.7%. The remaining 7 were released after the pipeline change and only gain the manifest strip.
A new check in
scripts/check-published-artifacts.tsfails the build ifscriptsordevDependenciesever reappear in a published manifest, so the strip cannot silently regress.The dependency ranges did not need updating: every plugin pins
@interlace/eslint-devkitwith a caret that 1.6.0 satisfies, verified by a clean install of an unchanged plugin resolving devkit 1.6.0 with zero dependencies and notypescriptin the tree. -
Updated dependencies [
dc25c81]:- @interlace/eslint-devkit@1.6.1
3.4.0
Minor Changes
-
#288
89bea05Thanks @ofri-peretz! - Cut false positives in five security rules, measured against a 1,470-file corpus (webpacklib/, lodash, eslint-plugin-importsrc/, and two NestJS boilerplates).secure-coding/no-hardcoded-credentials— decide on the value, not the key name. The rule reported any string in a credential-named slot, soerrors: { password: 'incorrectPassword' }(an i18n error key) was a CVSS 9.8 finding — 5 of its 10 corpus hits. Detection is now driven by the value's shape: entropy, character-class mix, charset, and a "natural word string" test that rejects identifier- and message-shaped values. A credential-shaped name is still consulted, but only to promote an already-secret-shaped value. Corpus: 10 → 7 findings, and all 7 are true positives — including two the old logic missed, because a 25-character randomkey:is now found by shape rather than by being on a name allowlist.secure-coding/no-unsafe-deserialization—setTimeoutis not a deserializer.await new Promise(resolve => setTimeout(resolve, 1000))was rated CVSS 9.8 CRITICAL.setTimeout/setIntervalnow only report in their implied-evalform (string first argument), and calls inside a function nameddeserialize/unserialize/fromJSON/fromBuffer— a class implementing a serialization protocol — are exempt. Corpus: 35 → 4.secure-coding/no-graphql-injection— require real GraphQL syntax. Any template literal containing a nested brace or the wordtypewas a CVSS 9.8 GraphQL injection. Operation and schema keywords must now start a line, schema keywords require a body, and a bare selection set must be the entire string. Concatenations are matched on their reassembled static value rather than on their source text. Corpus: 41 → 0.node-security/require-secure-deletion— only sensitive properties. The rule fired on everydelete obj.prop. It now reports only a statically known, sensitive property name (password,token,apiKey,privateKey,sessionId, …), configurable via the newadditionalSensitivePropertiesoption, and understands computed access and optional chaining. Corpus: 25 → 1 (a genuinedelete userDto.oldPassword).secure-coding/no-insecure-comparison— removed fromrecommended,recommended-strictandowasp-top-10. It is deprecated in favour ofnode-security/no-timing-unsafe-compare, and its loose-equality half re-reports coreeqeqequnder a CWE-697 banner — 433 corpus findings, all duplicates. No narrowing fixes that, so the honest change is to stop switching it on for people; it remains exported and available viastrictor explicit opt-in. Its timing-attack half was also narrowed to match secret keywords on identifier word segments instead of substrings of the whole expression text, which stopsif (key === "__non_webpack_require__")(andmonkey,keyword,machine,author) from being reported: 443 → 221.
Patch Changes
-
#294
659f6dcThanks @ofri-peretz! - Rewritedescriptionandkeywordson every published package for npm search discovery. npm ranks on name, description, and keywords, and the registry only picks up these fields at publish — so this is metadata-only and takes effect for each package on its next release.Descriptions now lead with the search phrase. Every one starts
ESLint plugin for <the thing you'd search>instead of a brand-first or category-first framing, and names the concrete vulnerabilities the plugin actually detects. Three were corrected while doing so:eslint-plugin-import-nextclaimed "100x faster no-cycle detection". No 100x measurement exists:CLAIMS.mdrecords 3.1x end-to-end (8x in pure rule execution) on a 5,483-file React codebase, and the highest number in any benchmark result is 54.9x on the synthetic corpus. The description now states the real-codebase figure.eslint-plugin-secure-codingclaimed SQL injection, XSS and CSRF coverage — none of which are its rules. It now names what it does detect: LDAP, XPath, XXE, GraphQL and template injection, unsafe deserialization, ReDoS, missing authentication, and PII in logs.eslint-plugin-secure-coding("89 rules") andeslint-plugin-react-a11y("37 rules") hard-coded rule counts that had drifted from reality. Counts are generated intointerlace-numbers.json; hand-typed copies are removed rather than corrected.
Keywords now match the vocabulary of the plugins that rank.
eslint-plugin-security,eslint-plugin-jsx-a11y,eslint-plugin-nandeslint-plugin-importall carry theeslint/eslintplugin/eslint-plugintrio — six of our packages were missingeslintplugin, and every one now carries all three plusstatic-analysis,lintingandcode-quality. Security plugins addsast,appsecandvulnerability;node-securityandsecure-codingalso carrynodesecurity, the exact keywordeslint-plugin-securityranks on. Each plugin gained the CWE identifiers and attack names for what it detects (cwe-78command injection,cwe-22path traversal,cwe-89SQL injection,cwe-79XSS,cwe-347JWT algorithm confusion,cwe-352CSRF,cwe-943NoSQL injection), andnode-securitygained the crypto vocabulary it had been missing entirely despite absorbing the crypto rule set (crypto,cryptography,weak-hash,md5,sha1,timing-attack).No rule behavior, exports, or configuration changes.
-
#296
0c7a208Thanks @ofri-peretz! - Cut two false positives confirmed against the benchmark corpus SAFE fixtures.node-security/no-ssrf— the user-input gate only ran when the URL argument was a bare identifier, so every other shape reported unconditionally. A Node options object built from a helper's own parameters —https.request({ host, path, method: 'GET' }), frombenchmarks/corpus/CWE-444/safe/request-default-parser.js— was flagged with no user data anywhere in the flow.The gate now applies to every argument shape and requires evidence: a user-input-named identifier standing as the URL, a read off a request object (
req/request/ctx/event), or a template literal or concatenation interpolating either. Options-object fields count when they are request-sourced, or when aurl/href/urikey holds a user-input-named identifier.Newly ignored: options objects and interpolations built purely from locals. Still reported:
fetch(userUrl),fetch(req.query.url),https.request({ host: req.query.host }),fetch(`https://${userHost}/x`).secure-coding/no-hardcoded-credentials—secret: '<your-secret-here>'frombenchmarks/corpus/CWE-798/safe/test-placeholder-values.jswas reported at CVSS 9.8. The angle brackets are two character classes, which is all the shape gate asks for once the slot is credential-named.Self-evident placeholders are now skipped: bracketed template slots (
<…>,{{…}},${…},[…]), placeholder words standing as their own token (changeme,YOUR_API_KEY,example), and one character repeated (xxxxxxxxxxxx). Whole-token matching only, so a real secret that merely contains such a substring is unaffected.The allowlist applies to non-structural findings only — a JWT, an
sk_live_key, or apostgres://user:pass@hoststring still reports whatever words it contains. Set the newallowPlaceholders: falseoption to restore the previous behaviour. -
Updated dependencies [
e1cdf83,659f6dc]:- @interlace/eslint-devkit@1.4.3
3.3.4
Patch Changes
-
#269
7028fe2Thanks @ofri-peretz! - docs: dual-logo README header (Interlace mark + ESLint mark side by side) and closing Interlace footer — refreshes the README rendered on npmjs.com. No runtime changes. -
Updated dependencies [
7028fe2]:- @interlace/eslint-devkit@1.4.2
3.3.3
Patch Changes
- #252
d67e395Thanks @ofri-peretz! - Fix Codecov badge showing "unknown" — switch from flag to component URL format
3.3.2
Patch Changes
- #225
34ff5a8Thanks @ofri-peretz! - CI-only: pin all coverage thresholds at 100% (integration target, merges last).
3.3.1
Patch Changes
-
#213
391dbe6Thanks @ofri-peretz! - Align every security rule'smeta.docs.cvssto the CVSS its finding actually emits. The emitted machine-readable message sources itsCVSS:xfromCWE_MAPPINGviaformatLLMMessage→enrichFromCWE, but the staticmeta.docs.cvssdocumentation field had drifted on 45 rules across these 7 plugins — e.g.no-hardcoded-credentialsdocumented9.5while emittingCVSS:9.8(the value the published article and SARIF/LLM consumers already read).This corrects the documentation metadata only — no emitted finding changes. Locked by
security-cvss-docs-consistency.lock.test.ts(cross-plugin: every security rule'smeta.docs.cvssmust equal the CVSS it emits), theno-hardcoded-credentialsrule lock (real ESLintLinteremission), and a devkitenrichFromCWEcontract test pinningCWE-798 → 9.8.Follow-up (not in scope): 50 security rules document a CVSS that never appears in any emitted message (their messages carry no CWE), and several rules emit the generic CWE score where a rule-specific score may be warranted — both change emitted output and are separate decisions.
3.3.0
Minor Changes
-
#170
4cbf3edThanks @ofri-peretz! - Addrecommended-strictpreset + quick-start in READMENew preset:
configs['recommended-strict']Same 16-rule set asrecommendedbut every rule promoted to'error'. For teams that want CI to block on all security findings, not just critical ones. The recommended preset stays unchanged.// eslint.config.mjs import securePlugin from 'eslint-plugin-secure-coding'; export default [...securePlugin.configs['recommended-strict']];README: copy-paste quick-start block Added a one-line usage example immediately after
npm installso adopters don't have to discover the preset table buried further down the page. Also added cross-plugin discovery links tonode-security,jwt, andexpress-securityfor teams that want broader coverage.
Patch Changes
-
#137
a56da52Thanks @ofri-peretz! - fix(detect-object-injection): suppress ~3,470 Edge false positives via four new safe-pattern guards- Test-file skip: rule is now silent on
*.test.*,*.spec.*,__tests__/, and*.fixture.*paths for...inloop variable: keys fromfor (const key in obj)are own property names, not user inputObject.keys/entriesiteration:for (const key of Object.keys(obj))is safe by construction- Typed-array objects (
new Float32Array/Uint8Array/Int32Array/…): element access is numeric, not string-keyed
None of the guards widen the TP surface — dangerous properties (
__proto__,constructor,prototype) and genuine user-input bracket access still fire. Closes the largest single source of ILB-Wild noise. - Test-file skip: rule is now silent on
-
#144
8843ce7Thanks @ofri-peretz! - fix: ILB-Wild FP reduction + doc examples + doc-test-alignment scanner fixesno-unlimited-resource-allocation— FP reduction (430 Edge FPs)- Skip loop-allocation reporting when the first argument is a numeric literal (e.g.
Buffer.alloc(1024)inside a loop is statically bounded, not a risk) - Skip
Array.isArray,Array.from,Array.ofcalls in thealloc/Arraypattern check (these don't allocate unbounded memory)
no-hardcoded-credentials— FP reduction (~280 Edge FPs)- Extended test-file skip to cover
.fixture.,.mock.,__mocks__/,/tests/,/fixtures/,/mocks/paths - Skip string literals that are fallback values in
process.env.X || 'fallback'expressions — the secret lives in the environment, the string is only a dev-mode default
Doc examples — 4 rules now have ❌ Incorrect examples
lambda-security/no-missing-authorization-checklambda-security/no-overly-permissive-iam-policynode-security/prefer-native-crypto(renamed non-standard### ❌ Third-Party (Flagged)to### ❌ Incorrect)vercel-ai-security/require-tool-confirmation(replaced placeholder with a real tested example)
ilb-doc-test-alignmentscanner fixes- Accept both
## ❌and### ❌headings (docs use H3 under an H2## Examplessection; was only finding H2) - Slice from end-of-line rather than end-of-regex-match (prevents
## ❌ Incorrect Codefrom leaving a partial heading in the parsed section)
Result:
ilb:doc-test-alignment→ 206 ok, 0 doc has no ❌ examples (was 165 missing). - Skip loop-allocation reporting when the first argument is a numeric literal (e.g.
-
#141
38ab670Thanks @ofri-peretz! - fix: remove falsemeta.fixable: 'code'declarations from 21 rules that had nofix()functionRules that declared
fixable: 'code'in their ESLint meta without an actualfix()implementation would show the ⚡ auto-fix icon in editors and CI formatters but apply no change when--fixwas run. This patch removes the misleading declaration from:browser-security/no-clickjackingimport-next/first,named,no-barrel-import,no-import-module-exports,no-namespacenode-security/no-buffer-overread,no-unsafe-dynamic-require,no-zip-slipreact-features/react-no-inline-functionsreliability/no-jsdoc-terminator-in-example(usessuggest, not auto-fix; corrected tohasSuggestions: trueonly)secure-coding/no-directive-injection,no-electron-security-issues,no-graphql-injection,no-improper-sanitization,no-improper-type-validation,no-ldap-injection,no-unchecked-loop-condition,no-unlimited-resource-allocation,no-weak-password-recovery,no-xpath-injection
-
#148
82718c2Thanks @ofri-peretz! - feat+fix: ILB-Wild FP reduction + two new quality rulesno-unsafe-deserializationFP reduction (~112 FPs)- Track
fs.readFileSync('literal')calls inliteralPathFileVars— a file read with a hardcoded path (bundled config) is not user-controlled input for safe deserializers (JSON.parse, schema-validating parsers).eval()still fires even on literal-path reads.
no-buffer-overreadFP reduction (~129 FPs)- Remove
b(single-char, too broad) andchunk(too common for array chunks) from the Buffer alias heuristic —isBufferTypenow only matchesbufandbytesby name, reducing false matches on non-Buffer variables.
New rule:
modernization/prefer-template-literal- Flags
"string " + variableconcatenation and suggests the equivalent template literal. - Auto-fix produces the correct
`string ${variable}`replacement. - Pure string literal chains (
"a" + "b") and numeric addition are not flagged. - Closes P2 quality FN
prob_string_concatin the ILB-Arena-Quality bench.
New rule:
modularity/no-mutable-exports- Flags
export letandexport var— module exports should be immutableconstbindings so all importers share a stable reference. - Auto-fix replaces
let/varwithconst. - Closes P2 quality FN
prob_mutable_exportin the ILB-Arena-Quality bench.
- Track
-
Updated dependencies [
736a5fe]:- @interlace/eslint-devkit@1.4.1
3.2.0 (Unreleased)
Added
- New
./oxlintsub-export for use with oxlint's JS plugin API. Wire it via{ "jsPlugins": ["eslint-plugin-secure-coding/oxlint"] }in.oxlintrc.json. Exposes the same rule set as the main entry; rules degrade gracefully when type information is unavailable (oxlint's JS plugin context does not provideparserServices). The default ESLint entry (./) is unchanged.
3.1.3 (2026-02-09)
This was a version bump only for eslint-plugin-secure-coding to align it with other projects, there were no code changes.
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[3.1.1] - 2026-02-09
This was a version bump only for eslint-plugin-secure-coding to align it with other projects, there were no code changes.
[3.1.0] - 2026-02-09
This was a version bump only for eslint-plugin-secure-coding to align it with other projects, there were no code changes.
[3.0.3] - 2026-02-09
This was a version bump only for eslint-plugin-secure-coding to align it with other projects, there were no code changes.
[3.0.2] - 2025-12-20
Performance
- detect-object-injection: Replaced
getText()+ regex with AST-based validation (~4x faster) - detect-non-literal-fs-filename: Replaced
getText()+ regex with AST-based validation - no-timing-attack: Set-based O(1) lookups for sensitive variables and auth patterns
- no-buffer-overread: Set-based O(1) lookups for buffer methods and user-controlled keywords
- no-missing-csrf-protection: Set-based O(1) lookups for protected HTTP methods
- detect-child-process: Set-based O(1) lookups for dangerous child_process methods
[3.0.1] - 2025-12-20
Fixed444
- detect-object-injection: Reduced false positives by detecting validation patterns:
includes()checks in enclosing if-blockshasOwnProperty()/Object.hasOwn()/inoperator checks- Preceding guard clauses with early exit (
if (!valid) throw) - Numeric index access (
items[0],items[1]) now recognized as safe
- detect-non-literal-fs-filename: Allow safe path patterns:
path.join(__dirname, ...literals)with all literal arguments- Paths validated with
startsWith()checks (both inside if-blocks and after guard clauses)
- no-timing-attack: Skip false positives in timing-safe contexts:
- Length comparisons before
crypto.timingSafeEqual() - Early returns inside functions using
timingSafeEqual - Fixed file-level sensitive variable detection to be function-scoped
- Length comparisons before
- no-unsanitized-html: Track sanitized variables:
- Variables assigned from
DOMPurify.sanitize()now recognized as safe
- Variables assigned from
- no-unlimited-resource-allocation: Allow safe static paths:
fs.readFileSync(path.join(__dirname, ...literals))patterns now recognized as safe
[3.0.0] - 2025-12-14
Added
- OWASP Mobile Top 10 Coverage: Added 40 new rules targeting mobile security risks (M1-M10).
- New Presets:
owasp-mobile-top-10: Comprehensive mobile security ruleset.
- Documentation:
- Full "Mobile Security" table in README with CVSS scores and fixable icons.
- Updated
AGENTS.mdwith complete rule catalog for AI assistants.
Changed
- Recommended Config: Now includes critical mobile security rules for hybrid web/mobile apps.
- Rule Improvements: Refined AST detection for
no-clickjackingandno-unvalidated-deeplinksto reduce false positives.
[1.0.0] - 2025-01-01
Added
- Initial release with 48 security-focused ESLint rules
- LLM-optimized error messages with CWE references and OWASP mapping
- Three preset configurations:
recommended,strict,owasp-top-10 - Full ESLint 9 flat config support
- TypeScript support
Security Rules
Injection Prevention (11 rules)
no-sql-injection- SQL injection preventiondatabase-injection- Comprehensive SQL/NoSQL/ORM injectiondetect-eval-with-expression- Dynamic eval() detectiondetect-child-process- Command injection detectionno-unsafe-dynamic-require- Dynamic require() preventionno-graphql-injection- GraphQL injection preventionno-xxe-injection- XXE injection preventionno-xpath-injection- XPath injection preventionno-ldap-injection- LDAP injection preventionno-directive-injection- Template injection preventionno-format-string-injection- Format string injection prevention
Path & File Security (3 rules)
detect-non-literal-fs-filename- Path traversal detectionno-zip-slip- Zip slip vulnerability preventionno-toctou-vulnerability- TOCTOU race condition detection
Regex Security (3 rules)
detect-non-literal-regexp- ReDoS detection in RegExpno-redos-vulnerable-regex- ReDoS pattern detectionno-unsafe-regex-construction- Unsafe regex prevention
Object & Prototype (2 rules)
detect-object-injection- Prototype pollution detectionno-unsafe-deserialization- Unsafe deserialization prevention
Cryptography (6 rules)
no-hardcoded-credentials- Hardcoded secrets detectionno-weak-crypto- Weak algorithm detectionno-insufficient-random- Weak randomness detectionno-timing-attack- Timing attack preventionno-insecure-comparison- Insecure comparison detectionno-insecure-jwt- JWT security issues detection
Input Validation & XSS (5 rules)
no-unvalidated-user-input- Input validation enforcementno-unsanitized-html- XSS via innerHTML preventionno-unescaped-url-parameter- URL parameter XSS preventionno-improper-sanitization- Output encoding enforcementno-improper-type-validation- Type confusion prevention
Authentication & Authorization (3 rules)
no-missing-authentication- Auth check enforcementno-privilege-escalation- Privilege escalation detectionno-weak-password-recovery- Secure password reset enforcement
Session & Cookies (3 rules)
no-insecure-cookie-settings- Cookie security enforcementno-missing-csrf-protection- CSRF protection enforcementno-document-cookie- Direct cookie access detection
Network & Headers (5 rules)
no-missing-cors-check- CORS validation enforcementno-missing-security-headers- Security header enforcementno-insecure-redirects- Open redirect preventionno-unencrypted-transmission- HTTPS enforcementno-clickjacking- Clickjacking prevention
Data Exposure (2 rules)
no-exposed-sensitive-data- Data exposure preventionno-sensitive-data-exposure- Log sanitization enforcement
Buffer & Memory (1 rule)
no-buffer-overread- Buffer safety enforcement
DoS & Resource (2 rules)
no-unlimited-resource-allocation- Resource limit enforcementno-unchecked-loop-condition- Infinite loop prevention
Platform-Specific (2 rules)
no-electron-security-issues- Electron security enforcementno-insufficient-postmessage-validation- postMessage validation
View on GitHub →
Building secure JavaScript with Interlace? Star the repo to get new rules and CWE coverage as we ship them — or follow the AI-code-security benchmarks behind them.