Skip to main content
interlace
Plugin: knex-security

Changelog

Release history and version updates for eslint-plugin-knex-security

Live from GitHub

This changelog is fetched directly from CHANGELOG.md on GitHub and cached for 2 hours.

Live changelogfrom eslint-plugin-knex-security/CHANGELOG.md, cached for 2 hours.Edit on GitHub

0.3.0

Minor Changes

  • #373 e5d31ab Thanks @ofri-peretz! - Add require-tls (CWE-319) to the Knex, mysql2, Sequelize and TypeORM security plugins.

    Reports two distinct failures, because they do not share a remediation:

    • tlsDisabled — the connection is plaintext (ssl: false, ?sslmode=disable). Every query, every row and the credentials that open the session cross the network in the clear.
    • certificateValidationDisabledrejectUnauthorized: false (or trustServerCertificate: true on mssql, which inverts the polarity). The traffic is encrypted but the server is never authenticated, so the client completes a handshake just as willingly with whoever answered in the database's place. The fix is to supply the CA, never to switch the check off.

    The detection gate is a database connection config — driver import plus a connection-shaped sibling key — which is what keeps the rule out of eslint-plugin-node-security, where a bare rejectUnauthorized: false would also match every https agent and fetch option in the repo, and double-report this line from two plugins.

    A value the rule cannot read statically (ssl: useTls) is never reported. That is a deliberate false negative in exchange for findings that are always real.

    Not shipped for prisma-security (connection config lives in schema.prisma, not JavaScript), drizzle-security (delegates connection setup to the underlying driver, which its own plugin covers) or sqlite-security (a local file, no network to protect).

Patch Changes

  • #381 74bbf60 Thanks @ofri-peretz! - Load rule modules on demand instead of at plugin load.

    Every plugin barrel used to require all of its rules the moment ESLint loaded the plugin, whether or not your config enabled them. plugin.rules[id] is only ever read for rules a config turns on, so the rest was parse-and-compile cost for code that never ran.

    The published entry now exposes each rule behind a getter, so a rule module is read the first time something asks for it. Measured on a 7-plugin config with 34 rules enabled: 163 rule modules loaded and 251 ms of plugin load, against 34 modules and 8.5 ms — total ESLint wall time 251 ms → 109 ms. On a preset that enables most of a plugin (node-security/recommended, 25 of 37) it is a wash, 72 ms → 65 ms. It is never slower; the win scales with how many plugins you stack and how few of their rules you use.

    Nothing about the plugin API changes. Object.keys(plugin.rules) still lists every rule without loading any of them, repeated reads return the same object, and the ./oxlint sub-export is the same plugin object it always was.

    eslint-plugin-jwt and eslint-plugin-vercel-ai-security also re-export their rule objects as named top-level exports, which cannot be deferred — those two keep loading eagerly.

  • #381 74bbf60 Thanks @ofri-peretz! - Declare what we support, load only what we use

    tslib is gone from every package. It was a NON-optional peer of @interlace/eslint-devkit, so all 26 plugins declared it as a dependency to satisfy that peer — 124 kB every consumer installed so twelve require("tslib") calls could resolve. The shipped JavaScript now inlines the TypeScript helpers instead (--importHelpers false on the emit pass that already re-writes it), costing ~9.5 kB in devkit. Zero tslib requires remain anywhere; verified by installing every plugin with no tslib in the tree and loading all 26 with every rule intact.

    eslint-plugin-import-next had a phantom dependency. Its rules require("typescript") at module load, but it was declared in neither dependencies nor peerDependencies — it worked only because something else in the tree happened to install it. A clean install crashed the whole plugin, not just the type-aware rules. typescript is now a required peer, which is what the code actually needs.

    23 "technologies we support" declarations did nothing. Seven plugins listed their target libraries in peerDependenciesMeta with no matching peerDependencies entry, and npm ignores meta for a package that is not declared a peer — verified by installing eslint-plugin-express-security and watching nothing install and nothing warn. eslint-plugin-jwt appeared to support six JWT libraries and formally supported none. All 23 are now real optional peers, matching the convention pg, mongodb, prisma and the other nine already followed:

    plugintechnologies now actually declared
    eslint-plugin-jwtjsonwebtoken, @nestjs/jwt, express-jwt, jose, jwks-rsa, jwt-decode
    eslint-plugin-lambda-security@aws-sdk/client-lambda, @middy/core, @middy/http-cors, @middy/http-security-headers, @middy/validator
    eslint-plugin-express-securityexpress, helmet, cors, csurf, express-rate-limit
    eslint-plugin-nestjs-security@nestjs/common, @nestjs/throttler, class-validator, class-transformer
    eslint-plugin-vercel-ai-securityai
    eslint-plugin-maintainability, eslint-plugin-react-featurestypescript

    All optional, so nothing is installed on the consumer’s behalf — the declaration is the supported-technology signal, which is exactly what it was meant to be.

    A new gate compares declared dependencies against what the emitted JavaScript actually loads, in both directions: a require with no declaration (works until someone installs cleanly) and a declaration nothing requires (weight every consumer pays). It understands that a dependency may exist to satisfy an optional peer of another dependency, which is why eslint-plugin-import-next legitimately declares oxc-resolver that devkit lazily loads.

  • #335 47cde07 Thanks @ofri-peretz! - Fix the ./oxlint subpath export, which pointed at src/oxlint.js — a file no build produces. require('<package>/oxlint') threw MODULE_NOT_FOUND on every published package, while every README documented that exact wiring for oxlint's jsPlugins. The export now points at the build output, dist/src/oxlint.js.

    The path was hardcoded in scripts/generate-oxlint-shims.ts, so the generator rewrote any manual correction back to the broken value on the next drift check — fixed there rather than per package.

    This release also carries npm provenance: the affected packages were last published from a workstation, which has no OIDC token to attest with, so the published tarballs had no attestation. Publishing through the release workflow signs them.

  • Updated dependencies [85e57a7, 74bbf60, e5d31ab, 1fb1cad, d1a3d8c]:

    • @interlace/eslint-devkit@1.8.0

0.2.1

Patch Changes

  • #364 86baa02 Thanks @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.

0.2.0

Minor Changes

  • #353 e8e9ee6 Thanks @ofri-peretz! - Add no-unscoped-mutation (CWE-284) to the Prisma, Drizzle and Knex plugins

    Every ORM ships a bulk mutation whose unscoped form rewrites or deletes the whole table. prisma.user.deleteMany(), db.delete(users), knex('users').del() — each one type-checks, passes review, and only shows up once it has run against production data. eslint-plugin-drizzle's entire published surface is this single check for a single ORM; this generalizes it.

    The detection lives in one place, createUnscopedMutationRule in @interlace/eslint-devkit, and each plugin instantiates it with its own sinks and remediation copy — the same shape createSqlInjectionRule already uses. Each plugin declares where its scope lives: an options-object filter for Prisma, a chained .where*() for Drizzle and Knex.

    Every instantiation is gated on the driver: the rule is silent in files that never import it, and silent on receivers that do not read as a driver handle. Without that gate, delete and update would match map.delete(key) and store.update(patch) — method names alone are not discriminators.

    PluginSinksWhere scope comes from
    prisma-securitydeleteMany, updateMany{ where } in the options object
    drizzle-securitydelete, updatea chained .where()
    knex-securitydel, delete, updateany of the chained where* family

    argumentRole is the one thing that cannot be inferred from the AST. A lone identifier argument is the filter for Prisma (deleteMany(opts)) and the table for Drizzle (db.delete(users)); reading it wrong either suppresses the headline Drizzle finding or invents a false positive on every dynamically built filter.

    Not shipped for Sequelize or TypeORM. Sequelize gives its instance and static forms the same names and both accept an options object, so user.destroy({ transaction: t }) (one row) and User.destroy({}) (the whole table) are the same AST. Two false positives surfaced in its test suite, and the rule was withdrawn from that package rather than shipped with them — a rule that fires on correct code is the one users disable. The genuinely detectable case, destroy({ truncate: true }), becomes its own rule. TypeORM's bare-criteria shape (repo.delete({ id }), with no where key) is a third detection shape and is deferred for the same reason.

    Scope that cannot be read statically is treated as present, so the rule stays silent rather than guessing. Ships in strict only — promotion to recommended and flagship waits on a measured false-positive profile against the benchmark corpus.

Patch Changes

  • Updated dependencies [e8e9ee6]:
    • @interlace/eslint-devkit@1.7.0

0.1.1

Patch Changes

  • #338 dc25c81 Thanks @ofri-peretz! - Re-publish every package so npm carries the optimised artifact

    No source changed. This is a no-op patch whose entire purpose is to ship the artifact the current build already produces.

    Manifests. scripts and devDependencies are now stripped from every published package.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.ts tree:

    packagepublishedrebuiltsaving
    eslint-plugin-react-features547 kB320 kB−227 kB
    eslint-plugin-secure-coding653 kB477 kB−176 kB
    eslint-plugin-conventions241 kB116 kB−125 kB
    eslint-plugin-browser-security380 kB291 kB−89 kB
    eslint-plugin-maintainability178 kB116 kB−62 kB
    eslint-plugin-react-a11y232 kB173 kB−59 kB
    eslint-plugin-reliability148 kB90 kB−58 kB
    eslint-plugin-vercel-ai-security187 kB130 kB−57 kB
    eslint-plugin-operability90 kB43 kB−47 kB
    eslint-plugin-jwt140 kB95 kB−45 kB
    eslint-plugin-modularity98 kB58 kB−40 kB
    eslint-plugin-nestjs-security122 kB86 kB−36 kB
    eslint-plugin-sqlite-security54 kB20 kB−34 kB
    eslint-plugin-sequelize-security54 kB21 kB−34 kB
    eslint-plugin-prisma-security52 kB19 kB−33 kB
    eslint-plugin-mysql-security52 kB19 kB−33 kB
    eslint-plugin-typeorm-security52 kB19 kB−33 kB
    eslint-plugin-drizzle-security52 kB19 kB−33 kB
    eslint-plugin-knex-security51 kB19 kB−32 kB
    eslint-plugin-modernization45 kB38 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.ts fails the build if scripts or devDependencies ever reappear in a published manifest, so the strip cannot silently regress.

    The dependency ranges did not need updating: every plugin pins @interlace/eslint-devkit with 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 no typescript in the tree.

  • Updated dependencies [dc25c81]:

    • @interlace/eslint-devkit@1.6.1

0.1.0

Minor Changes

Six new driver-scoped SQL-injection plugins (CWE-89), each shipping one rule — no-unsafe-query at error in recommended:

  • eslint-plugin-mysql-security — mysql2 / mysql. Sinks: .query(), .execute() (gated on SQL keywords in the static text, since these are common method names outside MySQL). Remediation names MySQL's own safe API.
  • eslint-plugin-prisma-security — @prisma/client. Sinks: .$queryRawUnsafe(), .$executeRawUnsafe(). Remediation names Prisma's own safe API.
  • eslint-plugin-drizzle-security — drizzle-orm. Sinks: .raw(). Remediation names Drizzle's own safe API.
  • eslint-plugin-knex-security — knex. Sinks: .raw(). Remediation names Knex's own safe API.
  • eslint-plugin-sqlite-security — better-sqlite3 / sqlite3. Sinks: .prepare(), .exec(), .run(), .all(), .get() (gated on SQL keywords in the static text, since these are common method names outside SQLite). Remediation names SQLite's own safe API.
  • eslint-plugin-typeorm-security — typeorm. Sinks: .query(). Remediation names TypeORM's own safe API.

All six instantiate the shared createSqlInjectionRule from @interlace/eslint-devkit, so detection is one implementation and each plugin differs only in sinks, precision gate and remediation copy. Install the one matching your stack and you get exactly one finding per line.

None are added to eslint-config-interlace's aggregated presets: sink names overlap across drivers (.query(), .raw()), so bundling them would report the same line more than once.

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.

On this page

No Headings