From 79e56c9c04c7f2ed7a38853136a9eac147863206 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Thu, 23 Jul 2026 01:13:35 +0200 Subject: [PATCH 01/15] Add `no-unsafe-sqlite-interpolation` rule --- docs/rules/no-unsafe-sqlite-interpolation.md | 43 ++ readme.md | 1 + rules/index.js | 1 + rules/no-unsafe-sqlite-interpolation.js | 349 +++++++++++++++ test/no-unsafe-sqlite-interpolation.js | 72 +++ .../no-unsafe-sqlite-interpolation.js.md | 421 ++++++++++++++++++ .../no-unsafe-sqlite-interpolation.js.snap | Bin 0 -> 1428 bytes 7 files changed, 887 insertions(+) create mode 100644 docs/rules/no-unsafe-sqlite-interpolation.md create mode 100644 rules/no-unsafe-sqlite-interpolation.js create mode 100644 test/no-unsafe-sqlite-interpolation.js create mode 100644 test/snapshots/no-unsafe-sqlite-interpolation.js.md create mode 100644 test/snapshots/no-unsafe-sqlite-interpolation.js.snap diff --git a/docs/rules/no-unsafe-sqlite-interpolation.md b/docs/rules/no-unsafe-sqlite-interpolation.md new file mode 100644 index 0000000000..20a422921d --- /dev/null +++ b/docs/rules/no-unsafe-sqlite-interpolation.md @@ -0,0 +1,43 @@ +# no-unsafe-sqlite-interpolation + +πŸ“ Disallow interpolation into SQL strings passed to Node’s `node:sqlite` APIs. + +πŸ’Ό This rule is enabled in the following [configs](https://github.com/sindresorhus/eslint-plugin-unicorn#recommended-config): βœ… `recommended`, β˜‘οΈ `unopinionated`. + + + + +Interpolating values into SQL strings makes them part of the SQL source instead of bound parameters. This can lead to SQL injection and data corruption. + +Tagged templates passed directly to `exec()` or `prepare()` are also reported because they are not `SQLTagStore` calls, including static tags such as `String.raw`. + +Use placeholders with `DatabaseSync#prepare()` and bind values when executing the prepared statement: + +```js +const query = database.prepare('SELECT * FROM users WHERE id = ?'); +query.get(id); +``` + +For Node.js 24.9.0 and later, use `SQLTagStore` tagged templates, which bind interpolated values as parameters: + +```js +const sql = database.createTagStore(); +sql.get`SELECT * FROM users WHERE id = ${id}`; +``` + +## Examples + +```js +// ❌ +database.exec(`SELECT * FROM users WHERE id = ${id}`); +``` + +```js +// ❌ +database.prepare(`SELECT * FROM users WHERE id = ${id}`); +``` + +```js +// βœ… +database.exec('CREATE TABLE users (id INTEGER)'); +``` diff --git a/readme.md b/readme.md index abb6410af2..9a93598e32 100644 --- a/readme.md +++ b/readme.md @@ -246,6 +246,7 @@ export default defineConfig([ | [no-unsafe-dom-html](docs/rules/no-unsafe-dom-html.md) | Disallow unsafe DOM HTML APIs. | | | | | | [no-unsafe-promise-all-settled-values](docs/rules/no-unsafe-promise-all-settled-values.md) | Disallow reading `.value` from `Promise.allSettled()` results without a fulfilled status guard. | βœ… β˜‘οΈ | | | | | [no-unsafe-property-key](docs/rules/no-unsafe-property-key.md) | Disallow unsafe values as property keys. | βœ… | | | | +| [no-unsafe-sqlite-interpolation](docs/rules/no-unsafe-sqlite-interpolation.md) | Disallow interpolation into SQL strings passed to Node’s `node:sqlite` APIs. | βœ… β˜‘οΈ | | | | | [no-unsafe-string-replacement](docs/rules/no-unsafe-string-replacement.md) | Disallow non-literal replacement values in `String#replace()` and `String#replaceAll()`. | βœ… | | | | | [no-unused-array-method-return](docs/rules/no-unused-array-method-return.md) | Disallow ignoring the return value of selected array methods. | βœ… β˜‘οΈ | | | | | [no-unused-properties](docs/rules/no-unused-properties.md) | Disallow unused object properties. | | | | | diff --git a/rules/index.js b/rules/index.js index b6dc1bf7af..fd58e39337 100644 --- a/rules/index.js +++ b/rules/index.js @@ -155,6 +155,7 @@ export {default as 'no-unsafe-buffer-conversion'} from './no-unsafe-buffer-conve export {default as 'no-unsafe-dom-html'} from './no-unsafe-dom-html.js'; export {default as 'no-unsafe-promise-all-settled-values'} from './no-unsafe-promise-all-settled-values.js'; export {default as 'no-unsafe-property-key'} from './no-unsafe-property-key.js'; +export {default as 'no-unsafe-sqlite-interpolation'} from './no-unsafe-sqlite-interpolation.js'; export {default as 'no-unsafe-string-replacement'} from './no-unsafe-string-replacement.js'; export {default as 'no-unused-array-method-return'} from './no-unused-array-method-return.js'; export {default as 'no-unused-properties'} from './no-unused-properties.js'; diff --git a/rules/no-unsafe-sqlite-interpolation.js b/rules/no-unsafe-sqlite-interpolation.js new file mode 100644 index 0000000000..3f55c57eae --- /dev/null +++ b/rules/no-unsafe-sqlite-interpolation.js @@ -0,0 +1,349 @@ +import {findVariable, getPropertyName, ReferenceTracker} from '@eslint-community/eslint-utils'; +import {isStaticRequire} from './ast/index.js'; +import {getConstVariableInitializer, isRuntimeImportSpecifier} from './utils/index.js'; + +const MESSAGE_ID = 'no-unsafe-sqlite-interpolation'; +const messages = { + [MESSAGE_ID]: 'Do not interpolate values into `node:sqlite` SQL strings.', +}; + +const databaseMethods = new Set([ + 'exec', + 'prepare', +]); + +const transparentExpressionTypes = new Set([ + 'ChainExpression', + 'TSAsExpression', + 'TSSatisfiesExpression', + 'TSNonNullExpression', + 'TSTypeAssertion', +]); + +const databaseSyncEsmTraceMap = { + 'node:sqlite': { + [ReferenceTracker.ESM]: true, + default: { + DatabaseSync: { + [ReferenceTracker.CONSTRUCT]: true, + }, + }, + DatabaseSync: { + [ReferenceTracker.CONSTRUCT]: true, + }, + }, +}; + +const databaseSyncCjsTraceMap = { + 'node:sqlite': { + DatabaseSync: { + [ReferenceTracker.CONSTRUCT]: true, + }, + }, +}; + +const unwrapExpression = node => { + while (node && transparentExpressionTypes.has(node.type)) { + node = node.expression; + } + + return node; +}; + +const getVariableDefinition = (node, context) => { + if (node.type !== 'Identifier') { + return; + } + + const variable = findVariable(context.sourceCode.getScope(node), node); + if (!variable || variable.defs.length !== 1) { + return; + } + + return variable.defs[0]; +}; + +const getVariableInitializer = (node, context) => { + const definition = getVariableDefinition(node, context); + if ( + definition?.type !== 'Variable' + || definition.node.type !== 'VariableDeclarator' + || definition.node.id.type !== 'Identifier' + ) { + return; + } + + const initializer = getConstVariableInitializer(node, context); + if (!initializer) { + return; + } + + return { + variable: findVariable(context.sourceCode.getScope(node), node), + initializer, + }; +}; + +const isDatabaseSyncImport = (node, context) => { + const definition = getVariableDefinition(node, context); + if ( + !definition + || definition.type !== 'ImportBinding' + || definition.parent.source.value !== 'node:sqlite' + || !isRuntimeImportSpecifier(definition.node) + ) { + return false; + } + + return definition.node.type === 'ImportSpecifier' + && definition.node.imported.type === 'Identifier' + && definition.node.imported.name === 'DatabaseSync'; +}; + +const isSqliteNamespaceImport = (node, context) => { + const definition = getVariableDefinition(node, context); + if ( + !definition + || definition.type !== 'ImportBinding' + || definition.parent.source.value !== 'node:sqlite' + || !isRuntimeImportSpecifier(definition.node) + ) { + return false; + } + + return definition.node.type === 'ImportNamespaceSpecifier' + || definition.node.type === 'ImportDefaultSpecifier' + || ( + definition.node.type === 'ImportSpecifier' + && definition.node.imported.type === 'Identifier' + && definition.node.imported.name === 'default' + ); +}; + +const isConstRequireBinding = (node, context) => { + const definition = getVariableDefinition(node, context); + return Boolean( + definition + && definition.type === 'Variable' + && definition.node.type === 'VariableDeclarator' + && definition.parent.type === 'VariableDeclaration' + && definition.parent.kind === 'const' + && isStaticRequire(definition.node.init), + ); +}; + +const isDatabaseSyncRequireBinding = (node, context) => { + if (!isConstRequireBinding(node, context)) { + return false; + } + + const definition = getVariableDefinition(node, context); + const variable = findVariable(context.sourceCode.getScope(node), node); + const {id} = definition.node; + return id.type === 'ObjectPattern' + && id.properties.some(property => + property.type === 'Property' + && getPropertyName(property) === 'DatabaseSync' + && property.value.type === 'Identifier' + && findVariable(context.sourceCode.getScope(property.value), property.value) === variable, + ); +}; + +const isSqliteNamespaceRequireBinding = (node, context) => { + if (!isConstRequireBinding(node, context)) { + return false; + } + + return getVariableDefinition(node, context).node.id.type === 'Identifier'; +}; + +const isDirectDatabaseSyncConstructor = (node, context) => { + const callee = unwrapExpression(node.callee); + if (callee.type === 'Identifier') { + return isDatabaseSyncImport(callee, context) + || isDatabaseSyncRequireBinding(callee, context); + } + + if (callee.type !== 'MemberExpression' || getPropertyName(callee, context.sourceCode.getScope(callee)) !== 'DatabaseSync') { + return false; + } + + const object = unwrapExpression(callee.object); + return isSqliteNamespaceImport(object, context) + || isSqliteNamespaceRequireBinding(object, context) + || isStaticRequire(object); +}; + +const getDatabaseConstructors = (program, context) => { + const tracker = new ReferenceTracker(context.sourceCode.getScope(program)); + const databaseConstructors = new WeakSet(); + + for (const reference of tracker.iterateEsmReferences(databaseSyncEsmTraceMap)) { + if ( + reference.type === ReferenceTracker.CONSTRUCT + && isDirectDatabaseSyncConstructor(reference.node, context) + ) { + databaseConstructors.add(reference.node); + } + } + + for (const reference of tracker.iterateCjsReferences(databaseSyncCjsTraceMap)) { + if ( + reference.type === ReferenceTracker.CONSTRUCT + && isDirectDatabaseSyncConstructor(reference.node, context) + ) { + databaseConstructors.add(reference.node); + } + } + + return databaseConstructors; +}; + +const createDatabaseInstanceChecker = (databaseConstructors, context) => { + const cache = new WeakMap(); + + const isDatabaseInstance = (node, seenVariables = new Set()) => { + node = unwrapExpression(node); + if (!node) { + return false; + } + + if (node.type === 'NewExpression') { + return databaseConstructors.has(node); + } + + if (node.type !== 'Identifier') { + return false; + } + + const variableInfo = getVariableInitializer(node, context); + if (!variableInfo) { + return false; + } + + const {variable, initializer} = variableInfo; + if (cache.has(variable)) { + return cache.get(variable); + } + + if (seenVariables.has(variable)) { + return false; + } + + seenVariables.add(variable); + const result = isDatabaseInstance(initializer, seenVariables); + cache.set(variable, result); + + return result; + }; + + return isDatabaseInstance; +}; + +const createUnsafeSqlArgumentChecker = context => { + const cache = new WeakMap(); + + const isUnsafeSqlArgument = (node, seenVariables = new Set()) => { + node = unwrapExpression(node); + if (!node) { + return false; + } + + if (node.type === 'TemplateLiteral') { + return node.expressions.length > 0; + } + + if (node.type === 'TaggedTemplateExpression') { + return true; + } + + if (node.type !== 'Identifier') { + return false; + } + + const variableInfo = getVariableInitializer(node, context); + if (!variableInfo) { + return false; + } + + const {variable, initializer} = variableInfo; + if (cache.has(variable)) { + return cache.get(variable); + } + + if (seenVariables.has(variable)) { + return false; + } + + seenVariables.add(variable); + const result = isUnsafeSqlArgument(initializer, seenVariables); + cache.set(variable, result); + + return result; + }; + + return isUnsafeSqlArgument; +}; + +const getProblem = (callExpression, context, isDatabaseInstance, isUnsafeSqlArgument) => { + const callee = unwrapExpression(callExpression.callee); + if (callee?.type !== 'MemberExpression') { + return; + } + + const method = getPropertyName(callee, context.sourceCode.getScope(callee)); + if (!databaseMethods.has(method) || !isDatabaseInstance(callee.object)) { + return; + } + + const [sql] = callExpression.arguments; + if (!isUnsafeSqlArgument(sql, new Set())) { + return; + } + + return { + node: sql, + messageId: MESSAGE_ID, + }; +}; + +/** @param {import('eslint').Rule.RuleContext} context */ +const create = context => { + const callExpressions = []; + + context.on('CallExpression', node => { + callExpressions.push(node); + }); + + context.onExit('Program', function * (program) { + const databaseConstructors = getDatabaseConstructors(program, context); + const isDatabaseInstance = createDatabaseInstanceChecker(databaseConstructors, context); + const isUnsafeSqlArgument = createUnsafeSqlArgumentChecker(context); + + for (const callExpression of callExpressions) { + const problem = getProblem(callExpression, context, isDatabaseInstance, isUnsafeSqlArgument); + if (problem) { + yield problem; + } + } + }); +}; + +/** @type {import('eslint').Rule.RuleModule} */ +const config = { + create, + meta: { + type: 'problem', + docs: { + description: 'Disallow interpolation into SQL strings passed to Node’s `node:sqlite` APIs.', + recommended: 'unopinionated', + }, + schema: [], + messages, + languages: [ + 'js/js', + ], + }, +}; + +export default config; diff --git a/test/no-unsafe-sqlite-interpolation.js b/test/no-unsafe-sqlite-interpolation.js new file mode 100644 index 0000000000..d88b074ee9 --- /dev/null +++ b/test/no-unsafe-sqlite-interpolation.js @@ -0,0 +1,72 @@ +/* eslint-disable no-template-curly-in-string */ +import {getTester, parsers} from './utils/test.js'; + +const {test} = getTester(import.meta); + +test.snapshot({ + valid: [ + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec("SELECT 1");', + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT 1`);', + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.prepare("SELECT ?").get(value);', + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.run(`SELECT ${value}`);', + 'import {DatabaseSync} from "node:sqlite"; const sql = new DatabaseSync(":memory:").createTagStore(); sql.get`SELECT * FROM users WHERE id = ${value}`;', + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec("SELECT " + value);', + 'import {DatabaseSync} from "node:sqlite"; const database = createDatabase(); database.exec(`SELECT ${value}`);', + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const {exec} = database; exec(`SELECT ${value}`);', + 'import {DatabaseSync} from "node:sqlite"; const {database} = new DatabaseSync(":memory:"); database.exec(`SELECT ${value}`);', + 'const sqlite = await import("node:sqlite"); const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${value}`);', + { + code: 'import type {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${value}`);', + languageOptions: {parser: parsers.typescript}, + }, + { + code: 'import {type DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${value}`);', + languageOptions: {parser: parsers.typescript}, + }, + 'import {DatabaseSync} from "node:sqlite"; let Database = DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${value}`);', + 'import {DatabaseSync} from "node:sqlite"; let Database; Database = DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${value}`);', + 'import {DatabaseSync} from "node:sqlite"; let database = new DatabaseSync(":memory:"); database.exec(`SELECT ${value}`);', + 'import {DatabaseSync} from "other-sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${value}`);', + 'class DatabaseSync { exec() {} } const database = new DatabaseSync(); database.exec(`SELECT ${value}`);', + 'database.exec(`SELECT ${value}`);', + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec();', + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(...queries);', + ], + invalid: [ + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT * FROM users WHERE id = ${id}`);', + 'import {DatabaseSync as Database} from "node:sqlite"; const database = new Database(":memory:"); database.prepare(`SELECT * FROM users WHERE id = ${id}`);', + 'import * as sqlite from "node:sqlite"; const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT * FROM users WHERE id = ${id}`);', + 'import sqlite from "node:sqlite"; const database = new sqlite.DatabaseSync(":memory:"); database.prepare(`SELECT * FROM users WHERE id = ${id}`);', + 'import {DatabaseSync} from "node:sqlite"; new DatabaseSync(":memory:").exec(`SELECT * FROM users WHERE id = ${id}`);', + 'const {DatabaseSync} = require("node:sqlite"); const database = new DatabaseSync(":memory:"); database.exec(`SELECT * FROM users WHERE id = ${id}`);', + 'const {DatabaseSync: Database} = require("node:sqlite"); const database = new Database(":memory:"); database.prepare(`SELECT * FROM users WHERE id = ${id}`);', + 'const sqlite = require("node:sqlite"); const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT * FROM users WHERE id = ${id}`);', + 'const database = new (require("node:sqlite").DatabaseSync)(":memory:"); database.prepare(`SELECT * FROM users WHERE id = ${id}`);', + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const query = `SELECT * FROM users WHERE id = ${id}`; database.exec(query);', + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const query = `SELECT * FROM users WHERE id = ${id}`; const alias = query; database.prepare(alias);', + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database?.exec(`SELECT * FROM users WHERE id = ${id}`);', + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec?.(`SELECT * FROM users WHERE id = ${id}`);', + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database["prepare"](`SELECT * FROM users WHERE id = ${id}`);', + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${first} FROM users WHERE id = ${second}`);', + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(/* keep */ `SELECT * FROM users WHERE id = ${id}`);', + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(String.raw`SELECT * FROM users WHERE id = ${id}`);', + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.prepare(String.raw`SELECT 1`);', + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const sql = `SELECT ${id}`; database.exec(sql);', + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const sql = `SELECT ${id}`; const alias = sql; database.prepare(alias);', + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const alias = database; alias.exec(`SELECT ${id}`);', + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database["exec"](`SELECT ${id}`);', + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const method = "exec"; database[method](`SELECT ${id}`);', + { + code: 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` as string);', + languageOptions: {parser: parsers.typescript}, + }, + { + code: 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT * FROM users WHERE id = ${id}` as string);', + languageOptions: {parser: parsers.typescript}, + }, + { + code: 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database!.exec(`SELECT ${id}` satisfies string);', + languageOptions: {parser: parsers.typescript}, + }, + ], +}); diff --git a/test/snapshots/no-unsafe-sqlite-interpolation.js.md b/test/snapshots/no-unsafe-sqlite-interpolation.js.md new file mode 100644 index 0000000000..98b8ca6aab --- /dev/null +++ b/test/snapshots/no-unsafe-sqlite-interpolation.js.md @@ -0,0 +1,421 @@ +# Snapshot report for `test/no-unsafe-sqlite-interpolation.js` + +The actual snapshot is saved in `no-unsafe-sqlite-interpolation.js.snap`. + +Generated by [AVA](https://avajs.dev). + +## invalid(1): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT * FROM users WHERE id = ${id}`); + +> Input + + `␊ + 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(\`SELECT * FROM users WHERE id = ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(\`SELECT * FROM users WHERE id = ${id}\`);␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(2): import {DatabaseSync as Database} from "node:sqlite"; const database = new Database(":memory:"); database.prepare(`SELECT * FROM users WHERE id = ${id}`); + +> Input + + `␊ + 1 | import {DatabaseSync as Database} from "node:sqlite"; const database = new Database(":memory:"); database.prepare(\`SELECT * FROM users WHERE id = ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {DatabaseSync as Database} from "node:sqlite"; const database = new Database(":memory:"); database.prepare(\`SELECT * FROM users WHERE id = ${id}\`);␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(3): import * as sqlite from "node:sqlite"; const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT * FROM users WHERE id = ${id}`); + +> Input + + `␊ + 1 | import * as sqlite from "node:sqlite"; const database = new sqlite.DatabaseSync(":memory:"); database.exec(\`SELECT * FROM users WHERE id = ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import * as sqlite from "node:sqlite"; const database = new sqlite.DatabaseSync(":memory:"); database.exec(\`SELECT * FROM users WHERE id = ${id}\`);␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(4): import sqlite from "node:sqlite"; const database = new sqlite.DatabaseSync(":memory:"); database.prepare(`SELECT * FROM users WHERE id = ${id}`); + +> Input + + `␊ + 1 | import sqlite from "node:sqlite"; const database = new sqlite.DatabaseSync(":memory:"); database.prepare(\`SELECT * FROM users WHERE id = ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import sqlite from "node:sqlite"; const database = new sqlite.DatabaseSync(":memory:"); database.prepare(\`SELECT * FROM users WHERE id = ${id}\`);␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(5): import {DatabaseSync} from "node:sqlite"; new DatabaseSync(":memory:").exec(`SELECT * FROM users WHERE id = ${id}`); + +> Input + + `␊ + 1 | import {DatabaseSync} from "node:sqlite"; new DatabaseSync(":memory:").exec(\`SELECT * FROM users WHERE id = ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; new DatabaseSync(":memory:").exec(\`SELECT * FROM users WHERE id = ${id}\`);␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(6): const {DatabaseSync} = require("node:sqlite"); const database = new DatabaseSync(":memory:"); database.exec(`SELECT * FROM users WHERE id = ${id}`); + +> Input + + `␊ + 1 | const {DatabaseSync} = require("node:sqlite"); const database = new DatabaseSync(":memory:"); database.exec(\`SELECT * FROM users WHERE id = ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | const {DatabaseSync} = require("node:sqlite"); const database = new DatabaseSync(":memory:"); database.exec(\`SELECT * FROM users WHERE id = ${id}\`);␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(7): const {DatabaseSync: Database} = require("node:sqlite"); const database = new Database(":memory:"); database.prepare(`SELECT * FROM users WHERE id = ${id}`); + +> Input + + `␊ + 1 | const {DatabaseSync: Database} = require("node:sqlite"); const database = new Database(":memory:"); database.prepare(\`SELECT * FROM users WHERE id = ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | const {DatabaseSync: Database} = require("node:sqlite"); const database = new Database(":memory:"); database.prepare(\`SELECT * FROM users WHERE id = ${id}\`);␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(8): const sqlite = require("node:sqlite"); const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT * FROM users WHERE id = ${id}`); + +> Input + + `␊ + 1 | const sqlite = require("node:sqlite"); const database = new sqlite.DatabaseSync(":memory:"); database.exec(\`SELECT * FROM users WHERE id = ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | const sqlite = require("node:sqlite"); const database = new sqlite.DatabaseSync(":memory:"); database.exec(\`SELECT * FROM users WHERE id = ${id}\`);␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(9): const database = new (require("node:sqlite").DatabaseSync)(":memory:"); database.prepare(`SELECT * FROM users WHERE id = ${id}`); + +> Input + + `␊ + 1 | const database = new (require("node:sqlite").DatabaseSync)(":memory:"); database.prepare(\`SELECT * FROM users WHERE id = ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | const database = new (require("node:sqlite").DatabaseSync)(":memory:"); database.prepare(\`SELECT * FROM users WHERE id = ${id}\`);␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(10): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const query = `SELECT * FROM users WHERE id = ${id}`; database.exec(query); + +> Input + + `␊ + 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const query = \`SELECT * FROM users WHERE id = ${id}\`; database.exec(query);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const query = \`SELECT * FROM users WHERE id = ${id}\`; database.exec(query);␊ + | ^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(11): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const query = `SELECT * FROM users WHERE id = ${id}`; const alias = query; database.prepare(alias); + +> Input + + `␊ + 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const query = \`SELECT * FROM users WHERE id = ${id}\`; const alias = query; database.prepare(alias);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const query = \`SELECT * FROM users WHERE id = ${id}\`; const alias = query; database.prepare(alias);␊ + | ^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(12): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database?.exec(`SELECT * FROM users WHERE id = ${id}`); + +> Input + + `␊ + 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database?.exec(\`SELECT * FROM users WHERE id = ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database?.exec(\`SELECT * FROM users WHERE id = ${id}\`);␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(13): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec?.(`SELECT * FROM users WHERE id = ${id}`); + +> Input + + `␊ + 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec?.(\`SELECT * FROM users WHERE id = ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec?.(\`SELECT * FROM users WHERE id = ${id}\`);␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(14): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database["prepare"](`SELECT * FROM users WHERE id = ${id}`); + +> Input + + `␊ + 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database["prepare"](\`SELECT * FROM users WHERE id = ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database["prepare"](\`SELECT * FROM users WHERE id = ${id}\`);␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(15): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${first} FROM users WHERE id = ${second}`); + +> Input + + `␊ + 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(\`SELECT ${first} FROM users WHERE id = ${second}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(\`SELECT ${first} FROM users WHERE id = ${second}\`);␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(16): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(/* keep */ `SELECT * FROM users WHERE id = ${id}`); + +> Input + + `␊ + 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(/* keep */ \`SELECT * FROM users WHERE id = ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(/* keep */ \`SELECT * FROM users WHERE id = ${id}\`);␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(17): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(String.raw`SELECT * FROM users WHERE id = ${id}`); + +> Input + + `␊ + 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(String.raw\`SELECT * FROM users WHERE id = ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(String.raw\`SELECT * FROM users WHERE id = ${id}\`);␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(18): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.prepare(String.raw`SELECT 1`); + +> Input + + `␊ + 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.prepare(String.raw\`SELECT 1\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.prepare(String.raw\`SELECT 1\`);␊ + | ^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(19): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const sql = `SELECT ${id}`; database.exec(sql); + +> Input + + `␊ + 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const sql = \`SELECT ${id}\`; database.exec(sql);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const sql = \`SELECT ${id}\`; database.exec(sql);␊ + | ^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(20): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const sql = `SELECT ${id}`; const alias = sql; database.prepare(alias); + +> Input + + `␊ + 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const sql = \`SELECT ${id}\`; const alias = sql; database.prepare(alias);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const sql = \`SELECT ${id}\`; const alias = sql; database.prepare(alias);␊ + | ^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(21): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const alias = database; alias.exec(`SELECT ${id}`); + +> Input + + `␊ + 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const alias = database; alias.exec(\`SELECT ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const alias = database; alias.exec(\`SELECT ${id}\`);␊ + | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(22): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database["exec"](`SELECT ${id}`); + +> Input + + `␊ + 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database["exec"](\`SELECT ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database["exec"](\`SELECT ${id}\`);␊ + | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(23): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const method = "exec"; database[method](`SELECT ${id}`); + +> Input + + `␊ + 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const method = "exec"; database[method](\`SELECT ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const method = "exec"; database[method](\`SELECT ${id}\`);␊ + | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(24): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` as string); + +> Input + + `␊ + 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\` as string);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\` as string);␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(25): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT * FROM users WHERE id = ${id}` as string); + +> Input + + `␊ + 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(\`SELECT * FROM users WHERE id = ${id}\` as string);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(\`SELECT * FROM users WHERE id = ${id}\` as string);␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(26): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database!.exec(`SELECT ${id}` satisfies string); + +> Input + + `␊ + 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database!.exec(\`SELECT ${id}\` satisfies string);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database!.exec(\`SELECT ${id}\` satisfies string);␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` diff --git a/test/snapshots/no-unsafe-sqlite-interpolation.js.snap b/test/snapshots/no-unsafe-sqlite-interpolation.js.snap new file mode 100644 index 0000000000000000000000000000000000000000..ec9930fa646b7f12500b4ad5c26454924e0844b4 GIT binary patch literal 1428 zcmV;F1#9|2RzV0aa0HB@h)KO;k$TAld>&q7o8^UOB*-1OE=P*Sq!W&bqtnop)xA z_OA6jGtX~lXWsFh)fLNWoacKl95B!HEVg;WwAT&GZ01XaGMKHl<9e{YWO&Ak!C7s~ zZtTLU>$D(eJ55&R9n17sZV?)e%{^!iu7e8LYy*aG%je21)^gmfa;~r#URY$CtdXzR zs;8>2z5}!H+VYvx(B;hK@a`McsKDWEv$(N!(*^+SY5_@A30DT>Zdx?-aZA~b4~jkFAhe;th%n_LTRoP z#Q$y(|M`gcgTceTO)(r1L(z6w+i=+gBs@fMIv7WS9u|a9e2rXOiUoxxc1Zcq0hL<; zDn}lpGTR#r|5xIVg#T?({%r9JO4p4MMs(6HgySi2xKTU`?xAg=fUbrdg* z^kt-f^NC1nX(ao|Ym3Cs0TRc>NR)>bR4Io=+I!0%C|f^`VM8AmLP++Jk*jea(Z~@! zE$XoeSG)@$HiI|JgoB}a#jBzAR4ZN`eUO8p42LfQ98QEdjP!9{JR+#m3({taJ(Qj+ zIQ$Bg3ZrfyvC9!rSE*WZu$9I5VOa5cNoL8*zr53B?iN%aUayZng!*@jqW%b_=YjM( zScYbVG~7de_UMXZSsIfJOcqTRQ~!iaNi8F%8!i7X!!ivH75{F*bFu%uh_!!FHviI; zwL~U`4E~3F{;fNM$qa-3Y(_O%%!UPwQdI3b)8hvxCtcf&%n@W~KN1QTvh^|2-ljkN zk<hCVk{aKiC`b2|W%l}4Q=khg z;wEdJ*bf;ZsG$cV5!|-ue=@q=5w#5X5oPQfB3=@S7P7H{bgxmfk<0)n=Ro?q(d97n zBsJ)>xn55XZFWZ|d}NJCSX_vZD9m#Wsa#S~L5Pw|onGl#4Qs{oQftNj}!qm0mw956W6{mJV9e7n7d>L{;QY#isj{SeU_jM*X2d-Rcs&nA#Rg>mg z#UA{;g4*u*o!;^JR=WqE+=0$}@G0DA!2=;D--Voz)OBAJK9IQKnS9k` Date: Thu, 23 Jul 2026 01:38:52 +0200 Subject: [PATCH 02/15] `no-unsafe-sqlite-interpolation`: Track immutable aliases and TypeScript wrappers --- docs/rules/no-unsafe-sqlite-interpolation.md | 2 + rules/no-unsafe-sqlite-interpolation.js | 30 ++-- test/no-unsafe-sqlite-interpolation.js | 26 +++ .../no-unsafe-sqlite-interpolation.js.md | 158 +++++++++++++++++- .../no-unsafe-sqlite-interpolation.js.snap | Bin 1428 -> 1780 bytes 5 files changed, 203 insertions(+), 13 deletions(-) diff --git a/docs/rules/no-unsafe-sqlite-interpolation.md b/docs/rules/no-unsafe-sqlite-interpolation.md index 20a422921d..92cf17b7da 100644 --- a/docs/rules/no-unsafe-sqlite-interpolation.md +++ b/docs/rules/no-unsafe-sqlite-interpolation.md @@ -25,6 +25,8 @@ const sql = database.createTagStore(); sql.get`SELECT * FROM users WHERE id = ${id}`; ``` +This rule only recognizes runtime `DatabaseSync` instances imported or required from exactly `node:sqlite`, including simple immutable `const` aliases, and only checks `exec()` and `prepare()`. It intentionally ignores string concatenation, generic query methods, dynamic imports, factory-created databases, and mutable assignment flows. + ## Examples ```js diff --git a/rules/no-unsafe-sqlite-interpolation.js b/rules/no-unsafe-sqlite-interpolation.js index 3f55c57eae..bebf731cf5 100644 --- a/rules/no-unsafe-sqlite-interpolation.js +++ b/rules/no-unsafe-sqlite-interpolation.js @@ -18,6 +18,7 @@ const transparentExpressionTypes = new Set([ 'TSSatisfiesExpression', 'TSNonNullExpression', 'TSTypeAssertion', + 'TSInstantiationExpression', ]); const databaseSyncEsmTraceMap = { @@ -84,6 +85,8 @@ const getVariableInitializer = (node, context) => { }; }; +const getImportSpecifierName = node => node.imported.type === 'Identifier' ? node.imported.name : node.imported.value; + const isDatabaseSyncImport = (node, context) => { const definition = getVariableDefinition(node, context); if ( @@ -96,8 +99,7 @@ const isDatabaseSyncImport = (node, context) => { } return definition.node.type === 'ImportSpecifier' - && definition.node.imported.type === 'Identifier' - && definition.node.imported.name === 'DatabaseSync'; + && getImportSpecifierName(definition.node) === 'DatabaseSync'; }; const isSqliteNamespaceImport = (node, context) => { @@ -115,8 +117,7 @@ const isSqliteNamespaceImport = (node, context) => { || definition.node.type === 'ImportDefaultSpecifier' || ( definition.node.type === 'ImportSpecifier' - && definition.node.imported.type === 'Identifier' - && definition.node.imported.name === 'default' + && getImportSpecifierName(definition.node) === 'default' ); }; @@ -157,11 +158,20 @@ const isSqliteNamespaceRequireBinding = (node, context) => { return getVariableDefinition(node, context).node.id.type === 'Identifier'; }; -const isDirectDatabaseSyncConstructor = (node, context) => { - const callee = unwrapExpression(node.callee); +const isDatabaseSyncConstructor = (node, context, seenVariables = new Set()) => { + const callee = unwrapExpression(node); if (callee.type === 'Identifier') { - return isDatabaseSyncImport(callee, context) - || isDatabaseSyncRequireBinding(callee, context); + if (isDatabaseSyncImport(callee, context) || isDatabaseSyncRequireBinding(callee, context)) { + return true; + } + + const variableInfo = getVariableInitializer(callee, context); + if (!variableInfo || seenVariables.has(variableInfo.variable)) { + return false; + } + + seenVariables.add(variableInfo.variable); + return isDatabaseSyncConstructor(variableInfo.initializer, context, seenVariables); } if (callee.type !== 'MemberExpression' || getPropertyName(callee, context.sourceCode.getScope(callee)) !== 'DatabaseSync') { @@ -181,7 +191,7 @@ const getDatabaseConstructors = (program, context) => { for (const reference of tracker.iterateEsmReferences(databaseSyncEsmTraceMap)) { if ( reference.type === ReferenceTracker.CONSTRUCT - && isDirectDatabaseSyncConstructor(reference.node, context) + && isDatabaseSyncConstructor(reference.node.callee, context) ) { databaseConstructors.add(reference.node); } @@ -190,7 +200,7 @@ const getDatabaseConstructors = (program, context) => { for (const reference of tracker.iterateCjsReferences(databaseSyncCjsTraceMap)) { if ( reference.type === ReferenceTracker.CONSTRUCT - && isDirectDatabaseSyncConstructor(reference.node, context) + && isDatabaseSyncConstructor(reference.node.callee, context) ) { databaseConstructors.add(reference.node); } diff --git a/test/no-unsafe-sqlite-interpolation.js b/test/no-unsafe-sqlite-interpolation.js index d88b074ee9..2345128d18 100644 --- a/test/no-unsafe-sqlite-interpolation.js +++ b/test/no-unsafe-sqlite-interpolation.js @@ -7,8 +7,13 @@ test.snapshot({ valid: [ 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec("SELECT 1");', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT 1`);', + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const sql = `SELECT 1`; database.prepare(sql);', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.prepare("SELECT ?").get(value);', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.run(`SELECT ${value}`);', + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.query(`SELECT ${value}`);', + 'const sqlite = require("other-sqlite"); const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${value}`);', + 'const {DatabaseSync} = require("other-sqlite"); const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${value}`);', + 'const database = new (require("other-sqlite").DatabaseSync)(":memory:"); database.exec(`SELECT ${value}`);', 'import {DatabaseSync} from "node:sqlite"; const sql = new DatabaseSync(":memory:").createTagStore(); sql.get`SELECT * FROM users WHERE id = ${value}`;', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec("SELECT " + value);', 'import {DatabaseSync} from "node:sqlite"; const database = createDatabase(); database.exec(`SELECT ${value}`);', @@ -56,10 +61,31 @@ test.snapshot({ 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const alias = database; alias.exec(`SELECT ${id}`);', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database["exec"](`SELECT ${id}`);', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const method = "exec"; database[method](`SELECT ${id}`);', + 'import {DatabaseSync} from "node:sqlite"; const Database = DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`);', + 'import {"DatabaseSync" as Database} from "node:sqlite"; const database = new Database(":memory:"); database.exec(`SELECT ${id}`);', + 'import {"default" as sqlite} from "node:sqlite"; const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`);', + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); database.prepare(`SELECT ${id}`);', + 'const {DatabaseSync} = require("node:sqlite"); const Database = DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`);', { code: 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` as string);', languageOptions: {parser: parsers.typescript}, }, + { + code: 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`);', + languageOptions: {parser: parsers.typescript}, + }, + { + code: 'import {DatabaseSync} from "node:sqlite"; const database = new (DatabaseSync)(":memory:"); database.exec(`SELECT ${id}`);', + languageOptions: {parser: parsers.typescript}, + }, + { + code: 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`);', + languageOptions: {parser: parsers.typescript}, + }, + { + code: 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`!);', + languageOptions: {parser: parsers.typescript}, + }, { code: 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT * FROM users WHERE id = ${id}` as string);', languageOptions: {parser: parsers.typescript}, diff --git a/test/snapshots/no-unsafe-sqlite-interpolation.js.md b/test/snapshots/no-unsafe-sqlite-interpolation.js.md index 98b8ca6aab..62341a7c95 100644 --- a/test/snapshots/no-unsafe-sqlite-interpolation.js.md +++ b/test/snapshots/no-unsafe-sqlite-interpolation.js.md @@ -372,7 +372,95 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(24): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` as string); +## invalid(24): import {DatabaseSync} from "node:sqlite"; const Database = DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`); + +> Input + + `␊ + 1 | import {DatabaseSync} from "node:sqlite"; const Database = DatabaseSync; const database = new Database(":memory:"); database.exec(\`SELECT ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; const Database = DatabaseSync; const database = new Database(":memory:"); database.exec(\`SELECT ${id}\`);␊ + | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(25): import {"DatabaseSync" as Database} from "node:sqlite"; const database = new Database(":memory:"); database.exec(`SELECT ${id}`); + +> Input + + `␊ + 1 | import {"DatabaseSync" as Database} from "node:sqlite"; const database = new Database(":memory:"); database.exec(\`SELECT ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {"DatabaseSync" as Database} from "node:sqlite"; const database = new Database(":memory:"); database.exec(\`SELECT ${id}\`);␊ + | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(26): import {"default" as sqlite} from "node:sqlite"; const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); + +> Input + + `␊ + 1 | import {"default" as sqlite} from "node:sqlite"; const database = new sqlite.DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {"default" as sqlite} from "node:sqlite"; const database = new sqlite.DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\`);␊ + | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(27): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); database.prepare(`SELECT ${id}`); + +> Input + + `␊ + 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\`); database.prepare(\`SELECT ${id}\`);␊ + ` + +> Error 1/2 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\`); database.prepare(\`SELECT ${id}\`);␊ + | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +> Error 2/2 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\`); database.prepare(\`SELECT ${id}\`);␊ + | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(28): const {DatabaseSync} = require("node:sqlite"); const Database = DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`); + +> Input + + `␊ + 1 | const {DatabaseSync} = require("node:sqlite"); const Database = DatabaseSync; const database = new Database(":memory:"); database.exec(\`SELECT ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | const {DatabaseSync} = require("node:sqlite"); const Database = DatabaseSync; const database = new Database(":memory:"); database.exec(\`SELECT ${id}\`);␊ + | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(29): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` as string); > Input @@ -388,7 +476,71 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(25): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT * FROM users WHERE id = ${id}` as string); +## invalid(30): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); + +> Input + + `␊ + 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\`);␊ + | ^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(31): import {DatabaseSync} from "node:sqlite"; const database = new (DatabaseSync)(":memory:"); database.exec(`SELECT ${id}`); + +> Input + + `␊ + 1 | import {DatabaseSync} from "node:sqlite"; const database = new (DatabaseSync)(":memory:"); database.exec(\`SELECT ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; const database = new (DatabaseSync)(":memory:"); database.exec(\`SELECT ${id}\`);␊ + | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(32): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); + +> Input + + `␊ + 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\`);␊ + | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(33): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`!); + +> Input + + `␊ + 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\`!);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\`!);␊ + | ^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(34): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT * FROM users WHERE id = ${id}` as string); > Input @@ -404,7 +556,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(26): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database!.exec(`SELECT ${id}` satisfies string); +## invalid(35): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database!.exec(`SELECT ${id}` satisfies string); > Input diff --git a/test/snapshots/no-unsafe-sqlite-interpolation.js.snap b/test/snapshots/no-unsafe-sqlite-interpolation.js.snap index ec9930fa646b7f12500b4ad5c26454924e0844b4..1aa74bcd31164d7e0c3963dcee788add1748f121 100644 GIT binary patch literal 1780 zcmVBDOj*wKS#RU%ZR(r8MHjD|_Pk-4m3*;KCgxn#aecBpYWT*K;n9gD zdwP}3xXv8O*-nL)y_#kEG&fA99ozFsW#@NfgxGX}gfGkI%5!wiahJ-u!Z7<`KV78L z`N@f~<6|$pLHfvx<0ns$x<_4)y!rCj_!u!OWP}`8HY=->g<;dPjjA{6_}=;LO5-oy zy7PF~lUJZ!UiD~=PtvZ(BQ!;N@b?P-fa>!(;pC-w~>&J=byQY1{>)#m> zbIf%emlOwzf&K3U_Ad$SPYjRsZGzzt3`NzYRl}tzNO%P7wB3mWJwo6@@fC>T<490w z5)LXKI-s%@pfb3NN?&6zTK^J#BwFA0%fDMZ0Pecp#)wYZxo}(r^5;cxXcY%K70Ph9 z8Q^em7YBKxkp6~o54dTw4F-Lbb5ZyRh<8OOXcPr92+A-x7hv#gto)(Vk~QtLe>rTH z?v>a%LU!7?UVaI9c_>99kfG4*?W)X?w*p5VDwKDyZ|lnu;?i2(bZH)RXhB(8`xCIl zjbLupQ9ReuYe4_v9!qN(NcNW37K!@-5{LOnltT$s!eNoNZut#(>&Fgk=o1AOl1&iE zak%N?-0XgXN4JaFjQx}8fs6J@#+`{IvC1uSPyVG!fZkcR@TGc*U_S1CtC)7EKmYe>0|}mXVWd zwmt;ftN1T}#0`R=@}g`l_#I>OCW&v@p(uT2{IE(xwy-?h*#duq=I!|dB$`-e^odj9-VgV z^zR4i5kh9~2Wk=yDkjkY!D8hQ9xDTVanz5M1R?0mN|JC;S&5B}%-7C~ zhw)gMXsYUWjfLc>{0dBV8yS`O5pYSl0)jZNij)M=pn{^oJ%yDVi$f@!1dk2(4o#7Z z!!_WfPw;T)ER^f|_kh;d6u5pL(WvGY_*;U9zhNFeBEL4`)vVAnbbh_ct64lRZvlUN zfmG*+EST$Q2WXzeu31DEkL_(?ljHH9*)}?anmWJNVLiyJS#fH7zXKoLQ|kQ~fpD2< z<5&+S;six>VTIph-6u8~LkKnWU?hUehFhPsKkg8v0pA0UeT~FREYVyx7J%+qY&PNr zK$LSJeQmVHJdaX?K9y_qbm(EX*x_wCB4TktAd&0m37~RRMFl}57u&tky~=9EC8@RI z*3ajtKkGD7|5pEoI=uBw6bob5ijykWt5%#i3T(rx;(+anC?%;CkM52`_>yG1SR$@g z?gKA8d-t`FzJ{hE_=UP(mj3-&}PSDI)~gnHFP zjj^Ts_)F&FaP2l%p)*F^@^iZ5EVD0t#1ug2V@gvad>3r&FP??ZLK}oZEx9;Pxf0Hv(rGZi~U;V)5 ztD&Tam#9dvegOvCN_BpTqAY72gz!N&N0|JNQmtk~%(tZ0(mVBebB)&2nIPX%rZiQ) zrA{@`;e96wKK_)Qr75*u4*U7JmaL=u&*xWxG1ib>EYivK@M$3RRaG7qYSzHPGUwsJ z3~JT+@O42)ijJ|!=DC+Lr7Gs(3{<1KSmbA^^Ds*Nx!JqH2t;tP(s@{@Q{#D9tXYYJ zrTh0H^Do~1<+i@<^1J?Cf}ano8F8`J8^LfSn3KI7f}^+Ij8~AQ}`T2 WR*&lY{fwWX&;J9~zg(GoY5)L`OND;` literal 1428 zcmV;F1#9|2RzV0aa0HB@h)KO;k$TAld>&q7o8^UOB*-1OE=P*Sq!W&bqtnop)xA z_OA6jGtX~lXWsFh)fLNWoacKl95B!HEVg;WwAT&GZ01XaGMKHl<9e{YWO&Ak!C7s~ zZtTLU>$D(eJ55&R9n17sZV?)e%{^!iu7e8LYy*aG%je21)^gmfa;~r#URY$CtdXzR zs;8>2z5}!H+VYvx(B;hK@a`McsKDWEv$(N!(*^+SY5_@A30DT>Zdx?-aZA~b4~jkFAhe;th%n_LTRoP z#Q$y(|M`gcgTceTO)(r1L(z6w+i=+gBs@fMIv7WS9u|a9e2rXOiUoxxc1Zcq0hL<; zDn}lpGTR#r|5xIVg#T?({%r9JO4p4MMs(6HgySi2xKTU`?xAg=fUbrdg* z^kt-f^NC1nX(ao|Ym3Cs0TRc>NR)>bR4Io=+I!0%C|f^`VM8AmLP++Jk*jea(Z~@! zE$XoeSG)@$HiI|JgoB}a#jBzAR4ZN`eUO8p42LfQ98QEdjP!9{JR+#m3({taJ(Qj+ zIQ$Bg3ZrfyvC9!rSE*WZu$9I5VOa5cNoL8*zr53B?iN%aUayZng!*@jqW%b_=YjM( zScYbVG~7de_UMXZSsIfJOcqTRQ~!iaNi8F%8!i7X!!ivH75{F*bFu%uh_!!FHviI; zwL~U`4E~3F{;fNM$qa-3Y(_O%%!UPwQdI3b)8hvxCtcf&%n@W~KN1QTvh^|2-ljkN zk<hCVk{aKiC`b2|W%l}4Q=khg z;wEdJ*bf;ZsG$cV5!|-ue=@q=5w#5X5oPQfB3=@S7P7H{bgxmfk<0)n=Ro?q(d97n zBsJ)>xn55XZFWZ|d}NJCSX_vZD9m#Wsa#S~L5Pw|onGl#4Qs{oQftNj}!qm0mw956W6{mJV9e7n7d>L{;QY#isj{SeU_jM*X2d-Rcs&nA#Rg>mg z#UA{;g4*u*o!;^JR=WqE+=0$}@G0DA!2=;D--Voz)OBAJK9IQKnS9k` Date: Thu, 23 Jul 2026 01:58:57 +0200 Subject: [PATCH 03/15] `no-unsafe-sqlite-interpolation`: Improve provenance tracking --- docs/rules/no-unsafe-sqlite-interpolation.md | 14 +- rules/no-unsafe-sqlite-interpolation.js | 173 ++++++++---------- test/no-unsafe-sqlite-interpolation.js | 17 +- .../no-unsafe-sqlite-interpolation.js.md | 122 +++++++++--- .../no-unsafe-sqlite-interpolation.js.snap | Bin 1780 -> 1909 bytes 5 files changed, 207 insertions(+), 119 deletions(-) diff --git a/docs/rules/no-unsafe-sqlite-interpolation.md b/docs/rules/no-unsafe-sqlite-interpolation.md index 92cf17b7da..767d2e6241 100644 --- a/docs/rules/no-unsafe-sqlite-interpolation.md +++ b/docs/rules/no-unsafe-sqlite-interpolation.md @@ -25,21 +25,33 @@ const sql = database.createTagStore(); sql.get`SELECT * FROM users WHERE id = ${id}`; ``` -This rule only recognizes runtime `DatabaseSync` instances imported or required from exactly `node:sqlite`, including simple immutable `const` aliases, and only checks `exec()` and `prepare()`. It intentionally ignores string concatenation, generic query methods, dynamic imports, factory-created databases, and mutable assignment flows. +This rule only recognizes runtime `DatabaseSync` instances imported or required from exactly `node:sqlite`, including simple immutable `const` aliases of constructors, namespaces, and instances, and only checks `exec()` and `prepare()`. It intentionally ignores string concatenation, generic query methods, dynamic imports, factory-created databases, and mutable assignment flows. ## Examples ```js +import {DatabaseSync} from 'node:sqlite'; + +const database = new DatabaseSync(':memory:'); + // ❌ database.exec(`SELECT * FROM users WHERE id = ${id}`); ``` ```js +import {DatabaseSync} from 'node:sqlite'; + +const database = new DatabaseSync(':memory:'); + // ❌ database.prepare(`SELECT * FROM users WHERE id = ${id}`); ``` ```js +import {DatabaseSync} from 'node:sqlite'; + +const database = new DatabaseSync(':memory:'); + // βœ… database.exec('CREATE TABLE users (id INTEGER)'); ``` diff --git a/rules/no-unsafe-sqlite-interpolation.js b/rules/no-unsafe-sqlite-interpolation.js index bebf731cf5..61be4cdefb 100644 --- a/rules/no-unsafe-sqlite-interpolation.js +++ b/rules/no-unsafe-sqlite-interpolation.js @@ -1,6 +1,6 @@ -import {findVariable, getPropertyName, ReferenceTracker} from '@eslint-community/eslint-utils'; +import {findVariable, getPropertyName} from '@eslint-community/eslint-utils'; import {isStaticRequire} from './ast/index.js'; -import {getConstVariableInitializer, isRuntimeImportSpecifier} from './utils/index.js'; +import {isGlobalIdentifier, isRuntimeImportSpecifier, isTypeScriptExpressionWrapper} from './utils/index.js'; const MESSAGE_ID = 'no-unsafe-sqlite-interpolation'; const messages = { @@ -12,46 +12,15 @@ const databaseMethods = new Set([ 'prepare', ]); -const transparentExpressionTypes = new Set([ - 'ChainExpression', - 'TSAsExpression', - 'TSSatisfiesExpression', - 'TSNonNullExpression', - 'TSTypeAssertion', - 'TSInstantiationExpression', -]); - -const databaseSyncEsmTraceMap = { - 'node:sqlite': { - [ReferenceTracker.ESM]: true, - default: { - DatabaseSync: { - [ReferenceTracker.CONSTRUCT]: true, - }, - }, - DatabaseSync: { - [ReferenceTracker.CONSTRUCT]: true, - }, - }, -}; - -const databaseSyncCjsTraceMap = { - 'node:sqlite': { - DatabaseSync: { - [ReferenceTracker.CONSTRUCT]: true, - }, - }, -}; - const unwrapExpression = node => { - while (node && transparentExpressionTypes.has(node.type)) { + while (node && (node.type === 'ChainExpression' || node.type === 'TSInstantiationExpression' || isTypeScriptExpressionWrapper(node))) { node = node.expression; } return node; }; -const getVariableDefinition = (node, context) => { +const getVariableInfo = (node, context) => { if (node.type !== 'Identifier') { return; } @@ -61,34 +30,40 @@ const getVariableDefinition = (node, context) => { return; } - return variable.defs[0]; + return { + variable, + definition: variable.defs[0], + }; }; -const getVariableInitializer = (node, context) => { - const definition = getVariableDefinition(node, context); +const getConstVariableInfo = (node, context) => { + const variableInfo = getVariableInfo(node, context); + const definition = variableInfo?.definition; if ( - definition?.type !== 'Variable' + !variableInfo + || definition.type !== 'Variable' || definition.node.type !== 'VariableDeclarator' - || definition.node.id.type !== 'Identifier' + || definition.parent.type !== 'VariableDeclaration' + || definition.parent.kind !== 'const' ) { return; } - const initializer = getConstVariableInitializer(node, context); - if (!initializer) { - return; - } - return { - variable: findVariable(context.sourceCode.getScope(node), node), - initializer, + ...variableInfo, + initializer: definition.node.init, }; }; +const getSimpleConstVariableInfo = (node, context) => { + const variableInfo = getConstVariableInfo(node, context); + return variableInfo?.definition.node.id.type === 'Identifier' ? variableInfo : undefined; +}; + const getImportSpecifierName = node => node.imported.type === 'Identifier' ? node.imported.name : node.imported.value; const isDatabaseSyncImport = (node, context) => { - const definition = getVariableDefinition(node, context); + const definition = getVariableInfo(node, context)?.definition; if ( !definition || definition.type !== 'ImportBinding' @@ -103,7 +78,7 @@ const isDatabaseSyncImport = (node, context) => { }; const isSqliteNamespaceImport = (node, context) => { - const definition = getVariableDefinition(node, context); + const definition = getVariableInfo(node, context)?.definition; if ( !definition || definition.type !== 'ImportBinding' @@ -121,25 +96,29 @@ const isSqliteNamespaceImport = (node, context) => { ); }; -const isConstRequireBinding = (node, context) => { - const definition = getVariableDefinition(node, context); - return Boolean( - definition - && definition.type === 'Variable' - && definition.node.type === 'VariableDeclarator' - && definition.parent.type === 'VariableDeclaration' - && definition.parent.kind === 'const' - && isStaticRequire(definition.node.init), - ); +const isNodeSqliteRequire = (node, context) => { + const requireCall = unwrapExpression(node); + return isStaticRequire(requireCall) + && isGlobalIdentifier(requireCall.callee, context) + && requireCall.arguments[0].value === 'node:sqlite'; +}; + +const getNodeSqliteRequireBinding = (node, context) => { + const variableInfo = getConstVariableInfo(node, context); + if (!variableInfo || !isNodeSqliteRequire(variableInfo.initializer, context)) { + return; + } + + return variableInfo; }; const isDatabaseSyncRequireBinding = (node, context) => { - if (!isConstRequireBinding(node, context)) { + const variableInfo = getNodeSqliteRequireBinding(node, context); + if (!variableInfo) { return false; } - const definition = getVariableDefinition(node, context); - const variable = findVariable(context.sourceCode.getScope(node), node); + const {definition, variable} = variableInfo; const {id} = definition.node; return id.type === 'ObjectPattern' && id.properties.some(property => @@ -150,13 +129,7 @@ const isDatabaseSyncRequireBinding = (node, context) => { ); }; -const isSqliteNamespaceRequireBinding = (node, context) => { - if (!isConstRequireBinding(node, context)) { - return false; - } - - return getVariableDefinition(node, context).node.id.type === 'Identifier'; -}; +const isSqliteNamespaceRequireBinding = (node, context) => getNodeSqliteRequireBinding(node, context)?.definition.node.id.type === 'Identifier'; const isDatabaseSyncConstructor = (node, context, seenVariables = new Set()) => { const callee = unwrapExpression(node); @@ -165,7 +138,7 @@ const isDatabaseSyncConstructor = (node, context, seenVariables = new Set()) => return true; } - const variableInfo = getVariableInitializer(callee, context); + const variableInfo = getSimpleConstVariableInfo(callee, context); if (!variableInfo || seenVariables.has(variableInfo.variable)) { return false; } @@ -178,31 +151,38 @@ const isDatabaseSyncConstructor = (node, context, seenVariables = new Set()) => return false; } - const object = unwrapExpression(callee.object); - return isSqliteNamespaceImport(object, context) - || isSqliteNamespaceRequireBinding(object, context) - || isStaticRequire(object); + return isSqliteNamespace(callee.object, context); }; -const getDatabaseConstructors = (program, context) => { - const tracker = new ReferenceTracker(context.sourceCode.getScope(program)); - const databaseConstructors = new WeakSet(); +const isSqliteNamespace = (node, context, seenVariables = new Set()) => { + node = unwrapExpression(node); + if (isNodeSqliteRequire(node, context)) { + return true; + } - for (const reference of tracker.iterateEsmReferences(databaseSyncEsmTraceMap)) { - if ( - reference.type === ReferenceTracker.CONSTRUCT - && isDatabaseSyncConstructor(reference.node.callee, context) - ) { - databaseConstructors.add(reference.node); - } + if (node?.type !== 'Identifier') { + return false; } - for (const reference of tracker.iterateCjsReferences(databaseSyncCjsTraceMap)) { - if ( - reference.type === ReferenceTracker.CONSTRUCT - && isDatabaseSyncConstructor(reference.node.callee, context) - ) { - databaseConstructors.add(reference.node); + if (isSqliteNamespaceImport(node, context) || isSqliteNamespaceRequireBinding(node, context)) { + return true; + } + + const variableInfo = getSimpleConstVariableInfo(node, context); + if (!variableInfo || seenVariables.has(variableInfo.variable)) { + return false; + } + + seenVariables.add(variableInfo.variable); + return isSqliteNamespace(variableInfo.initializer, context, seenVariables); +}; + +const getDatabaseConstructors = (newExpressions, context) => { + const databaseConstructors = new WeakSet(); + + for (const newExpression of newExpressions) { + if (isDatabaseSyncConstructor(newExpression.callee, context)) { + databaseConstructors.add(newExpression); } } @@ -226,7 +206,7 @@ const createDatabaseInstanceChecker = (databaseConstructors, context) => { return false; } - const variableInfo = getVariableInitializer(node, context); + const variableInfo = getSimpleConstVariableInfo(node, context); if (!variableInfo) { return false; } @@ -271,7 +251,7 @@ const createUnsafeSqlArgumentChecker = context => { return false; } - const variableInfo = getVariableInitializer(node, context); + const variableInfo = getSimpleConstVariableInfo(node, context); if (!variableInfo) { return false; } @@ -320,13 +300,18 @@ const getProblem = (callExpression, context, isDatabaseInstance, isUnsafeSqlArgu /** @param {import('eslint').Rule.RuleContext} context */ const create = context => { const callExpressions = []; + const newExpressions = []; context.on('CallExpression', node => { callExpressions.push(node); }); - context.onExit('Program', function * (program) { - const databaseConstructors = getDatabaseConstructors(program, context); + context.on('NewExpression', node => { + newExpressions.push(node); + }); + + context.onExit('Program', function * () { + const databaseConstructors = getDatabaseConstructors(newExpressions, context); const isDatabaseInstance = createDatabaseInstanceChecker(databaseConstructors, context); const isUnsafeSqlArgument = createUnsafeSqlArgumentChecker(context); diff --git a/test/no-unsafe-sqlite-interpolation.js b/test/no-unsafe-sqlite-interpolation.js index 2345128d18..ce7bc88449 100644 --- a/test/no-unsafe-sqlite-interpolation.js +++ b/test/no-unsafe-sqlite-interpolation.js @@ -8,12 +8,15 @@ test.snapshot({ 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec("SELECT 1");', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT 1`);', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const sql = `SELECT 1`; database.prepare(sql);', + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const sql = `SELECT ${value}`; let alias = sql; database.prepare(alias);', + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const sql = `SELECT ${value}`; let alias; alias = sql; database.prepare(alias);', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.prepare("SELECT ?").get(value);', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.run(`SELECT ${value}`);', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.query(`SELECT ${value}`);', 'const sqlite = require("other-sqlite"); const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${value}`);', 'const {DatabaseSync} = require("other-sqlite"); const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${value}`);', 'const database = new (require("other-sqlite").DatabaseSync)(":memory:"); database.exec(`SELECT ${value}`);', + 'const require = () => ({DatabaseSync: class {}}); const database = new (require("node:sqlite").DatabaseSync)(":memory:"); database.exec(`SELECT ${value}`);', 'import {DatabaseSync} from "node:sqlite"; const sql = new DatabaseSync(":memory:").createTagStore(); sql.get`SELECT * FROM users WHERE id = ${value}`;', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec("SELECT " + value);', 'import {DatabaseSync} from "node:sqlite"; const database = createDatabase(); database.exec(`SELECT ${value}`);', @@ -47,7 +50,7 @@ test.snapshot({ 'const {DatabaseSync: Database} = require("node:sqlite"); const database = new Database(":memory:"); database.prepare(`SELECT * FROM users WHERE id = ${id}`);', 'const sqlite = require("node:sqlite"); const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT * FROM users WHERE id = ${id}`);', 'const database = new (require("node:sqlite").DatabaseSync)(":memory:"); database.prepare(`SELECT * FROM users WHERE id = ${id}`);', - 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const query = `SELECT * FROM users WHERE id = ${id}`; database.exec(query);', + 'const Database = require("node:sqlite").DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`);', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const query = `SELECT * FROM users WHERE id = ${id}`; const alias = query; database.prepare(alias);', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database?.exec(`SELECT * FROM users WHERE id = ${id}`);', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec?.(`SELECT * FROM users WHERE id = ${id}`);', @@ -62,10 +65,18 @@ test.snapshot({ 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database["exec"](`SELECT ${id}`);', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const method = "exec"; database[method](`SELECT ${id}`);', 'import {DatabaseSync} from "node:sqlite"; const Database = DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`);', + 'import * as sqlite from "node:sqlite"; const alias = sqlite; const database = new alias.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`);', + 'const sqlite = require("node:sqlite"); const alias = sqlite; const database = new alias.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`);', + 'import * as sqlite from "node:sqlite"; const Database = sqlite.DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`);', + 'const sqlite = require("node:sqlite"); const Database = sqlite.DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`);', 'import {"DatabaseSync" as Database} from "node:sqlite"; const database = new Database(":memory:"); database.exec(`SELECT ${id}`);', 'import {"default" as sqlite} from "node:sqlite"; const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`);', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); database.prepare(`SELECT ${id}`);', 'const {DatabaseSync} = require("node:sqlite"); const Database = DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`);', + { + code: 'const sqlite = require("node:sqlite") as typeof import("node:sqlite"); const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`);', + languageOptions: {parser: parsers.typescript}, + }, { code: 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` as string);', languageOptions: {parser: parsers.typescript}, @@ -87,11 +98,11 @@ test.snapshot({ languageOptions: {parser: parsers.typescript}, }, { - code: 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT * FROM users WHERE id = ${id}` as string);', + code: 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database!.exec(`SELECT ${id}`);', languageOptions: {parser: parsers.typescript}, }, { - code: 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database!.exec(`SELECT ${id}` satisfies string);', + code: 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` satisfies string);', languageOptions: {parser: parsers.typescript}, }, ], diff --git a/test/snapshots/no-unsafe-sqlite-interpolation.js.md b/test/snapshots/no-unsafe-sqlite-interpolation.js.md index 62341a7c95..02090d93e8 100644 --- a/test/snapshots/no-unsafe-sqlite-interpolation.js.md +++ b/test/snapshots/no-unsafe-sqlite-interpolation.js.md @@ -148,20 +148,20 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(10): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const query = `SELECT * FROM users WHERE id = ${id}`; database.exec(query); +## invalid(10): const Database = require("node:sqlite").DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`); > Input `␊ - 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const query = \`SELECT * FROM users WHERE id = ${id}\`; database.exec(query);␊ + 1 | const Database = require("node:sqlite").DatabaseSync; const database = new Database(":memory:"); database.exec(\`SELECT ${id}\`);␊ ` > Error 1/1 `␊ Message:␊ - > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const query = \`SELECT * FROM users WHERE id = ${id}\`; database.exec(query);␊ - | ^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + > 1 | const Database = require("node:sqlite").DatabaseSync; const database = new Database(":memory:"); database.exec(\`SELECT ${id}\`);␊ + | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` ## invalid(11): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const query = `SELECT * FROM users WHERE id = ${id}`; const alias = query; database.prepare(alias); @@ -388,7 +388,71 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(25): import {"DatabaseSync" as Database} from "node:sqlite"; const database = new Database(":memory:"); database.exec(`SELECT ${id}`); +## invalid(25): import * as sqlite from "node:sqlite"; const alias = sqlite; const database = new alias.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); + +> Input + + `␊ + 1 | import * as sqlite from "node:sqlite"; const alias = sqlite; const database = new alias.DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import * as sqlite from "node:sqlite"; const alias = sqlite; const database = new alias.DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\`);␊ + | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(26): const sqlite = require("node:sqlite"); const alias = sqlite; const database = new alias.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); + +> Input + + `␊ + 1 | const sqlite = require("node:sqlite"); const alias = sqlite; const database = new alias.DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | const sqlite = require("node:sqlite"); const alias = sqlite; const database = new alias.DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\`);␊ + | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(27): import * as sqlite from "node:sqlite"; const Database = sqlite.DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`); + +> Input + + `␊ + 1 | import * as sqlite from "node:sqlite"; const Database = sqlite.DatabaseSync; const database = new Database(":memory:"); database.exec(\`SELECT ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import * as sqlite from "node:sqlite"; const Database = sqlite.DatabaseSync; const database = new Database(":memory:"); database.exec(\`SELECT ${id}\`);␊ + | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(28): const sqlite = require("node:sqlite"); const Database = sqlite.DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`); + +> Input + + `␊ + 1 | const sqlite = require("node:sqlite"); const Database = sqlite.DatabaseSync; const database = new Database(":memory:"); database.exec(\`SELECT ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | const sqlite = require("node:sqlite"); const Database = sqlite.DatabaseSync; const database = new Database(":memory:"); database.exec(\`SELECT ${id}\`);␊ + | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(29): import {"DatabaseSync" as Database} from "node:sqlite"; const database = new Database(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -404,7 +468,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(26): import {"default" as sqlite} from "node:sqlite"; const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); +## invalid(30): import {"default" as sqlite} from "node:sqlite"; const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -420,7 +484,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(27): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); database.prepare(`SELECT ${id}`); +## invalid(31): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); database.prepare(`SELECT ${id}`); > Input @@ -444,7 +508,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(28): const {DatabaseSync} = require("node:sqlite"); const Database = DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`); +## invalid(32): const {DatabaseSync} = require("node:sqlite"); const Database = DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -460,7 +524,23 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(29): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` as string); +## invalid(33): const sqlite = require("node:sqlite") as typeof import("node:sqlite"); const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); + +> Input + + `␊ + 1 | const sqlite = require("node:sqlite") as typeof import("node:sqlite"); const database = new sqlite.DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | const sqlite = require("node:sqlite") as typeof import("node:sqlite"); const database = new sqlite.DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\`);␊ + | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(34): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` as string); > Input @@ -476,7 +556,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(30): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); +## invalid(35): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -492,7 +572,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(31): import {DatabaseSync} from "node:sqlite"; const database = new (DatabaseSync)(":memory:"); database.exec(`SELECT ${id}`); +## invalid(36): import {DatabaseSync} from "node:sqlite"; const database = new (DatabaseSync)(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -508,7 +588,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(32): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); +## invalid(37): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -524,7 +604,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(33): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`!); +## invalid(38): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`!); > Input @@ -540,34 +620,34 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(34): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT * FROM users WHERE id = ${id}` as string); +## invalid(39): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database!.exec(`SELECT ${id}`); > Input `␊ - 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(\`SELECT * FROM users WHERE id = ${id}\` as string);␊ + 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database!.exec(\`SELECT ${id}\`);␊ ` > Error 1/1 `␊ Message:␊ - > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(\`SELECT * FROM users WHERE id = ${id}\` as string);␊ - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database!.exec(\`SELECT ${id}\`);␊ + | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(35): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database!.exec(`SELECT ${id}` satisfies string); +## invalid(40): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` satisfies string); > Input `␊ - 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database!.exec(\`SELECT ${id}\` satisfies string);␊ + 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\` satisfies string);␊ ` > Error 1/1 `␊ Message:␊ - > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database!.exec(\`SELECT ${id}\` satisfies string);␊ - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\` satisfies string);␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` diff --git a/test/snapshots/no-unsafe-sqlite-interpolation.js.snap b/test/snapshots/no-unsafe-sqlite-interpolation.js.snap index 1aa74bcd31164d7e0c3963dcee788add1748f121..96cc667cc930706e2b019c8dfb3685552b805821 100644 GIT binary patch literal 1909 zcmV-*2a5PXRzV|MxB zk^2|F8Xt=Y00000000B!oLf^IMHI(D%d(0$zd^H=hg}{ZyAU9YBvuJtsuH7Mm6ueM zfo;N6hMi?*xCv-XjF>3iW58T6L=zH?rIuBG0Uz|)2fvQBEYr+^ZZgv|eY!2?k)5Ib zr~Ciw+v)DxCWq-=C1Dfk2V{X+CNM-vy(od>%mP9|x=gO0G()H%cxk4X%v7OG*vHZyJ z;EBO!UnOnixuH`h$+S;BpSV6< zO;0w}pXMi;o_g$BQwt$QvS^y_7V21|)S)j{YtunP`5VnkKzg;gnts|95XTo zLI_zTNY7ib9x~vPsv8iy8c?t1I#xi*jO9#Izy2qej6}+5gbcrOg7|@FSI_$G+cjbj zdYB?L0v7R?=Uujo7Q&0V2*Mw3hx8)o(KhlqCf^g8wTe?42~q4KkPSTP222WiJGN( zG4UKBJ8fJqKM%azn_?o6q0sE@s?Cw#g^uhll((_pRhIq4qf^tiNAsXS3)-f&KLShK z2-{{8#d9sa4D`?Kv9ytaWN&$6k+>fs(Zffg9GOr_R*SN6%kRKjKkUPXIZ<#SSp$Jw zi35p25#iIK9~(B~?Pb_(k1!KfhVB`!f!d{Jye7th4u&=yRze((G8}f5ah_izpwJ7- zdWu!xp3Bhu8kKUbZUM1F0;}s(t=QPwZ2Sq!cpYXw9@r`87)31C<$`8K?BZ{LF_xi= zwdv$~_zaNxlEB0IG%I#+9A7K&@88V7N>jFt*QchbH&6OWyjCOp%5rSWC;g4DIc?C^ z?>zJW1040I^87ABkX&;91#$j`mkh=TSPOR9JgS&w$%3LN&zxzh|9IMY#6958RkVE; z359dndJkx?;cv7^8U#V*MccUR7Gt0jZL87oc4ZkebCjpam>CoWE(ZEI*kBxVVQ_Fd zx7|PI&ZJ=BCt#?%4^ojJGoh7>dyI=@spL$uMTZu~ZO;#ugcah` zF}Iq2FP<48WVRR2kZ@2ji3bQ4E8TdkbhMFkl-5XFN6d+%d8|l;U@|K*;h?gT7#o#a zyo)_}tPF3dYWJ*Jc~pJ@CcBM{O7aM}qAhkv3Mb*R;ohn# za&fo@ob(YM4*Lt`y8dmT^<@pNe}rgMa|`^fz{6LWhmWeyjrdbeWDPxBD~IilKjrYe zybk>FIZ~b@wqUNOU7&dcJ7*DJJhr!mj~b6ZWJlHx>+(3v2l-P@k{sV3z(<=}y`LZu zE)%;rHiL;IK@n3};XkVSi8aO$LJmEwiSV<&#$V!Y2)P41_B9eOi9~bRm<75gu-Ql! z0MX8Y^0Cn}^E^rp`gE>d(vih(vBP)G5fPIM0*PEd4+E70Ix+|%x!CSi?p2m6E-B3w zH(sBl!Gv3%`ZvlqQs9kW;#e3vS1haCtX#2l6xfE>#Q{5kP?E_NM|NJKZC||)y!5Rs z2q2t!H6XbTf-vk9Lc{zgE%Qt{MR`z&Vr&_L`Z6Nqfv7fan z*W_swU$cFhof@v94oi4%rd;gxi$?6zOdf$*yL(bcgUW;|3QC6Umo>c}9{s4PsZvKN zY_Gm&?Nv$kqCp)AnxBEe)>GXzs3}Xc0z!BHb literal 1780 zcmVBDOj*wKS#RU%ZR(r8MHjD|_Pk-4m3*;KCgxn#aecBpYWT*K;n9gD zdwP}3xXv8O*-nL)y_#kEG&fA99ozFsW#@NfgxGX}gfGkI%5!wiahJ-u!Z7<`KV78L z`N@f~<6|$pLHfvx<0ns$x<_4)y!rCj_!u!OWP}`8HY=->g<;dPjjA{6_}=;LO5-oy zy7PF~lUJZ!UiD~=PtvZ(BQ!;N@b?P-fa>!(;pC-w~>&J=byQY1{>)#m> zbIf%emlOwzf&K3U_Ad$SPYjRsZGzzt3`NzYRl}tzNO%P7wB3mWJwo6@@fC>T<490w z5)LXKI-s%@pfb3NN?&6zTK^J#BwFA0%fDMZ0Pecp#)wYZxo}(r^5;cxXcY%K70Ph9 z8Q^em7YBKxkp6~o54dTw4F-Lbb5ZyRh<8OOXcPr92+A-x7hv#gto)(Vk~QtLe>rTH z?v>a%LU!7?UVaI9c_>99kfG4*?W)X?w*p5VDwKDyZ|lnu;?i2(bZH)RXhB(8`xCIl zjbLupQ9ReuYe4_v9!qN(NcNW37K!@-5{LOnltT$s!eNoNZut#(>&Fgk=o1AOl1&iE zak%N?-0XgXN4JaFjQx}8fs6J@#+`{IvC1uSPyVG!fZkcR@TGc*U_S1CtC)7EKmYe>0|}mXVWd zwmt;ftN1T}#0`R=@}g`l_#I>OCW&v@p(uT2{IE(xwy-?h*#duq=I!|dB$`-e^odj9-VgV z^zR4i5kh9~2Wk=yDkjkY!D8hQ9xDTVanz5M1R?0mN|JC;S&5B}%-7C~ zhw)gMXsYUWjfLc>{0dBV8yS`O5pYSl0)jZNij)M=pn{^oJ%yDVi$f@!1dk2(4o#7Z z!!_WfPw;T)ER^f|_kh;d6u5pL(WvGY_*;U9zhNFeBEL4`)vVAnbbh_ct64lRZvlUN zfmG*+EST$Q2WXzeu31DEkL_(?ljHH9*)}?anmWJNVLiyJS#fH7zXKoLQ|kQ~fpD2< z<5&+S;six>VTIph-6u8~LkKnWU?hUehFhPsKkg8v0pA0UeT~FREYVyx7J%+qY&PNr zK$LSJeQmVHJdaX?K9y_qbm(EX*x_wCB4TktAd&0m37~RRMFl}57u&tky~=9EC8@RI z*3ajtKkGD7|5pEoI=uBw6bob5ijykWt5%#i3T(rx;(+anC?%;CkM52`_>yG1SR$@g z?gKA8d-t`FzJ{hE_=UP(mj3-&}PSDI)~gnHFP zjj^Ts_)F&FaP2l%p)*F^@^iZ5EVD0t#1ug2V@gvad>3r&FP??ZLK}oZEx9;Pxf0Hv(rGZi~U;V)5 ztD&Tam#9dvegOvCN_BpTqAY72gz!N&N0|JNQmtk~%(tZ0(mVBebB)&2nIPX%rZiQ) zrA{@`;e96wKK_)Qr75*u4*U7JmaL=u&*xWxG1ib>EYivK@M$3RRaG7qYSzHPGUwsJ z3~JT+@O42)ijJ|!=DC+Lr7Gs(3{<1KSmbA^^Ds*Nx!JqH2t;tP(s@{@Q{#D9tXYYJ zrTh0H^Do~1<+i@<^1J?Cf}ano8F8`J8^LfSn3KI7f}^+Ij8~AQ}`T2 WR*&lY{fwWX&;J9~zg(GoY5)L`OND;` From f2f0c7303a6c09422b6b99ca994ccdb13285233c Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Thu, 23 Jul 2026 02:15:26 +0200 Subject: [PATCH 04/15] `no-unsafe-sqlite-interpolation`: Handle uninitialized aliases --- rules/no-unsafe-sqlite-interpolation.js | 35 +++++++++++++------------ test/no-unsafe-sqlite-interpolation.js | 1 + 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/rules/no-unsafe-sqlite-interpolation.js b/rules/no-unsafe-sqlite-interpolation.js index 61be4cdefb..9f596f6c2e 100644 --- a/rules/no-unsafe-sqlite-interpolation.js +++ b/rules/no-unsafe-sqlite-interpolation.js @@ -62,7 +62,7 @@ const getSimpleConstVariableInfo = (node, context) => { const getImportSpecifierName = node => node.imported.type === 'Identifier' ? node.imported.name : node.imported.value; -const isDatabaseSyncImport = (node, context) => { +const getNodeSqliteImportSpecifier = (node, context) => { const definition = getVariableInfo(node, context)?.definition; if ( !definition @@ -70,29 +70,26 @@ const isDatabaseSyncImport = (node, context) => { || definition.parent.source.value !== 'node:sqlite' || !isRuntimeImportSpecifier(definition.node) ) { - return false; + return; } - return definition.node.type === 'ImportSpecifier' - && getImportSpecifierName(definition.node) === 'DatabaseSync'; + return definition.node; +}; + +const isDatabaseSyncImport = (node, context) => { + const specifier = getNodeSqliteImportSpecifier(node, context); + return specifier?.type === 'ImportSpecifier' + && getImportSpecifierName(specifier) === 'DatabaseSync'; }; const isSqliteNamespaceImport = (node, context) => { - const definition = getVariableInfo(node, context)?.definition; - if ( - !definition - || definition.type !== 'ImportBinding' - || definition.parent.source.value !== 'node:sqlite' - || !isRuntimeImportSpecifier(definition.node) - ) { - return false; - } + const specifier = getNodeSqliteImportSpecifier(node, context); - return definition.node.type === 'ImportNamespaceSpecifier' - || definition.node.type === 'ImportDefaultSpecifier' + return specifier?.type === 'ImportNamespaceSpecifier' + || specifier?.type === 'ImportDefaultSpecifier' || ( - definition.node.type === 'ImportSpecifier' - && getImportSpecifierName(definition.node) === 'default' + specifier?.type === 'ImportSpecifier' + && getImportSpecifierName(specifier) === 'default' ); }; @@ -133,6 +130,10 @@ const isSqliteNamespaceRequireBinding = (node, context) => getNodeSqliteRequireB const isDatabaseSyncConstructor = (node, context, seenVariables = new Set()) => { const callee = unwrapExpression(node); + if (!callee) { + return false; + } + if (callee.type === 'Identifier') { if (isDatabaseSyncImport(callee, context) || isDatabaseSyncRequireBinding(callee, context)) { return true; diff --git a/test/no-unsafe-sqlite-interpolation.js b/test/no-unsafe-sqlite-interpolation.js index ce7bc88449..17df86bcf0 100644 --- a/test/no-unsafe-sqlite-interpolation.js +++ b/test/no-unsafe-sqlite-interpolation.js @@ -36,6 +36,7 @@ test.snapshot({ 'import {DatabaseSync} from "node:sqlite"; let database = new DatabaseSync(":memory:"); database.exec(`SELECT ${value}`);', 'import {DatabaseSync} from "other-sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${value}`);', 'class DatabaseSync { exec() {} } const database = new DatabaseSync(); database.exec(`SELECT ${value}`);', + 'for (const Database of values) { const database = new Database(":memory:"); database.exec(`SELECT ${value}`); }', 'database.exec(`SELECT ${value}`);', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec();', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(...queries);', From 4383acb04d97ca45af4434065369b40bb9e69a4d Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Thu, 23 Jul 2026 02:24:31 +0200 Subject: [PATCH 05/15] `no-unsafe-sqlite-interpolation`: Handle TypeScript import equals --- rules/no-unsafe-sqlite-interpolation.js | 1 + test/no-unsafe-sqlite-interpolation.js | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/rules/no-unsafe-sqlite-interpolation.js b/rules/no-unsafe-sqlite-interpolation.js index 9f596f6c2e..50934383f2 100644 --- a/rules/no-unsafe-sqlite-interpolation.js +++ b/rules/no-unsafe-sqlite-interpolation.js @@ -67,6 +67,7 @@ const getNodeSqliteImportSpecifier = (node, context) => { if ( !definition || definition.type !== 'ImportBinding' + || definition.parent.type !== 'ImportDeclaration' || definition.parent.source.value !== 'node:sqlite' || !isRuntimeImportSpecifier(definition.node) ) { diff --git a/test/no-unsafe-sqlite-interpolation.js b/test/no-unsafe-sqlite-interpolation.js index 17df86bcf0..abf465a123 100644 --- a/test/no-unsafe-sqlite-interpolation.js +++ b/test/no-unsafe-sqlite-interpolation.js @@ -31,6 +31,10 @@ test.snapshot({ code: 'import {type DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${value}`);', languageOptions: {parser: parsers.typescript}, }, + { + code: 'import sqlite = require("node:sqlite"); const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${value}`);', + languageOptions: {parser: parsers.typescript}, + }, 'import {DatabaseSync} from "node:sqlite"; let Database = DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${value}`);', 'import {DatabaseSync} from "node:sqlite"; let Database; Database = DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${value}`);', 'import {DatabaseSync} from "node:sqlite"; let database = new DatabaseSync(":memory:"); database.exec(`SELECT ${value}`);', From f98b58e5b540cd9edef138b5e77f1197c252d2d7 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Thu, 23 Jul 2026 02:29:59 +0200 Subject: [PATCH 06/15] `no-unsafe-sqlite-interpolation`: Remove `require()` support --- docs/rules/no-unsafe-sqlite-interpolation.md | 2 +- rules/no-unsafe-sqlite-interpolation.js | 47 +---- test/no-unsafe-sqlite-interpolation.js | 20 +- .../no-unsafe-sqlite-interpolation.js.md | 196 +++--------------- .../no-unsafe-sqlite-interpolation.js.snap | Bin 1909 -> 1599 bytes 5 files changed, 35 insertions(+), 230 deletions(-) diff --git a/docs/rules/no-unsafe-sqlite-interpolation.md b/docs/rules/no-unsafe-sqlite-interpolation.md index 767d2e6241..b6f41722a1 100644 --- a/docs/rules/no-unsafe-sqlite-interpolation.md +++ b/docs/rules/no-unsafe-sqlite-interpolation.md @@ -25,7 +25,7 @@ const sql = database.createTagStore(); sql.get`SELECT * FROM users WHERE id = ${id}`; ``` -This rule only recognizes runtime `DatabaseSync` instances imported or required from exactly `node:sqlite`, including simple immutable `const` aliases of constructors, namespaces, and instances, and only checks `exec()` and `prepare()`. It intentionally ignores string concatenation, generic query methods, dynamic imports, factory-created databases, and mutable assignment flows. +This rule only recognizes runtime `DatabaseSync` instances imported from exactly `node:sqlite`, including simple immutable `const` aliases of constructors, namespaces, and instances, and only checks `exec()` and `prepare()`. It intentionally ignores CommonJS `require()`, TypeScript import-equals declarations, string concatenation, generic query methods, dynamic imports, factory-created databases, and mutable assignment flows. ## Examples diff --git a/rules/no-unsafe-sqlite-interpolation.js b/rules/no-unsafe-sqlite-interpolation.js index 50934383f2..953e4b6aa0 100644 --- a/rules/no-unsafe-sqlite-interpolation.js +++ b/rules/no-unsafe-sqlite-interpolation.js @@ -1,6 +1,5 @@ import {findVariable, getPropertyName} from '@eslint-community/eslint-utils'; -import {isStaticRequire} from './ast/index.js'; -import {isGlobalIdentifier, isRuntimeImportSpecifier, isTypeScriptExpressionWrapper} from './utils/index.js'; +import {isRuntimeImportSpecifier, isTypeScriptExpressionWrapper} from './utils/index.js'; const MESSAGE_ID = 'no-unsafe-sqlite-interpolation'; const messages = { @@ -62,6 +61,7 @@ const getSimpleConstVariableInfo = (node, context) => { const getImportSpecifierName = node => node.imported.type === 'Identifier' ? node.imported.name : node.imported.value; +// Only ESM imports are supported. CommonJS `require()` and TypeScript import-equals declarations are intentionally ignored. const getNodeSqliteImportSpecifier = (node, context) => { const definition = getVariableInfo(node, context)?.definition; if ( @@ -94,41 +94,6 @@ const isSqliteNamespaceImport = (node, context) => { ); }; -const isNodeSqliteRequire = (node, context) => { - const requireCall = unwrapExpression(node); - return isStaticRequire(requireCall) - && isGlobalIdentifier(requireCall.callee, context) - && requireCall.arguments[0].value === 'node:sqlite'; -}; - -const getNodeSqliteRequireBinding = (node, context) => { - const variableInfo = getConstVariableInfo(node, context); - if (!variableInfo || !isNodeSqliteRequire(variableInfo.initializer, context)) { - return; - } - - return variableInfo; -}; - -const isDatabaseSyncRequireBinding = (node, context) => { - const variableInfo = getNodeSqliteRequireBinding(node, context); - if (!variableInfo) { - return false; - } - - const {definition, variable} = variableInfo; - const {id} = definition.node; - return id.type === 'ObjectPattern' - && id.properties.some(property => - property.type === 'Property' - && getPropertyName(property) === 'DatabaseSync' - && property.value.type === 'Identifier' - && findVariable(context.sourceCode.getScope(property.value), property.value) === variable, - ); -}; - -const isSqliteNamespaceRequireBinding = (node, context) => getNodeSqliteRequireBinding(node, context)?.definition.node.id.type === 'Identifier'; - const isDatabaseSyncConstructor = (node, context, seenVariables = new Set()) => { const callee = unwrapExpression(node); if (!callee) { @@ -136,7 +101,7 @@ const isDatabaseSyncConstructor = (node, context, seenVariables = new Set()) => } if (callee.type === 'Identifier') { - if (isDatabaseSyncImport(callee, context) || isDatabaseSyncRequireBinding(callee, context)) { + if (isDatabaseSyncImport(callee, context)) { return true; } @@ -158,15 +123,11 @@ const isDatabaseSyncConstructor = (node, context, seenVariables = new Set()) => const isSqliteNamespace = (node, context, seenVariables = new Set()) => { node = unwrapExpression(node); - if (isNodeSqliteRequire(node, context)) { - return true; - } - if (node?.type !== 'Identifier') { return false; } - if (isSqliteNamespaceImport(node, context) || isSqliteNamespaceRequireBinding(node, context)) { + if (isSqliteNamespaceImport(node, context)) { return true; } diff --git a/test/no-unsafe-sqlite-interpolation.js b/test/no-unsafe-sqlite-interpolation.js index abf465a123..77fba2a327 100644 --- a/test/no-unsafe-sqlite-interpolation.js +++ b/test/no-unsafe-sqlite-interpolation.js @@ -13,10 +13,10 @@ test.snapshot({ 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.prepare("SELECT ?").get(value);', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.run(`SELECT ${value}`);', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.query(`SELECT ${value}`);', - 'const sqlite = require("other-sqlite"); const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${value}`);', - 'const {DatabaseSync} = require("other-sqlite"); const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${value}`);', - 'const database = new (require("other-sqlite").DatabaseSync)(":memory:"); database.exec(`SELECT ${value}`);', - 'const require = () => ({DatabaseSync: class {}}); const database = new (require("node:sqlite").DatabaseSync)(":memory:"); database.exec(`SELECT ${value}`);', + 'const sqlite = require("node:sqlite"); const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${value}`);', + 'const {DatabaseSync} = require("node:sqlite"); const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${value}`);', + 'const database = new (require("node:sqlite").DatabaseSync)(":memory:"); database.exec(`SELECT ${value}`);', + 'const Database = require("node:sqlite").DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${value}`);', 'import {DatabaseSync} from "node:sqlite"; const sql = new DatabaseSync(":memory:").createTagStore(); sql.get`SELECT * FROM users WHERE id = ${value}`;', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec("SELECT " + value);', 'import {DatabaseSync} from "node:sqlite"; const database = createDatabase(); database.exec(`SELECT ${value}`);', @@ -51,11 +51,6 @@ test.snapshot({ 'import * as sqlite from "node:sqlite"; const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT * FROM users WHERE id = ${id}`);', 'import sqlite from "node:sqlite"; const database = new sqlite.DatabaseSync(":memory:"); database.prepare(`SELECT * FROM users WHERE id = ${id}`);', 'import {DatabaseSync} from "node:sqlite"; new DatabaseSync(":memory:").exec(`SELECT * FROM users WHERE id = ${id}`);', - 'const {DatabaseSync} = require("node:sqlite"); const database = new DatabaseSync(":memory:"); database.exec(`SELECT * FROM users WHERE id = ${id}`);', - 'const {DatabaseSync: Database} = require("node:sqlite"); const database = new Database(":memory:"); database.prepare(`SELECT * FROM users WHERE id = ${id}`);', - 'const sqlite = require("node:sqlite"); const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT * FROM users WHERE id = ${id}`);', - 'const database = new (require("node:sqlite").DatabaseSync)(":memory:"); database.prepare(`SELECT * FROM users WHERE id = ${id}`);', - 'const Database = require("node:sqlite").DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`);', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const query = `SELECT * FROM users WHERE id = ${id}`; const alias = query; database.prepare(alias);', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database?.exec(`SELECT * FROM users WHERE id = ${id}`);', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec?.(`SELECT * FROM users WHERE id = ${id}`);', @@ -71,17 +66,10 @@ test.snapshot({ 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const method = "exec"; database[method](`SELECT ${id}`);', 'import {DatabaseSync} from "node:sqlite"; const Database = DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`);', 'import * as sqlite from "node:sqlite"; const alias = sqlite; const database = new alias.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`);', - 'const sqlite = require("node:sqlite"); const alias = sqlite; const database = new alias.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`);', 'import * as sqlite from "node:sqlite"; const Database = sqlite.DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`);', - 'const sqlite = require("node:sqlite"); const Database = sqlite.DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`);', 'import {"DatabaseSync" as Database} from "node:sqlite"; const database = new Database(":memory:"); database.exec(`SELECT ${id}`);', 'import {"default" as sqlite} from "node:sqlite"; const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`);', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); database.prepare(`SELECT ${id}`);', - 'const {DatabaseSync} = require("node:sqlite"); const Database = DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`);', - { - code: 'const sqlite = require("node:sqlite") as typeof import("node:sqlite"); const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`);', - languageOptions: {parser: parsers.typescript}, - }, { code: 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` as string);', languageOptions: {parser: parsers.typescript}, diff --git a/test/snapshots/no-unsafe-sqlite-interpolation.js.md b/test/snapshots/no-unsafe-sqlite-interpolation.js.md index 02090d93e8..16d172df7c 100644 --- a/test/snapshots/no-unsafe-sqlite-interpolation.js.md +++ b/test/snapshots/no-unsafe-sqlite-interpolation.js.md @@ -84,87 +84,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(6): const {DatabaseSync} = require("node:sqlite"); const database = new DatabaseSync(":memory:"); database.exec(`SELECT * FROM users WHERE id = ${id}`); - -> Input - - `␊ - 1 | const {DatabaseSync} = require("node:sqlite"); const database = new DatabaseSync(":memory:"); database.exec(\`SELECT * FROM users WHERE id = ${id}\`);␊ - ` - -> Error 1/1 - - `␊ - Message:␊ - > 1 | const {DatabaseSync} = require("node:sqlite"); const database = new DatabaseSync(":memory:"); database.exec(\`SELECT * FROM users WHERE id = ${id}\`);␊ - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ - ` - -## invalid(7): const {DatabaseSync: Database} = require("node:sqlite"); const database = new Database(":memory:"); database.prepare(`SELECT * FROM users WHERE id = ${id}`); - -> Input - - `␊ - 1 | const {DatabaseSync: Database} = require("node:sqlite"); const database = new Database(":memory:"); database.prepare(\`SELECT * FROM users WHERE id = ${id}\`);␊ - ` - -> Error 1/1 - - `␊ - Message:␊ - > 1 | const {DatabaseSync: Database} = require("node:sqlite"); const database = new Database(":memory:"); database.prepare(\`SELECT * FROM users WHERE id = ${id}\`);␊ - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ - ` - -## invalid(8): const sqlite = require("node:sqlite"); const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT * FROM users WHERE id = ${id}`); - -> Input - - `␊ - 1 | const sqlite = require("node:sqlite"); const database = new sqlite.DatabaseSync(":memory:"); database.exec(\`SELECT * FROM users WHERE id = ${id}\`);␊ - ` - -> Error 1/1 - - `␊ - Message:␊ - > 1 | const sqlite = require("node:sqlite"); const database = new sqlite.DatabaseSync(":memory:"); database.exec(\`SELECT * FROM users WHERE id = ${id}\`);␊ - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ - ` - -## invalid(9): const database = new (require("node:sqlite").DatabaseSync)(":memory:"); database.prepare(`SELECT * FROM users WHERE id = ${id}`); - -> Input - - `␊ - 1 | const database = new (require("node:sqlite").DatabaseSync)(":memory:"); database.prepare(\`SELECT * FROM users WHERE id = ${id}\`);␊ - ` - -> Error 1/1 - - `␊ - Message:␊ - > 1 | const database = new (require("node:sqlite").DatabaseSync)(":memory:"); database.prepare(\`SELECT * FROM users WHERE id = ${id}\`);␊ - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ - ` - -## invalid(10): const Database = require("node:sqlite").DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`); - -> Input - - `␊ - 1 | const Database = require("node:sqlite").DatabaseSync; const database = new Database(":memory:"); database.exec(\`SELECT ${id}\`);␊ - ` - -> Error 1/1 - - `␊ - Message:␊ - > 1 | const Database = require("node:sqlite").DatabaseSync; const database = new Database(":memory:"); database.exec(\`SELECT ${id}\`);␊ - | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ - ` - -## invalid(11): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const query = `SELECT * FROM users WHERE id = ${id}`; const alias = query; database.prepare(alias); +## invalid(6): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const query = `SELECT * FROM users WHERE id = ${id}`; const alias = query; database.prepare(alias); > Input @@ -180,7 +100,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(12): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database?.exec(`SELECT * FROM users WHERE id = ${id}`); +## invalid(7): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database?.exec(`SELECT * FROM users WHERE id = ${id}`); > Input @@ -196,7 +116,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(13): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec?.(`SELECT * FROM users WHERE id = ${id}`); +## invalid(8): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec?.(`SELECT * FROM users WHERE id = ${id}`); > Input @@ -212,7 +132,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(14): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database["prepare"](`SELECT * FROM users WHERE id = ${id}`); +## invalid(9): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database["prepare"](`SELECT * FROM users WHERE id = ${id}`); > Input @@ -228,7 +148,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(15): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${first} FROM users WHERE id = ${second}`); +## invalid(10): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${first} FROM users WHERE id = ${second}`); > Input @@ -244,7 +164,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(16): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(/* keep */ `SELECT * FROM users WHERE id = ${id}`); +## invalid(11): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(/* keep */ `SELECT * FROM users WHERE id = ${id}`); > Input @@ -260,7 +180,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(17): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(String.raw`SELECT * FROM users WHERE id = ${id}`); +## invalid(12): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(String.raw`SELECT * FROM users WHERE id = ${id}`); > Input @@ -276,7 +196,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(18): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.prepare(String.raw`SELECT 1`); +## invalid(13): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.prepare(String.raw`SELECT 1`); > Input @@ -292,7 +212,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(19): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const sql = `SELECT ${id}`; database.exec(sql); +## invalid(14): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const sql = `SELECT ${id}`; database.exec(sql); > Input @@ -308,7 +228,7 @@ Generated by [AVA](https://avajs.dev). | ^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(20): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const sql = `SELECT ${id}`; const alias = sql; database.prepare(alias); +## invalid(15): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const sql = `SELECT ${id}`; const alias = sql; database.prepare(alias); > Input @@ -324,7 +244,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(21): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const alias = database; alias.exec(`SELECT ${id}`); +## invalid(16): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const alias = database; alias.exec(`SELECT ${id}`); > Input @@ -340,7 +260,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(22): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database["exec"](`SELECT ${id}`); +## invalid(17): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database["exec"](`SELECT ${id}`); > Input @@ -356,7 +276,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(23): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const method = "exec"; database[method](`SELECT ${id}`); +## invalid(18): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const method = "exec"; database[method](`SELECT ${id}`); > Input @@ -372,7 +292,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(24): import {DatabaseSync} from "node:sqlite"; const Database = DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`); +## invalid(19): import {DatabaseSync} from "node:sqlite"; const Database = DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -388,7 +308,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(25): import * as sqlite from "node:sqlite"; const alias = sqlite; const database = new alias.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); +## invalid(20): import * as sqlite from "node:sqlite"; const alias = sqlite; const database = new alias.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -404,23 +324,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(26): const sqlite = require("node:sqlite"); const alias = sqlite; const database = new alias.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); - -> Input - - `␊ - 1 | const sqlite = require("node:sqlite"); const alias = sqlite; const database = new alias.DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\`);␊ - ` - -> Error 1/1 - - `␊ - Message:␊ - > 1 | const sqlite = require("node:sqlite"); const alias = sqlite; const database = new alias.DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\`);␊ - | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ - ` - -## invalid(27): import * as sqlite from "node:sqlite"; const Database = sqlite.DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`); +## invalid(21): import * as sqlite from "node:sqlite"; const Database = sqlite.DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -436,23 +340,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(28): const sqlite = require("node:sqlite"); const Database = sqlite.DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`); - -> Input - - `␊ - 1 | const sqlite = require("node:sqlite"); const Database = sqlite.DatabaseSync; const database = new Database(":memory:"); database.exec(\`SELECT ${id}\`);␊ - ` - -> Error 1/1 - - `␊ - Message:␊ - > 1 | const sqlite = require("node:sqlite"); const Database = sqlite.DatabaseSync; const database = new Database(":memory:"); database.exec(\`SELECT ${id}\`);␊ - | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ - ` - -## invalid(29): import {"DatabaseSync" as Database} from "node:sqlite"; const database = new Database(":memory:"); database.exec(`SELECT ${id}`); +## invalid(22): import {"DatabaseSync" as Database} from "node:sqlite"; const database = new Database(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -468,7 +356,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(30): import {"default" as sqlite} from "node:sqlite"; const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); +## invalid(23): import {"default" as sqlite} from "node:sqlite"; const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -484,7 +372,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(31): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); database.prepare(`SELECT ${id}`); +## invalid(24): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); database.prepare(`SELECT ${id}`); > Input @@ -508,39 +396,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(32): const {DatabaseSync} = require("node:sqlite"); const Database = DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`); - -> Input - - `␊ - 1 | const {DatabaseSync} = require("node:sqlite"); const Database = DatabaseSync; const database = new Database(":memory:"); database.exec(\`SELECT ${id}\`);␊ - ` - -> Error 1/1 - - `␊ - Message:␊ - > 1 | const {DatabaseSync} = require("node:sqlite"); const Database = DatabaseSync; const database = new Database(":memory:"); database.exec(\`SELECT ${id}\`);␊ - | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ - ` - -## invalid(33): const sqlite = require("node:sqlite") as typeof import("node:sqlite"); const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); - -> Input - - `␊ - 1 | const sqlite = require("node:sqlite") as typeof import("node:sqlite"); const database = new sqlite.DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\`);␊ - ` - -> Error 1/1 - - `␊ - Message:␊ - > 1 | const sqlite = require("node:sqlite") as typeof import("node:sqlite"); const database = new sqlite.DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\`);␊ - | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ - ` - -## invalid(34): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` as string); +## invalid(25): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` as string); > Input @@ -556,7 +412,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(35): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); +## invalid(26): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -572,7 +428,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(36): import {DatabaseSync} from "node:sqlite"; const database = new (DatabaseSync)(":memory:"); database.exec(`SELECT ${id}`); +## invalid(27): import {DatabaseSync} from "node:sqlite"; const database = new (DatabaseSync)(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -588,7 +444,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(37): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); +## invalid(28): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -604,7 +460,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(38): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`!); +## invalid(29): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`!); > Input @@ -620,7 +476,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(39): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database!.exec(`SELECT ${id}`); +## invalid(30): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database!.exec(`SELECT ${id}`); > Input @@ -636,7 +492,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(40): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` satisfies string); +## invalid(31): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` satisfies string); > Input diff --git a/test/snapshots/no-unsafe-sqlite-interpolation.js.snap b/test/snapshots/no-unsafe-sqlite-interpolation.js.snap index 96cc667cc930706e2b019c8dfb3685552b805821..29e46a1cc4859455e682d3e12ea840bbe34ec89b 100644 GIT binary patch literal 1599 zcmV-F2Eh42RzVZfGiT0@w;M~gTRTU$p4}n7<=f=^P0Lv^ZL2;}m@I+SY`LBf>kFoDE}4{6)|}cV zG(5KnhU3;riMDOaC&nDqT!;El-@6ay!6B;Xt-J#I%(&^ahZ|7B7#G!IMAt3hQsX;ha){4k~$M z3x69bZ4iKpi~4lpDB3>k@o8!4gg(miq!SuNflGlp47NiIUc|uQw6Qli8)tH$@H^wC z+h{0cQP0KUDigrR_&8*W1Q`jXSUiZTbcgZFbcfa(mPh?fVTVvsbDjKk3Ozy?TAf0Z za8NNx1_%}_h$ZcrDL6++3#MigW{vu>k|6}0S;-O(Dl4h6ky-vOi0a=GdZx0gYT7eb zv!n6{W3p{zRMJPlCFMINh>NO7$q)@HC>qQu*CI}4Q8)?r4EJ|Ufs4a+#z~*!;V@Vz z*YzJUTHjLO`U6Cxnp@y+2_F6_^6(M)g%R4eV;ktj%Ug-5h{E=*jM-r}9er3G$OI8r% zOXresiBa$8CIKM~O;C`}LiF}f@o?J1Ik8;G@`<4JgXLlRGG(?m=(G`@=M6f2f&eWj zkj}uph=D>{24X`I=SYauIpGoGraPHPK$Fkq;Y%ihjeI=dg`x))!6luIuOl|%bzURh zwuYQ@wFxoUVoY?ke`%ylDcA8PBX&`^5>lrc)R~29*g}6cjakA4>OrIQWv&Q^lF${`I8PI{To0*`0%Sb!J#kDpQ(kJ*iGL=YJ)OEYz%lgQe#6L#SI%#GeZr za#CtuuS}_mad-%-QC%$Zv(#}I@Az}r0|{KLbQ~7y)OZ{gYgXc5>HfVO`4{EfGH-Mk z=Nb1K#tj#gE`!Gfjy=U_d|8jx@3`V6gG`UkqMRWfPr5`<|MxB zk^2|F8Xt=Y00000000B!oLf^IMHI(D%d(0$zd^H=hg}{ZyAU9YBvuJtsuH7Mm6ueM zfo;N6hMi?*xCv-XjF>3iW58T6L=zH?rIuBG0Uz|)2fvQBEYr+^ZZgv|eY!2?k)5Ib zr~Ciw+v)DxCWq-=C1Dfk2V{X+CNM-vy(od>%mP9|x=gO0G()H%cxk4X%v7OG*vHZyJ z;EBO!UnOnixuH`h$+S;BpSV6< zO;0w}pXMi;o_g$BQwt$QvS^y_7V21|)S)j{YtunP`5VnkKzg;gnts|95XTo zLI_zTNY7ib9x~vPsv8iy8c?t1I#xi*jO9#Izy2qej6}+5gbcrOg7|@FSI_$G+cjbj zdYB?L0v7R?=Uujo7Q&0V2*Mw3hx8)o(KhlqCf^g8wTe?42~q4KkPSTP222WiJGN( zG4UKBJ8fJqKM%azn_?o6q0sE@s?Cw#g^uhll((_pRhIq4qf^tiNAsXS3)-f&KLShK z2-{{8#d9sa4D`?Kv9ytaWN&$6k+>fs(Zffg9GOr_R*SN6%kRKjKkUPXIZ<#SSp$Jw zi35p25#iIK9~(B~?Pb_(k1!KfhVB`!f!d{Jye7th4u&=yRze((G8}f5ah_izpwJ7- zdWu!xp3Bhu8kKUbZUM1F0;}s(t=QPwZ2Sq!cpYXw9@r`87)31C<$`8K?BZ{LF_xi= zwdv$~_zaNxlEB0IG%I#+9A7K&@88V7N>jFt*QchbH&6OWyjCOp%5rSWC;g4DIc?C^ z?>zJW1040I^87ABkX&;91#$j`mkh=TSPOR9JgS&w$%3LN&zxzh|9IMY#6958RkVE; z359dndJkx?;cv7^8U#V*MccUR7Gt0jZL87oc4ZkebCjpam>CoWE(ZEI*kBxVVQ_Fd zx7|PI&ZJ=BCt#?%4^ojJGoh7>dyI=@spL$uMTZu~ZO;#ugcah` zF}Iq2FP<48WVRR2kZ@2ji3bQ4E8TdkbhMFkl-5XFN6d+%d8|l;U@|K*;h?gT7#o#a zyo)_}tPF3dYWJ*Jc~pJ@CcBM{O7aM}qAhkv3Mb*R;ohn# za&fo@ob(YM4*Lt`y8dmT^<@pNe}rgMa|`^fz{6LWhmWeyjrdbeWDPxBD~IilKjrYe zybk>FIZ~b@wqUNOU7&dcJ7*DJJhr!mj~b6ZWJlHx>+(3v2l-P@k{sV3z(<=}y`LZu zE)%;rHiL;IK@n3};XkVSi8aO$LJmEwiSV<&#$V!Y2)P41_B9eOi9~bRm<75gu-Ql! z0MX8Y^0Cn}^E^rp`gE>d(vih(vBP)G5fPIM0*PEd4+E70Ix+|%x!CSi?p2m6E-B3w zH(sBl!Gv3%`ZvlqQs9kW;#e3vS1haCtX#2l6xfE>#Q{5kP?E_NM|NJKZC||)y!5Rs z2q2t!H6XbTf-vk9Lc{zgE%Qt{MR`z&Vr&_L`Z6Nqfv7fan z*W_swU$cFhof@v94oi4%rd;gxi$?6zOdf$*yL(bcgUW;|3QC6Umo>c}9{s4PsZvKN zY_Gm&?Nv$kqCp)AnxBEe)>GXzs3}Xc0z!BHb From 373eb098abcd84415fb8ea7515240adf89240a4e Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Thu, 23 Jul 2026 02:45:38 +0200 Subject: [PATCH 07/15] `no-unsafe-sqlite-interpolation`: Improve coverage and documentation --- docs/rules/no-unsafe-sqlite-interpolation.md | 2 +- test/no-unsafe-sqlite-interpolation.js | 9 +++++ .../no-unsafe-sqlite-interpolation.js.md | 32 ++++++++++++++++++ .../no-unsafe-sqlite-interpolation.js.snap | Bin 1599 -> 1700 bytes 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/docs/rules/no-unsafe-sqlite-interpolation.md b/docs/rules/no-unsafe-sqlite-interpolation.md index b6f41722a1..1e6621f59f 100644 --- a/docs/rules/no-unsafe-sqlite-interpolation.md +++ b/docs/rules/no-unsafe-sqlite-interpolation.md @@ -9,7 +9,7 @@ Interpolating values into SQL strings makes them part of the SQL source instead of bound parameters. This can lead to SQL injection and data corruption. -Tagged templates passed directly to `exec()` or `prepare()` are also reported because they are not `SQLTagStore` calls, including static tags such as `String.raw`. +Tagged templates passed directly to `exec()` or `prepare()` are also reported because those methods do not bind tagged-template interpolations, including static tags such as `String.raw`. Use placeholders with `DatabaseSync#prepare()` and bind values when executing the prepared statement: diff --git a/test/no-unsafe-sqlite-interpolation.js b/test/no-unsafe-sqlite-interpolation.js index 77fba2a327..c681e8dff0 100644 --- a/test/no-unsafe-sqlite-interpolation.js +++ b/test/no-unsafe-sqlite-interpolation.js @@ -22,6 +22,7 @@ test.snapshot({ 'import {DatabaseSync} from "node:sqlite"; const database = createDatabase(); database.exec(`SELECT ${value}`);', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const {exec} = database; exec(`SELECT ${value}`);', 'import {DatabaseSync} from "node:sqlite"; const {database} = new DatabaseSync(":memory:"); database.exec(`SELECT ${value}`);', + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const method = getMethod(); database[method](`SELECT ${value}`);', 'const sqlite = await import("node:sqlite"); const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${value}`);', { code: 'import type {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${value}`);', @@ -98,5 +99,13 @@ test.snapshot({ code: 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` satisfies string);', languageOptions: {parser: parsers.typescript}, }, + { + code: 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); (database as DatabaseSync).exec(`SELECT ${id}`);', + languageOptions: {parser: parsers.typescript}, + }, + { + code: 'import * as sqlite from "node:sqlite"; const database = new (sqlite as typeof import("node:sqlite")).DatabaseSync(":memory:"); database.exec(`SELECT ${id}`);', + languageOptions: {parser: parsers.typescript}, + }, ], }); diff --git a/test/snapshots/no-unsafe-sqlite-interpolation.js.md b/test/snapshots/no-unsafe-sqlite-interpolation.js.md index 16d172df7c..0638a8cccd 100644 --- a/test/snapshots/no-unsafe-sqlite-interpolation.js.md +++ b/test/snapshots/no-unsafe-sqlite-interpolation.js.md @@ -507,3 +507,35 @@ Generated by [AVA](https://avajs.dev). > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\` satisfies string);␊ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` + +## invalid(32): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); (database as DatabaseSync).exec(`SELECT ${id}`); + +> Input + + `␊ + 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); (database as DatabaseSync).exec(\`SELECT ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); (database as DatabaseSync).exec(\`SELECT ${id}\`);␊ + | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(33): import * as sqlite from "node:sqlite"; const database = new (sqlite as typeof import("node:sqlite")).DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); + +> Input + + `␊ + 1 | import * as sqlite from "node:sqlite"; const database = new (sqlite as typeof import("node:sqlite")).DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import * as sqlite from "node:sqlite"; const database = new (sqlite as typeof import("node:sqlite")).DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\`);␊ + | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` diff --git a/test/snapshots/no-unsafe-sqlite-interpolation.js.snap b/test/snapshots/no-unsafe-sqlite-interpolation.js.snap index 29e46a1cc4859455e682d3e12ea840bbe34ec89b..1f73dd3ebcd850be1b4a62670c5252e73d004102 100644 GIT binary patch literal 1700 zcmV;V23z?-RzVaDTrgQIZP9qopqF}niL z2Zk%KLm!I>00000000BkoLg_)L=?vp5JJiu-{8dR!)_in-K0QCNQ*?mMQSM|NL;GI zI@#IAs$=hZoTMR%C@ocKp+LE`3B8e2w56gFNJxAE9{SD$Uk7cxc08Mzjc;>in8#h) z`k$HKe{RR)t;VeF&YhtfgWJTne4CuTX*o-#ZPiQF@)%ePP1o~bb<*_BS(B2P6=!Z8 z8lJlVhU3=B7;V{>PmFPxa~lH*fJ2rcep|^HTObRrw=!mw$D<1?)aaA4J{uh+`s7In-fop*hDe!Jd&#QXQ3?t8Yc zUHYHtd+wA!lP)-}50>K-uj$&RPhiQk7YS|u9U~ z|N9~SBLe)vq*2{wXbwSBG(FNZJ(9x-e=s<0_i};6!76t{ ztBmeiWvJa4!BdjAMDTA#{%G-tq3dRsM|9H8HOECp{*uTXTE&6x3T1Y<9opg0t{vpH zLjD2cAw$z5g+PHqi^o+NkxxP*Uy!+?4K8mj5^n`2AXzC0b)Ah{O#mx=QU-dY(W;Nq=@R%BWBR%qE5F*c!&<{e-jFnDgD?Lws#&b8L3 zjP@q}HdNXm0M#z)mlKE4_F<1!q%9}(QJ!Zxp-~jLE>LHKt54IlEPUXCT``wo3X4xK6po1aV%KQ!+$@Y7`B|l&g_XMp1kc?iucFnkv^0HyDz> zz_UYdpymhUore2bLi zNGzDkw99C&VJ9q-i^pbLSmZGNIAT0qI=aAGXOls+Wv5B-{l#$fP$~N<0^xe1%g1_n zB27@Bi&ywX);O^lc?cna9=1ey+j#Ix_jZS98E}tb><1*jq!P{b#xkRO9@`t~0s!Sc zkUlrM6fuvIfj()pYdW^sEhfBcjfj|B5J=?mJj1A*RFOdt$;GBuI;(dgR;8wjgUc6) zKkv4e{z3i5Dm?fn3BuT^;;hQ`N)>000vmW$K8VgERI}uYCwJQ+eoMB)BME0KzcMWS zl+_6GrE{Hdfl=@0b^<~enpi=;7oxX^Mh>PuoD*9MSw0ceelR^upQgQzsbj)phNe52oPZ{u>xVCy2-foX0WTCiToDxM-uOE5 zMm*1J#Ov0ObFMa_4K^5vE_Y9jlquyBZ!%)1g)<>_szD9h(mDPv;y9j%HR_~cF514a z+r^});EYFM#Vp@q@VL5%WtD2VJX?(555+tyRj!C>6s~taLm@Rc+q;Ivr(O55enE@( zaMDMhSN1k{G^n1C<$@8--iOk?9riDBT54o?Wbb@ZYM#AUyX=fXJ32GWCzUD9HJ?TsK?KF9))Y>_3yd&VX?VnPQPnF2ko7sc)%bSg2V8f~BtO3&>lS z;`fCWS(du4SEf`&KU{!nREkABOYMj8jz5Pjkbq*P{jgA{#{IBZvl4=(^LsJk7v=D`8REgDQv_9v3IyuOs7Nd-qw@RF z=#0v!g3nQR*1gp9E!wb1uk8iwGb$+s|9^HldE!x-7Hu}jWBhu=xYRSZrRd67-m!gI zIcdlIGXu;u>5!*Y%%!}=h@QoxJhggM%=ZId0_Tq+&hvgSy41PBq~NbKiQDKrt;F11 uE*F@?{gc7ymm)qLEjZfGiT0@w;M~gTRTU$p4}n7<=f=^P0Lv^ZL2;}m@I+SY`LBf>kFoDE}4{6)|}cV zG(5KnhU3;riMDOaC&nDqT!;El-@6ay!6B;Xt-J#I%(&^ahZ|7B7#G!IMAt3hQsX;ha){4k~$M z3x69bZ4iKpi~4lpDB3>k@o8!4gg(miq!SuNflGlp47NiIUc|uQw6Qli8)tH$@H^wC z+h{0cQP0KUDigrR_&8*W1Q`jXSUiZTbcgZFbcfa(mPh?fVTVvsbDjKk3Ozy?TAf0Z za8NNx1_%}_h$ZcrDL6++3#MigW{vu>k|6}0S;-O(Dl4h6ky-vOi0a=GdZx0gYT7eb zv!n6{W3p{zRMJPlCFMINh>NO7$q)@HC>qQu*CI}4Q8)?r4EJ|Ufs4a+#z~*!;V@Vz z*YzJUTHjLO`U6Cxnp@y+2_F6_^6(M)g%R4eV;ktj%Ug-5h{E=*jM-r}9er3G$OI8r% zOXresiBa$8CIKM~O;C`}LiF}f@o?J1Ik8;G@`<4JgXLlRGG(?m=(G`@=M6f2f&eWj zkj}uph=D>{24X`I=SYauIpGoGraPHPK$Fkq;Y%ihjeI=dg`x))!6luIuOl|%bzURh zwuYQ@wFxoUVoY?ke`%ylDcA8PBX&`^5>lrc)R~29*g}6cjakA4>OrIQWv&Q^lF${`I8PI{To0*`0%Sb!J#kDpQ(kJ*iGL=YJ)OEYz%lgQe#6L#SI%#GeZr za#CtuuS}_mad-%-QC%$Zv(#}I@Az}r0|{KLbQ~7y)OZ{gYgXc5>HfVO`4{EfGH-Mk z=Nb1K#tj#gE`!Gfjy=U_d|8jx@3`V6gG`UkqMRWfPr5`< Date: Thu, 23 Jul 2026 02:49:01 +0200 Subject: [PATCH 08/15] `no-unsafe-sqlite-interpolation`: Clarify alias documentation --- docs/rules/no-unsafe-sqlite-interpolation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rules/no-unsafe-sqlite-interpolation.md b/docs/rules/no-unsafe-sqlite-interpolation.md index 1e6621f59f..c083f27cfe 100644 --- a/docs/rules/no-unsafe-sqlite-interpolation.md +++ b/docs/rules/no-unsafe-sqlite-interpolation.md @@ -25,7 +25,7 @@ const sql = database.createTagStore(); sql.get`SELECT * FROM users WHERE id = ${id}`; ``` -This rule only recognizes runtime `DatabaseSync` instances imported from exactly `node:sqlite`, including simple immutable `const` aliases of constructors, namespaces, and instances, and only checks `exec()` and `prepare()`. It intentionally ignores CommonJS `require()`, TypeScript import-equals declarations, string concatenation, generic query methods, dynamic imports, factory-created databases, and mutable assignment flows. +This rule only recognizes runtime `DatabaseSync` instances imported from exactly `node:sqlite`, including simple `const` aliases of constructors, namespaces, and instances, and only checks `exec()` and `prepare()`. It intentionally ignores CommonJS `require()`, TypeScript import-equals declarations, string concatenation, generic query methods, dynamic imports, factory-created databases, and mutable assignment flows. ## Examples From 58192d8dc3bfa109b8b2d4f28a4e46d246849ee1 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Thu, 23 Jul 2026 08:51:27 +0200 Subject: [PATCH 09/15] `no-unsafe-sqlite-interpolation`: Add regression coverage --- test/no-unsafe-sqlite-interpolation.js | 3 + .../no-unsafe-sqlite-interpolation.js.md | 56 ++++++++++++++---- .../no-unsafe-sqlite-interpolation.js.snap | Bin 1700 -> 1788 bytes 3 files changed, 47 insertions(+), 12 deletions(-) diff --git a/test/no-unsafe-sqlite-interpolation.js b/test/no-unsafe-sqlite-interpolation.js index c681e8dff0..28149f1793 100644 --- a/test/no-unsafe-sqlite-interpolation.js +++ b/test/no-unsafe-sqlite-interpolation.js @@ -41,6 +41,7 @@ test.snapshot({ 'import {DatabaseSync} from "node:sqlite"; let database = new DatabaseSync(":memory:"); database.exec(`SELECT ${value}`);', 'import {DatabaseSync} from "other-sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${value}`);', 'class DatabaseSync { exec() {} } const database = new DatabaseSync(); database.exec(`SELECT ${value}`);', + 'import {DatabaseSync} from "node:sqlite"; { const DatabaseSync = class {}; const database = new DatabaseSync(); database.exec(`SELECT ${value}`); }', 'for (const Database of values) { const database = new Database(":memory:"); database.exec(`SELECT ${value}`); }', 'database.exec(`SELECT ${value}`);', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec();', @@ -68,9 +69,11 @@ test.snapshot({ 'import {DatabaseSync} from "node:sqlite"; const Database = DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`);', 'import * as sqlite from "node:sqlite"; const alias = sqlite; const database = new alias.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`);', 'import * as sqlite from "node:sqlite"; const Database = sqlite.DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`);', + 'import * as sqlite from "node:sqlite"; const database = new sqlite["DatabaseSync"](":memory:"); database.exec(`SELECT ${id}`);', 'import {"DatabaseSync" as Database} from "node:sqlite"; const database = new Database(":memory:"); database.exec(`SELECT ${id}`);', 'import {"default" as sqlite} from "node:sqlite"; const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`);', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); database.prepare(`SELECT ${id}`);', + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.prepare(customTag`SELECT ${id}`);', { code: 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` as string);', languageOptions: {parser: parsers.typescript}, diff --git a/test/snapshots/no-unsafe-sqlite-interpolation.js.md b/test/snapshots/no-unsafe-sqlite-interpolation.js.md index 0638a8cccd..799f7bc8a3 100644 --- a/test/snapshots/no-unsafe-sqlite-interpolation.js.md +++ b/test/snapshots/no-unsafe-sqlite-interpolation.js.md @@ -340,7 +340,23 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(22): import {"DatabaseSync" as Database} from "node:sqlite"; const database = new Database(":memory:"); database.exec(`SELECT ${id}`); +## invalid(22): import * as sqlite from "node:sqlite"; const database = new sqlite["DatabaseSync"](":memory:"); database.exec(`SELECT ${id}`); + +> Input + + `␊ + 1 | import * as sqlite from "node:sqlite"; const database = new sqlite["DatabaseSync"](":memory:"); database.exec(\`SELECT ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import * as sqlite from "node:sqlite"; const database = new sqlite["DatabaseSync"](":memory:"); database.exec(\`SELECT ${id}\`);␊ + | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(23): import {"DatabaseSync" as Database} from "node:sqlite"; const database = new Database(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -356,7 +372,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(23): import {"default" as sqlite} from "node:sqlite"; const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); +## invalid(24): import {"default" as sqlite} from "node:sqlite"; const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -372,7 +388,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(24): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); database.prepare(`SELECT ${id}`); +## invalid(25): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); database.prepare(`SELECT ${id}`); > Input @@ -396,7 +412,23 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(25): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` as string); +## invalid(26): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.prepare(customTag`SELECT ${id}`); + +> Input + + `␊ + 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.prepare(customTag\`SELECT ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.prepare(customTag\`SELECT ${id}\`);␊ + | ^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(27): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` as string); > Input @@ -412,7 +444,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(26): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); +## invalid(28): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -428,7 +460,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(27): import {DatabaseSync} from "node:sqlite"; const database = new (DatabaseSync)(":memory:"); database.exec(`SELECT ${id}`); +## invalid(29): import {DatabaseSync} from "node:sqlite"; const database = new (DatabaseSync)(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -444,7 +476,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(28): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); +## invalid(30): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -460,7 +492,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(29): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`!); +## invalid(31): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`!); > Input @@ -476,7 +508,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(30): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database!.exec(`SELECT ${id}`); +## invalid(32): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database!.exec(`SELECT ${id}`); > Input @@ -492,7 +524,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(31): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` satisfies string); +## invalid(33): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` satisfies string); > Input @@ -508,7 +540,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(32): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); (database as DatabaseSync).exec(`SELECT ${id}`); +## invalid(34): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); (database as DatabaseSync).exec(`SELECT ${id}`); > Input @@ -524,7 +556,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(33): import * as sqlite from "node:sqlite"; const database = new (sqlite as typeof import("node:sqlite")).DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); +## invalid(35): import * as sqlite from "node:sqlite"; const database = new (sqlite as typeof import("node:sqlite")).DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); > Input diff --git a/test/snapshots/no-unsafe-sqlite-interpolation.js.snap b/test/snapshots/no-unsafe-sqlite-interpolation.js.snap index 1f73dd3ebcd850be1b4a62670c5252e73d004102..fa4dd0ef5aadc0927cf0c4f3390bb2109e7c2acc 100644 GIT binary patch literal 1788 zcmVrQGlj^z{R_hk(%_F; zAKu~#OCO5}00000000BkoLg@jMHI&q5JJiu-(X_;VCNyJofb+$S|k!KQc5AIxJ7|% z>~XT{#%u4alZGUsw3O0@LJPD_dL^moO;iF2i7&uI-+AEcAZN3iJdNHqrGeW z&&=;XJ7>;}H>%T?T|ULuUf!gx>00#kEweUnSY{<(EDR8Hu5LRnSsFH6W7=SJe6dzu zAyvnoBe|Mgp#!X8nJ&!@lCoW6E~#waM}|m^E|BnJ`P{%9owJ?Afm~rQxUrX>rse$9 z_{iwUn-;6nJNsLtY*~NtnIS1o0Zm2eEi_~ zju$&xSLX{IFFkjoql=ItS=LQ=7qyIOYSDLV^``47|Dm!>?h1zL~jDIjnnCqCe8P>bqBj$+X z*bXW76@C04`1qFu_!EN#ZJVGw1YJ>gXx(sV3Mc#laN69<33`OUHN{ULimxM?LX&V% z{h@rlcMS*MtWi~kD+u(4l{9&&tYuaJ|QW%!b z<=8nwcG|cszYSPEk|GhvP-ynJDkJilPvk2yGg|NVMw2>=WQas-Xa6a~G7UzCyr(&X z%+~ijvi||-{4G8GMF^5>k$*v)&+u|(jDWMZ8rmhxvbTN9zKXF3bu8@*<{`jy4Q&-F zrf{ycz69Fq_{&gngCMAOQJ+p6L0gAC)+;TY&_{WibV8#ja9yCz1{=N&Uc<1#L~i@# zoSRHxh2H>8chIbmL_OCI7eN4D;M*ZlB*>OfYKwBM`19 z+I*~sC*lM}bny!RlJ%Zg4?KiWK=*sXe{9hEq+L(pkM5uqw4w z?A<;`-C4Vp`g`pgYH;tHC0sQ$IiXz0bg7{Az3E~6GG($IblM0{ zvx82bAcz(&knVx|fd`6lJrEj-I7dRHz7w7Tn(iiY0-AiTA67vG%jx`p7m6OP@Gj}z zSPi^U%H;6oL@wMdxyf`MxCRLNCcAT*X=HL)p8!fnB^=32C7CE-S~|Pm2keGxudtcq zoC%MJ?;3#OV*9jSnNlvn1`s7x{VzE^Rq8AG} zU-x7k-G5BJ4#2p8OtDBOm*Ghu^*vP#3pHy%u+(ulgR{%|@N<4c7No9IDO0Lq9L_*B zD#aq6WsbxBcxR3M9*97((s5X*Q{!=1tXT=c()m3f@T=f2t-ZGG=7IYIpy7hlI z*b_kG8+y!syA@9vq%w+f?zTJX6hRfE0)cumDiVvzsQhs>I4{_bbLN$o_cFsZS=FR_ zEiYi77mO+R|Fde(6OYQYXt6;a zJ&i|sZ1t#^?+3mF&YuLFA5OXdE5H4K!HBz9r*?Jw*Ye=uLLtLF34a2Ne$8TgnDi*P eepm;AT*`F!nEZ$odxUpM^UZ$(m12r0X#fBacWea! literal 1700 zcmV;V23z?-RzVaDTrgQIZP9qopqF}niL z2Zk%KLm!I>00000000BkoLg_)L=?vp5JJiu-{8dR!)_in-K0QCNQ*?mMQSM|NL;GI zI@#IAs$=hZoTMR%C@ocKp+LE`3B8e2w56gFNJxAE9{SD$Uk7cxc08Mzjc;>in8#h) z`k$HKe{RR)t;VeF&YhtfgWJTne4CuTX*o-#ZPiQF@)%ePP1o~bb<*_BS(B2P6=!Z8 z8lJlVhU3=B7;V{>PmFPxa~lH*fJ2rcep|^HTObRrw=!mw$D<1?)aaA4J{uh+`s7In-fop*hDe!Jd&#QXQ3?t8Yc zUHYHtd+wA!lP)-}50>K-uj$&RPhiQk7YS|u9U~ z|N9~SBLe)vq*2{wXbwSBG(FNZJ(9x-e=s<0_i};6!76t{ ztBmeiWvJa4!BdjAMDTA#{%G-tq3dRsM|9H8HOECp{*uTXTE&6x3T1Y<9opg0t{vpH zLjD2cAw$z5g+PHqi^o+NkxxP*Uy!+?4K8mj5^n`2AXzC0b)Ah{O#mx=QU-dY(W;Nq=@R%BWBR%qE5F*c!&<{e-jFnDgD?Lws#&b8L3 zjP@q}HdNXm0M#z)mlKE4_F<1!q%9}(QJ!Zxp-~jLE>LHKt54IlEPUXCT``wo3X4xK6po1aV%KQ!+$@Y7`B|l&g_XMp1kc?iucFnkv^0HyDz> zz_UYdpymhUore2bLi zNGzDkw99C&VJ9q-i^pbLSmZGNIAT0qI=aAGXOls+Wv5B-{l#$fP$~N<0^xe1%g1_n zB27@Bi&ywX);O^lc?cna9=1ey+j#Ix_jZS98E}tb><1*jq!P{b#xkRO9@`t~0s!Sc zkUlrM6fuvIfj()pYdW^sEhfBcjfj|B5J=?mJj1A*RFOdt$;GBuI;(dgR;8wjgUc6) zKkv4e{z3i5Dm?fn3BuT^;;hQ`N)>000vmW$K8VgERI}uYCwJQ+eoMB)BME0KzcMWS zl+_6GrE{Hdfl=@0b^<~enpi=;7oxX^Mh>PuoD*9MSw0ceelR^upQgQzsbj)phNe52oPZ{u>xVCy2-foX0WTCiToDxM-uOE5 zMm*1J#Ov0ObFMa_4K^5vE_Y9jlquyBZ!%)1g)<>_szD9h(mDPv;y9j%HR_~cF514a z+r^});EYFM#Vp@q@VL5%WtD2VJX?(555+tyRj!C>6s~taLm@Rc+q;Ivr(O55enE@( zaMDMhSN1k{G^n1C<$@8--iOk?9riDBT54o?Wbb@ZYM#AUyX=fXJ32GWCzUD9HJ?TsK?KF9))Y>_3yd&VX?VnPQPnF2ko7sc)%bSg2V8f~BtO3&>lS z;`fCWS(du4SEf`&KU{!nREkABOYMj8jz5Pjkbq*P{jgA{#{IBZvl4=(^LsJk7v=D`8REgDQv_9v3IyuOs7Nd-qw@RF z=#0v!g3nQR*1gp9E!wb1uk8iwGb$+s|9^HldE!x-7Hu}jWBhu=xYRSZrRd67-m!gI zIcdlIGXu;u>5!*Y%%!}=h@QoxJhggM%=ZId0_Tq+&hvgSy41PBq~NbKiQDKrt;F11 uE*F@?{gc7ymm)qLEj Date: Thu, 23 Jul 2026 10:21:14 +0200 Subject: [PATCH 10/15] `no-unsafe-sqlite-interpolation`: Cover namespace shadowing --- test/no-unsafe-sqlite-interpolation.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/no-unsafe-sqlite-interpolation.js b/test/no-unsafe-sqlite-interpolation.js index 28149f1793..1f0924938d 100644 --- a/test/no-unsafe-sqlite-interpolation.js +++ b/test/no-unsafe-sqlite-interpolation.js @@ -42,6 +42,7 @@ test.snapshot({ 'import {DatabaseSync} from "other-sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${value}`);', 'class DatabaseSync { exec() {} } const database = new DatabaseSync(); database.exec(`SELECT ${value}`);', 'import {DatabaseSync} from "node:sqlite"; { const DatabaseSync = class {}; const database = new DatabaseSync(); database.exec(`SELECT ${value}`); }', + 'import * as sqlite from "node:sqlite"; { const sqlite = {}; const database = new sqlite.DatabaseSync(); database.exec(`SELECT ${value}`); }', 'for (const Database of values) { const database = new Database(":memory:"); database.exec(`SELECT ${value}`); }', 'database.exec(`SELECT ${value}`);', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec();', From 172ffa2687fee148f08ae23978547b22814397c9 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Thu, 23 Jul 2026 10:25:59 +0200 Subject: [PATCH 11/15] `no-unsafe-sqlite-interpolation`: Clarify import support comment --- rules/no-unsafe-sqlite-interpolation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/no-unsafe-sqlite-interpolation.js b/rules/no-unsafe-sqlite-interpolation.js index 953e4b6aa0..68bb318627 100644 --- a/rules/no-unsafe-sqlite-interpolation.js +++ b/rules/no-unsafe-sqlite-interpolation.js @@ -61,7 +61,7 @@ const getSimpleConstVariableInfo = (node, context) => { const getImportSpecifierName = node => node.imported.type === 'Identifier' ? node.imported.name : node.imported.value; -// Only ESM imports are supported. CommonJS `require()` and TypeScript import-equals declarations are intentionally ignored. +// Only runtime ESM imports are supported. CommonJS `require()` and TypeScript `import =` declarations are intentionally ignored. const getNodeSqliteImportSpecifier = (node, context) => { const definition = getVariableInfo(node, context)?.definition; if ( From f6c4dfd7050ceeee6a6d47cf98eedd5a354a6aef Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Thu, 23 Jul 2026 10:34:11 +0200 Subject: [PATCH 12/15] `no-unsafe-sqlite-interpolation`: Handle TypeScript type-only bindings --- rules/no-unsafe-sqlite-interpolation.js | 20 +++++-- test/no-unsafe-sqlite-interpolation.js | 8 +++ .../no-unsafe-sqlite-interpolation.js.md | 50 ++++++++++++++---- .../no-unsafe-sqlite-interpolation.js.snap | Bin 1788 -> 1898 bytes 4 files changed, 66 insertions(+), 12 deletions(-) diff --git a/rules/no-unsafe-sqlite-interpolation.js b/rules/no-unsafe-sqlite-interpolation.js index 68bb318627..2ee4097be1 100644 --- a/rules/no-unsafe-sqlite-interpolation.js +++ b/rules/no-unsafe-sqlite-interpolation.js @@ -19,19 +19,33 @@ const unwrapExpression = node => { return node; }; +const getNonTypeDefinitions = variable => variable.defs.filter(definition => definition.type !== 'Type'); + const getVariableInfo = (node, context) => { if (node.type !== 'Identifier') { return; } - const variable = findVariable(context.sourceCode.getScope(node), node); - if (!variable || variable.defs.length !== 1) { + let scope = context.sourceCode.getScope(node); + let variable = findVariable(scope, node); + let definitions = variable ? getNonTypeDefinitions(variable) : []; + while (variable && definitions.length === 0) { + scope = scope.upper; + if (!scope) { + return; + } + + variable = findVariable(scope, node.name); + definitions = variable ? getNonTypeDefinitions(variable) : []; + } + + if (!variable || definitions.length !== 1) { return; } return { variable, - definition: variable.defs[0], + definition: definitions[0], }; }; diff --git a/test/no-unsafe-sqlite-interpolation.js b/test/no-unsafe-sqlite-interpolation.js index 1f0924938d..162115b24e 100644 --- a/test/no-unsafe-sqlite-interpolation.js +++ b/test/no-unsafe-sqlite-interpolation.js @@ -75,6 +75,14 @@ test.snapshot({ 'import {"default" as sqlite} from "node:sqlite"; const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`);', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); database.prepare(`SELECT ${id}`);', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.prepare(customTag`SELECT ${id}`);', + { + code: 'import {DatabaseSync} from "node:sqlite"; function query() { type DatabaseSync = {}; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); }', + languageOptions: {parser: parsers.typescript}, + }, + { + code: 'import * as sqlite from "node:sqlite"; function query() { type sqlite = {}; const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); }', + languageOptions: {parser: parsers.typescript}, + }, { code: 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` as string);', languageOptions: {parser: parsers.typescript}, diff --git a/test/snapshots/no-unsafe-sqlite-interpolation.js.md b/test/snapshots/no-unsafe-sqlite-interpolation.js.md index 799f7bc8a3..4a1477d4a7 100644 --- a/test/snapshots/no-unsafe-sqlite-interpolation.js.md +++ b/test/snapshots/no-unsafe-sqlite-interpolation.js.md @@ -428,7 +428,39 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(27): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` as string); +## invalid(27): import {DatabaseSync} from "node:sqlite"; function query() { type DatabaseSync = {}; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); } + +> Input + + `␊ + 1 | import {DatabaseSync} from "node:sqlite"; function query() { type DatabaseSync = {}; const database = new DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\`); }␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; function query() { type DatabaseSync = {}; const database = new DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\`); }␊ + | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(28): import * as sqlite from "node:sqlite"; function query() { type sqlite = {}; const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); } + +> Input + + `␊ + 1 | import * as sqlite from "node:sqlite"; function query() { type sqlite = {}; const database = new sqlite.DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\`); }␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import * as sqlite from "node:sqlite"; function query() { type sqlite = {}; const database = new sqlite.DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\`); }␊ + | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(29): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` as string); > Input @@ -444,7 +476,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(28): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); +## invalid(30): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -460,7 +492,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(29): import {DatabaseSync} from "node:sqlite"; const database = new (DatabaseSync)(":memory:"); database.exec(`SELECT ${id}`); +## invalid(31): import {DatabaseSync} from "node:sqlite"; const database = new (DatabaseSync)(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -476,7 +508,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(30): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); +## invalid(32): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -492,7 +524,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(31): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`!); +## invalid(33): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`!); > Input @@ -508,7 +540,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(32): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database!.exec(`SELECT ${id}`); +## invalid(34): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database!.exec(`SELECT ${id}`); > Input @@ -524,7 +556,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(33): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` satisfies string); +## invalid(35): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` satisfies string); > Input @@ -540,7 +572,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(34): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); (database as DatabaseSync).exec(`SELECT ${id}`); +## invalid(36): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); (database as DatabaseSync).exec(`SELECT ${id}`); > Input @@ -556,7 +588,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(35): import * as sqlite from "node:sqlite"; const database = new (sqlite as typeof import("node:sqlite")).DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); +## invalid(37): import * as sqlite from "node:sqlite"; const database = new (sqlite as typeof import("node:sqlite")).DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); > Input diff --git a/test/snapshots/no-unsafe-sqlite-interpolation.js.snap b/test/snapshots/no-unsafe-sqlite-interpolation.js.snap index fa4dd0ef5aadc0927cf0c4f3390bb2109e7c2acc..966ddf22e1c2bdc40105e82a494f9970c5538f57 100644 GIT binary patch literal 1898 zcmV-w2bK6iRzV00000000BkoLg_)L=?vp5JJiu-{53bq}@Cu-K5ZxkQRxAi_}tRDsE9B z&hBh$)v-6WH)%*BN=qqiDYS*&Z=_W(RIQ*A5<fofH+_p8xo+AshGo{v)yg24Q%%S9VSdE$jY-2}V{>+G z5gM*D1tr_5vq5j#GJRGWf|_G{KGavPgJH1QEW~dsmjd9%JaQ5iBl+h}@+9N#;3r{4aFkMBR< z@nT22b-vK?(sP$Px&W%MpquV4W*L*rV(;3`mLDkpq;nBTw>!J`FwZj%vcayMTE+kX zumD8uvqTL>9Izc9OxtH})3FSn!Hi+GnAiTufr(f@O~BX(yTS8a(?00+u8fE|>bi~# z)xK(o|NRjEegXbqcv0VG7!JWuG+ov-T$aNLPZ3UcHgbXzGql6D zRXfNVh5QeUhlr-DYs{dJa;_D=LgHPK6*P*1*a*sOa5S{RE2-8G8@;mTo%T=1Vd-2* zy+$Zb8<*v`5z9MrECdP^T0E}GhpS(9bi8=+;lQ*1)*%KLzMfbhIRwhNU~ zIM-TVBJEB3ZK$+C0I6NnFDG`8?ZfWsmA0JFM|qy*gho-&xl6Mqz{T(#pfR zbRdTn9wVA=l35{(dafN#p#Z+1w?n2#h%KSi77wB<-B$WE-In==>3aU6a6mj(bL{+i z3Ozz7EKi|HIHWd7W)Lc`5T>;Idf+f)P3Y-MxN6kTD;Yx2*(+JXA@xdX+sI6RS4H`6 z2_rMMtg6>FX0u!6Hw4)du~pJXz;()b6vQ!APRS4rsZlg|rkss@GC<;!aL;i4&{Vm0 zxQs~pmd*|v3*}P(Inw&R0_ryrO$xVwZwU-vj2PY~KQZD>Td@uFaHAa0JKnU#WBE4X z;~Y_vBe7sE(+<)+L7lKjE}oigVUxr7lZf$j>F5G`9VdglX)8^F?+?V$L#6De2!!j2 zH9pqE6KR41UA)3?vYr!HBM%WI(8G}kZyO3eS-agKS_a%jjQvF9msFy;-k3$Y4^ev~ zT>zlm2h!h-PDad=WT3}O?VgTpc8dvLvqmILE(j!Yc^*S5M^t1GL~^O=mCov|h*hbn z;^6Wr<{xs}OaGvMV;vrRlLTSvRB=}2dZmgpM?np|Dj!7k2-PgP;*r&Hh~JW}cqHN5 z$}fneYgvsTUpm(b$B}wJw-X4$(8LPzy%3!q>ff5SI48CiGJPiK{owU5eVQ`c2|8_r z=Q%;APY|Gm3#5DCZsdV#S`WmgB2Gz&)MvsIMANNIP9T%d^}`ty!9qSi(1oIhD}pB7 z8)qVK^cPaRIbMnnOX)y94_rh9eN$YWW*(Vb*2j_3-4aSNb4dmTOiO3?`-t6m?iF{F zoSN{2_+CL!oLc)@uS_YIU;~NWCwz-nr&<>qg72`KDtB1?%Ms%!5DIWT?Ag80Hs?o{|>V z1OFEy=y7`B)dj7#Ou>k~Zm%`ZaRYj^w*#qJdhuWrHEgxwzo@=}*Mp zqm(;p3Pa(#X$eJhA?vQ2%CJC7FA?tDk<@eZx_=ppWt|!J?vyFbwRfjZHRw)@WSq-uL!Cb6$sRmQIS|uM&-|=Q7!v`vYaz` zhHrWelWnxUKwZn8QtrQGlj^z{R_hk(%_F; zAKu~#OCO5}00000000BkoLg@jMHI&q5JJiu-(X_;VCNyJofb+$S|k!KQc5AIxJ7|% z>~XT{#%u4alZGUsw3O0@LJPD_dL^moO;iF2i7&uI-+AEcAZN3iJdNHqrGeW z&&=;XJ7>;}H>%T?T|ULuUf!gx>00#kEweUnSY{<(EDR8Hu5LRnSsFH6W7=SJe6dzu zAyvnoBe|Mgp#!X8nJ&!@lCoW6E~#waM}|m^E|BnJ`P{%9owJ?Afm~rQxUrX>rse$9 z_{iwUn-;6nJNsLtY*~NtnIS1o0Zm2eEi_~ zju$&xSLX{IFFkjoql=ItS=LQ=7qyIOYSDLV^``47|Dm!>?h1zL~jDIjnnCqCe8P>bqBj$+X z*bXW76@C04`1qFu_!EN#ZJVGw1YJ>gXx(sV3Mc#laN69<33`OUHN{ULimxM?LX&V% z{h@rlcMS*MtWi~kD+u(4l{9&&tYuaJ|QW%!b z<=8nwcG|cszYSPEk|GhvP-ynJDkJilPvk2yGg|NVMw2>=WQas-Xa6a~G7UzCyr(&X z%+~ijvi||-{4G8GMF^5>k$*v)&+u|(jDWMZ8rmhxvbTN9zKXF3bu8@*<{`jy4Q&-F zrf{ycz69Fq_{&gngCMAOQJ+p6L0gAC)+;TY&_{WibV8#ja9yCz1{=N&Uc<1#L~i@# zoSRHxh2H>8chIbmL_OCI7eN4D;M*ZlB*>OfYKwBM`19 z+I*~sC*lM}bny!RlJ%Zg4?KiWK=*sXe{9hEq+L(pkM5uqw4w z?A<;`-C4Vp`g`pgYH;tHC0sQ$IiXz0bg7{Az3E~6GG($IblM0{ zvx82bAcz(&knVx|fd`6lJrEj-I7dRHz7w7Tn(iiY0-AiTA67vG%jx`p7m6OP@Gj}z zSPi^U%H;6oL@wMdxyf`MxCRLNCcAT*X=HL)p8!fnB^=32C7CE-S~|Pm2keGxudtcq zoC%MJ?;3#OV*9jSnNlvn1`s7x{VzE^Rq8AG} zU-x7k-G5BJ4#2p8OtDBOm*Ghu^*vP#3pHy%u+(ulgR{%|@N<4c7No9IDO0Lq9L_*B zD#aq6WsbxBcxR3M9*97((s5X*Q{!=1tXT=c()m3f@T=f2t-ZGG=7IYIpy7hlI z*b_kG8+y!syA@9vq%w+f?zTJX6hRfE0)cumDiVvzsQhs>I4{_bbLN$o_cFsZS=FR_ zEiYi77mO+R|Fde(6OYQYXt6;a zJ&i|sZ1t#^?+3mF&YuLFA5OXdE5H4K!HBz9r*?Jw*Ye=uLLtLF34a2Ne$8TgnDi*P eepm;AT*`F!nEZ$odxUpM^UZ$(m12r0X#fBacWea! From 9075f31de7dd15975d413f9e1fc873c4f6015c6f Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Thu, 23 Jul 2026 13:36:08 +0200 Subject: [PATCH 13/15] `no-unsafe-sqlite-interpolation`: Simplify constructor tracking --- rules/no-unsafe-sqlite-interpolation.js | 24 +------- test/no-unsafe-sqlite-interpolation.js | 8 +++ .../no-unsafe-sqlite-interpolation.js.md | 52 ++++++++++++++---- .../no-unsafe-sqlite-interpolation.js.snap | Bin 1898 -> 1964 bytes 4 files changed, 53 insertions(+), 31 deletions(-) diff --git a/rules/no-unsafe-sqlite-interpolation.js b/rules/no-unsafe-sqlite-interpolation.js index 2ee4097be1..8aa01f538b 100644 --- a/rules/no-unsafe-sqlite-interpolation.js +++ b/rules/no-unsafe-sqlite-interpolation.js @@ -154,19 +154,7 @@ const isSqliteNamespace = (node, context, seenVariables = new Set()) => { return isSqliteNamespace(variableInfo.initializer, context, seenVariables); }; -const getDatabaseConstructors = (newExpressions, context) => { - const databaseConstructors = new WeakSet(); - - for (const newExpression of newExpressions) { - if (isDatabaseSyncConstructor(newExpression.callee, context)) { - databaseConstructors.add(newExpression); - } - } - - return databaseConstructors; -}; - -const createDatabaseInstanceChecker = (databaseConstructors, context) => { +const createDatabaseInstanceChecker = context => { const cache = new WeakMap(); const isDatabaseInstance = (node, seenVariables = new Set()) => { @@ -176,7 +164,7 @@ const createDatabaseInstanceChecker = (databaseConstructors, context) => { } if (node.type === 'NewExpression') { - return databaseConstructors.has(node); + return isDatabaseSyncConstructor(node.callee, context); } if (node.type !== 'Identifier') { @@ -277,19 +265,13 @@ const getProblem = (callExpression, context, isDatabaseInstance, isUnsafeSqlArgu /** @param {import('eslint').Rule.RuleContext} context */ const create = context => { const callExpressions = []; - const newExpressions = []; context.on('CallExpression', node => { callExpressions.push(node); }); - context.on('NewExpression', node => { - newExpressions.push(node); - }); - context.onExit('Program', function * () { - const databaseConstructors = getDatabaseConstructors(newExpressions, context); - const isDatabaseInstance = createDatabaseInstanceChecker(databaseConstructors, context); + const isDatabaseInstance = createDatabaseInstanceChecker(context); const isUnsafeSqlArgument = createUnsafeSqlArgumentChecker(context); for (const callExpression of callExpressions) { diff --git a/test/no-unsafe-sqlite-interpolation.js b/test/no-unsafe-sqlite-interpolation.js index 162115b24e..e0f58aae12 100644 --- a/test/no-unsafe-sqlite-interpolation.js +++ b/test/no-unsafe-sqlite-interpolation.js @@ -79,10 +79,18 @@ test.snapshot({ code: 'import {DatabaseSync} from "node:sqlite"; function query() { type DatabaseSync = {}; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); }', languageOptions: {parser: parsers.typescript}, }, + { + code: 'import {DatabaseSync} from "node:sqlite"; type DatabaseSync = {}; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`);', + languageOptions: {parser: parsers.typescript}, + }, { code: 'import * as sqlite from "node:sqlite"; function query() { type sqlite = {}; const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); }', languageOptions: {parser: parsers.typescript}, }, + { + code: 'import * as sqlite from "node:sqlite"; type sqlite = {}; const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`);', + languageOptions: {parser: parsers.typescript}, + }, { code: 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` as string);', languageOptions: {parser: parsers.typescript}, diff --git a/test/snapshots/no-unsafe-sqlite-interpolation.js.md b/test/snapshots/no-unsafe-sqlite-interpolation.js.md index 4a1477d4a7..155b33e21e 100644 --- a/test/snapshots/no-unsafe-sqlite-interpolation.js.md +++ b/test/snapshots/no-unsafe-sqlite-interpolation.js.md @@ -444,7 +444,23 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(28): import * as sqlite from "node:sqlite"; function query() { type sqlite = {}; const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); } +## invalid(28): import {DatabaseSync} from "node:sqlite"; type DatabaseSync = {}; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); + +> Input + + `␊ + 1 | import {DatabaseSync} from "node:sqlite"; type DatabaseSync = {}; const database = new DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; type DatabaseSync = {}; const database = new DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\`);␊ + | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(29): import * as sqlite from "node:sqlite"; function query() { type sqlite = {}; const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); } > Input @@ -460,7 +476,23 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(29): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` as string); +## invalid(30): import * as sqlite from "node:sqlite"; type sqlite = {}; const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); + +> Input + + `␊ + 1 | import * as sqlite from "node:sqlite"; type sqlite = {}; const database = new sqlite.DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import * as sqlite from "node:sqlite"; type sqlite = {}; const database = new sqlite.DatabaseSync(":memory:"); database.exec(\`SELECT ${id}\`);␊ + | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(31): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` as string); > Input @@ -476,7 +508,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(30): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); +## invalid(32): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -492,7 +524,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(31): import {DatabaseSync} from "node:sqlite"; const database = new (DatabaseSync)(":memory:"); database.exec(`SELECT ${id}`); +## invalid(33): import {DatabaseSync} from "node:sqlite"; const database = new (DatabaseSync)(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -508,7 +540,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(32): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); +## invalid(34): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -524,7 +556,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(33): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`!); +## invalid(35): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`!); > Input @@ -540,7 +572,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(34): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database!.exec(`SELECT ${id}`); +## invalid(36): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database!.exec(`SELECT ${id}`); > Input @@ -556,7 +588,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(35): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` satisfies string); +## invalid(37): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` satisfies string); > Input @@ -572,7 +604,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(36): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); (database as DatabaseSync).exec(`SELECT ${id}`); +## invalid(38): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); (database as DatabaseSync).exec(`SELECT ${id}`); > Input @@ -588,7 +620,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(37): import * as sqlite from "node:sqlite"; const database = new (sqlite as typeof import("node:sqlite")).DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); +## invalid(39): import * as sqlite from "node:sqlite"; const database = new (sqlite as typeof import("node:sqlite")).DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); > Input diff --git a/test/snapshots/no-unsafe-sqlite-interpolation.js.snap b/test/snapshots/no-unsafe-sqlite-interpolation.js.snap index 966ddf22e1c2bdc40105e82a494f9970c5538f57..fd5ace4f42c4a38d25297b136a571d4a557d5068 100644 GIT binary patch literal 1964 zcmV;d2UGY#RzVXfFFwp00000000BkoLg_)L=?vp5JJiu-(a$;#BLsv?xsMSkQRxAi_}tRDsE9B z&hBh$y0JI5w{1uwN=qqiDYS*&Z=|IcqE=7|2_as2=sOR59kg*g@yX649@}$fn8(Ck z{m;zrKesdE#rjmktseBRym*HOcF>?luG`Kbt6|rQ<)bq&)Z;kC4BX*69kWF)TZDF!BZ2OMY^bfd!f9y`J{SzPF zf41xSu6F5uuIq(oE_d}1QYQ1J>F%KoYl=4L+fK6;gvvkaUI5aa?w(!L_pSYOsAs#8 zF%Uw?JV7d-Br0UoC5{^q+X<-GbQ@Mc$sw!JqJH~NE}4wg(lv3ypyDVjsj6itsdEsth!!V`ego%NhxMhIL}`~af(Dv>D+ z2?y05CRpWmWR<~HtMs)SBm9-*EfM~=U;SvY1n9cH#v>+a=bGaLkbg*I4x{2gcZD`P z+>Gq7Wz`PqS|R&@@et5-b&VO!QO>o(mq5HHvVuWTAR9rO4UR@Ocu8vgu-=q4Yqx(o z4ommE^co>QZCsY$1T1gOun@>mX#TirBl2lPuV8FK?Z`U7JOFrJ zLED9r6wbBQ7eIRxe;Z0R2!d)C{mY51X#21``jssw%u$|YIbl!~xGvCVgT=@O+c0b} zQCJzA3;Qxy;Sr$eCYlw}sOQ?@6bRsRd^@Cy1lbZ=ZSjC*={Do1={C*PZO;!Dgbm`; zs_SIWQ;P&_@nZ+9Z7g30IBgc_l>%CVM4KIH+EcwvEd4 zcUhGGR?srz%c}Z4>u`Fj{01OfM7D~21YD<_2SFUu<&+fBpc=)1G36}t$smePqCLaT zrYUpna2b&F4W1p=7s{pnQ=s)-4b*QS8WnB<-wGJM$Qa(DJ~86YG-4a*(MCD!cl?LkwOuInyBzD3gxp-{0g+&eHj~U}~>F6A59VUbPnTAY)?+?JyL#^yf z1j6;i8XueC37MdXDPG|hRpZ1}<{^XxdejopYs2AR)^2x*mH~GGV?QGKMM^Z+8;61J z1K8e>3xH_%f%3W0Nya=%27028Du$iqGrb))E^1n;GEj;7t8=WsQkviHsQ7Bj58Ua!*8Yu0;cEb?jSR;G>u*x)pyE6DI~@ zf+dvm|10BvTk_pvQAf4%$~vu)adFKXPyPg4ElKVuYYc_!l0^{Bg|xfQI>R!;I2}c~ zldiFEW=iDcaT=x5G(Q8(E=rmPM>W?Uvq1P~Y5jo|0nu*-yqwLThR=$JHND)}%Q{o! zY_uuOl(W&N8kK3m(pm+_-!hI7??=BH%G$nhk9`S%aT1wgkxnkd`+(GUbTKT{YyiPZ z`{5k!Ef?eOMHN|6nmgB~R7XFYgKAWYMLa9*hn3!42DKZtKmv-j_QOJ*2KU2a%~}Xn z&hH7vueKa{FAv=B0S!mBPJ?>|#O?+fUo&I&+pBrXpp;RRyl8LIDS|FW1p>`vR3sLa zQT6>O`-aIN$~|4~y4SY?+ppVnz3m0;Zpfb%8BxkQ$czi?i8!Sa{)@ZI{# yuNA?|rBaT$lHUPF_wu;1qC5(&AFhHxPUm`~MSVn?J;E!>eDfc?QFWudbpQagrq6%? literal 1898 zcmV-w2bK6iRzV00000000BkoLg_)L=?vp5JJiu-{53bq}@Cu-K5ZxkQRxAi_}tRDsE9B z&hBh$)v-6WH)%*BN=qqiDYS*&Z=_W(RIQ*A5<fofH+_p8xo+AshGo{v)yg24Q%%S9VSdE$jY-2}V{>+G z5gM*D1tr_5vq5j#GJRGWf|_G{KGavPgJH1QEW~dsmjd9%JaQ5iBl+h}@+9N#;3r{4aFkMBR< z@nT22b-vK?(sP$Px&W%MpquV4W*L*rV(;3`mLDkpq;nBTw>!J`FwZj%vcayMTE+kX zumD8uvqTL>9Izc9OxtH})3FSn!Hi+GnAiTufr(f@O~BX(yTS8a(?00+u8fE|>bi~# z)xK(o|NRjEegXbqcv0VG7!JWuG+ov-T$aNLPZ3UcHgbXzGql6D zRXfNVh5QeUhlr-DYs{dJa;_D=LgHPK6*P*1*a*sOa5S{RE2-8G8@;mTo%T=1Vd-2* zy+$Zb8<*v`5z9MrECdP^T0E}GhpS(9bi8=+;lQ*1)*%KLzMfbhIRwhNU~ zIM-TVBJEB3ZK$+C0I6NnFDG`8?ZfWsmA0JFM|qy*gho-&xl6Mqz{T(#pfR zbRdTn9wVA=l35{(dafN#p#Z+1w?n2#h%KSi77wB<-B$WE-In==>3aU6a6mj(bL{+i z3Ozz7EKi|HIHWd7W)Lc`5T>;Idf+f)P3Y-MxN6kTD;Yx2*(+JXA@xdX+sI6RS4H`6 z2_rMMtg6>FX0u!6Hw4)du~pJXz;()b6vQ!APRS4rsZlg|rkss@GC<;!aL;i4&{Vm0 zxQs~pmd*|v3*}P(Inw&R0_ryrO$xVwZwU-vj2PY~KQZD>Td@uFaHAa0JKnU#WBE4X z;~Y_vBe7sE(+<)+L7lKjE}oigVUxr7lZf$j>F5G`9VdglX)8^F?+?V$L#6De2!!j2 zH9pqE6KR41UA)3?vYr!HBM%WI(8G}kZyO3eS-agKS_a%jjQvF9msFy;-k3$Y4^ev~ zT>zlm2h!h-PDad=WT3}O?VgTpc8dvLvqmILE(j!Yc^*S5M^t1GL~^O=mCov|h*hbn z;^6Wr<{xs}OaGvMV;vrRlLTSvRB=}2dZmgpM?np|Dj!7k2-PgP;*r&Hh~JW}cqHN5 z$}fneYgvsTUpm(b$B}wJw-X4$(8LPzy%3!q>ff5SI48CiGJPiK{owU5eVQ`c2|8_r z=Q%;APY|Gm3#5DCZsdV#S`WmgB2Gz&)MvsIMANNIP9T%d^}`ty!9qSi(1oIhD}pB7 z8)qVK^cPaRIbMnnOX)y94_rh9eN$YWW*(Vb*2j_3-4aSNb4dmTOiO3?`-t6m?iF{F zoSN{2_+CL!oLc)@uS_YIU;~NWCwz-nr&<>qg72`KDtB1?%Ms%!5DIWT?Ag80Hs?o{|>V z1OFEy=y7`B)dj7#Ou>k~Zm%`ZaRYj^w*#qJdhuWrHEgxwzo@=}*Mp zqm(;p3Pa(#X$eJhA?vQ2%CJC7FA?tDk<@eZx_=ppWt|!J?vyFbwRfjZHRw)@WSq-uL!Cb6$sRmQIS|uM&-|=Q7!v`vYaz` zhHrWelWnxUKwZn8Qt Date: Thu, 23 Jul 2026 13:41:42 +0200 Subject: [PATCH 14/15] `no-unsafe-sqlite-interpolation`: Add optional computed member tests --- test/no-unsafe-sqlite-interpolation.js | 2 + .../no-unsafe-sqlite-interpolation.js.md | 94 ++++++++++++------ .../no-unsafe-sqlite-interpolation.js.snap | Bin 1964 -> 2035 bytes 3 files changed, 65 insertions(+), 31 deletions(-) diff --git a/test/no-unsafe-sqlite-interpolation.js b/test/no-unsafe-sqlite-interpolation.js index e0f58aae12..f6a69b61ea 100644 --- a/test/no-unsafe-sqlite-interpolation.js +++ b/test/no-unsafe-sqlite-interpolation.js @@ -57,6 +57,8 @@ test.snapshot({ 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const query = `SELECT * FROM users WHERE id = ${id}`; const alias = query; database.prepare(alias);', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database?.exec(`SELECT * FROM users WHERE id = ${id}`);', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec?.(`SELECT * FROM users WHERE id = ${id}`);', + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database?.["exec"](`SELECT * FROM users WHERE id = ${id}`);', + 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database["prepare"]?.(`SELECT * FROM users WHERE id = ${id}`);', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database["prepare"](`SELECT * FROM users WHERE id = ${id}`);', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${first} FROM users WHERE id = ${second}`);', 'import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(/* keep */ `SELECT * FROM users WHERE id = ${id}`);', diff --git a/test/snapshots/no-unsafe-sqlite-interpolation.js.md b/test/snapshots/no-unsafe-sqlite-interpolation.js.md index 155b33e21e..be65701dfa 100644 --- a/test/snapshots/no-unsafe-sqlite-interpolation.js.md +++ b/test/snapshots/no-unsafe-sqlite-interpolation.js.md @@ -132,7 +132,39 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(9): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database["prepare"](`SELECT * FROM users WHERE id = ${id}`); +## invalid(9): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database?.["exec"](`SELECT * FROM users WHERE id = ${id}`); + +> Input + + `␊ + 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database?.["exec"](\`SELECT * FROM users WHERE id = ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database?.["exec"](\`SELECT * FROM users WHERE id = ${id}\`);␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(10): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database["prepare"]?.(`SELECT * FROM users WHERE id = ${id}`); + +> Input + + `␊ + 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database["prepare"]?.(\`SELECT * FROM users WHERE id = ${id}\`);␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database["prepare"]?.(\`SELECT * FROM users WHERE id = ${id}\`);␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ + ` + +## invalid(11): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database["prepare"](`SELECT * FROM users WHERE id = ${id}`); > Input @@ -148,7 +180,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(10): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${first} FROM users WHERE id = ${second}`); +## invalid(12): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${first} FROM users WHERE id = ${second}`); > Input @@ -164,7 +196,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(11): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(/* keep */ `SELECT * FROM users WHERE id = ${id}`); +## invalid(13): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(/* keep */ `SELECT * FROM users WHERE id = ${id}`); > Input @@ -180,7 +212,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(12): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(String.raw`SELECT * FROM users WHERE id = ${id}`); +## invalid(14): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(String.raw`SELECT * FROM users WHERE id = ${id}`); > Input @@ -196,7 +228,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(13): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.prepare(String.raw`SELECT 1`); +## invalid(15): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.prepare(String.raw`SELECT 1`); > Input @@ -212,7 +244,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(14): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const sql = `SELECT ${id}`; database.exec(sql); +## invalid(16): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const sql = `SELECT ${id}`; database.exec(sql); > Input @@ -228,7 +260,7 @@ Generated by [AVA](https://avajs.dev). | ^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(15): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const sql = `SELECT ${id}`; const alias = sql; database.prepare(alias); +## invalid(17): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const sql = `SELECT ${id}`; const alias = sql; database.prepare(alias); > Input @@ -244,7 +276,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(16): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const alias = database; alias.exec(`SELECT ${id}`); +## invalid(18): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const alias = database; alias.exec(`SELECT ${id}`); > Input @@ -260,7 +292,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(17): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database["exec"](`SELECT ${id}`); +## invalid(19): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database["exec"](`SELECT ${id}`); > Input @@ -276,7 +308,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(18): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const method = "exec"; database[method](`SELECT ${id}`); +## invalid(20): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); const method = "exec"; database[method](`SELECT ${id}`); > Input @@ -292,7 +324,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(19): import {DatabaseSync} from "node:sqlite"; const Database = DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`); +## invalid(21): import {DatabaseSync} from "node:sqlite"; const Database = DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -308,7 +340,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(20): import * as sqlite from "node:sqlite"; const alias = sqlite; const database = new alias.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); +## invalid(22): import * as sqlite from "node:sqlite"; const alias = sqlite; const database = new alias.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -324,7 +356,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(21): import * as sqlite from "node:sqlite"; const Database = sqlite.DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`); +## invalid(23): import * as sqlite from "node:sqlite"; const Database = sqlite.DatabaseSync; const database = new Database(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -340,7 +372,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(22): import * as sqlite from "node:sqlite"; const database = new sqlite["DatabaseSync"](":memory:"); database.exec(`SELECT ${id}`); +## invalid(24): import * as sqlite from "node:sqlite"; const database = new sqlite["DatabaseSync"](":memory:"); database.exec(`SELECT ${id}`); > Input @@ -356,7 +388,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(23): import {"DatabaseSync" as Database} from "node:sqlite"; const database = new Database(":memory:"); database.exec(`SELECT ${id}`); +## invalid(25): import {"DatabaseSync" as Database} from "node:sqlite"; const database = new Database(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -372,7 +404,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(24): import {"default" as sqlite} from "node:sqlite"; const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); +## invalid(26): import {"default" as sqlite} from "node:sqlite"; const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -388,7 +420,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(25): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); database.prepare(`SELECT ${id}`); +## invalid(27): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); database.prepare(`SELECT ${id}`); > Input @@ -412,7 +444,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(26): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.prepare(customTag`SELECT ${id}`); +## invalid(28): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.prepare(customTag`SELECT ${id}`); > Input @@ -428,7 +460,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(27): import {DatabaseSync} from "node:sqlite"; function query() { type DatabaseSync = {}; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); } +## invalid(29): import {DatabaseSync} from "node:sqlite"; function query() { type DatabaseSync = {}; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); } > Input @@ -444,7 +476,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(28): import {DatabaseSync} from "node:sqlite"; type DatabaseSync = {}; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); +## invalid(30): import {DatabaseSync} from "node:sqlite"; type DatabaseSync = {}; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -460,7 +492,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(29): import * as sqlite from "node:sqlite"; function query() { type sqlite = {}; const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); } +## invalid(31): import * as sqlite from "node:sqlite"; function query() { type sqlite = {}; const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); } > Input @@ -476,7 +508,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(30): import * as sqlite from "node:sqlite"; type sqlite = {}; const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); +## invalid(32): import * as sqlite from "node:sqlite"; type sqlite = {}; const database = new sqlite.DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -492,7 +524,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(31): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` as string); +## invalid(33): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` as string); > Input @@ -508,7 +540,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(32): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); +## invalid(34): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -524,7 +556,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(33): import {DatabaseSync} from "node:sqlite"; const database = new (DatabaseSync)(":memory:"); database.exec(`SELECT ${id}`); +## invalid(35): import {DatabaseSync} from "node:sqlite"; const database = new (DatabaseSync)(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -540,7 +572,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(34): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); +## invalid(36): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); > Input @@ -556,7 +588,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(35): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`!); +## invalid(37): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}`!); > Input @@ -572,7 +604,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(36): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database!.exec(`SELECT ${id}`); +## invalid(38): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database!.exec(`SELECT ${id}`); > Input @@ -588,7 +620,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(37): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` satisfies string); +## invalid(39): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); database.exec(`SELECT ${id}` satisfies string); > Input @@ -604,7 +636,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(38): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); (database as DatabaseSync).exec(`SELECT ${id}`); +## invalid(40): import {DatabaseSync} from "node:sqlite"; const database = new DatabaseSync(":memory:"); (database as DatabaseSync).exec(`SELECT ${id}`); > Input @@ -620,7 +652,7 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^ Do not interpolate values into \`node:sqlite\` SQL strings.␊ ` -## invalid(39): import * as sqlite from "node:sqlite"; const database = new (sqlite as typeof import("node:sqlite")).DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); +## invalid(41): import * as sqlite from "node:sqlite"; const database = new (sqlite as typeof import("node:sqlite")).DatabaseSync(":memory:"); database.exec(`SELECT ${id}`); > Input diff --git a/test/snapshots/no-unsafe-sqlite-interpolation.js.snap b/test/snapshots/no-unsafe-sqlite-interpolation.js.snap index fd5ace4f42c4a38d25297b136a571d4a557d5068..dd01498aabfacbe555211d01719bdd4139be2238 100644 GIT binary patch literal 2035 zcmVUfk8 zK61ZeRUeB800000000Bkom+1kMHI&q5JJiu-(X_(!Ola1odP8xEfNVAsin|V+@e4> z_PAZT*>!f;NkbASEv2-j&=z{Xr&mG?R6;_CS04J#178O@&QA8kGs(`b=gcsVvDf;a zng4HYXJ&J&I_Y@j1Hr|@5)JLpp$D(n?u_Nwm3*-O2|JvRtRW%YYxh`4l?#2?G&2Bzqg=g$q~3WMy6z4Rb0=O;!- zc8H+lw^8TA6dq#*|Awy)#yj@wGC=A+xYt@4NUKku+sx*G$gL_YR zJloMIozHYU_tb@sE<%cA!8F}n)UhV1L*H_1^{}b@!_Gw@-R$pFA=VtEUMv`tD8=gudu#2)c| z&nLycVr2h&k^M^o`x7f*b(^9&1Wi%%Y0dI!1`-|vJ1uQQf*B!jq4)tr@l_%y3<(F7 z4--(i5uwt*hDuMPF`B=Uyd|2S_NpH(mVvvjtYgF^?OZsH0{Jr{IE;z|oeFI@T#Inn zx`u(eJJOFOGvRt4G6>o*I22*i(Dq?>^eS6Un4>()a>Ae}a4FD-!OaMRmvKgdv0S4i za^o3D_!*e#D&lA$EuIU*F`)mTjuAmV5M&;7B5{Xh=$^-5Vl1~hVa<(a8X$fFwz`3a zMLPXlGERXIKF22`Wi0q`=;ULG@qv=UjohS6kb-oX49kc8!*`|WK`rM;F59%1aVjwDJh~s z1;v0dJ0$C&3)a@8$4QnAC=tq~EE3j&E;KaT>H z!#XkuBDvV^Rqoa6?3$a>9CGvPQ#9P~HJ1L(`i)h1^OGbN#y(z@R=HWni&95{ZFpTA zusdv}G`Zs8wRVUjP(DqW?$ot0!n2&ZHYW&T1O>_&xXl=lzSJD+ia6H@m5vFIfSazTA^}Z4 zmxq%ef`x26;Dura70sfYjgyRxt!=sRKbDI(OKvDzo_mGWq~<*$3GI6knBw^QX}vb3TnAe~>>lAPUY}|mCdTmbS?1$- z{xw&jRjclVxwS4sqJmSt`a|sHtH2(oTD+`NE!WRB5d4wY&svph@-&LC*Iv7Ya^sg@ z!>w67vDc3nafVDj0<%sYWR3=v2~`x73_HtbdOK`h)U;HouN3uHr&)i6b3Hq%_;Ni6 zy{Wy{zRgGB%fL)0@sINfwQ~C)3go`0@kF0kH!-$L3pnVZyy5i)!zJP_i06C;A~XhujZD&_oGA7Rn|`?r zJ4(YSc#$%SZpANB#)*NLfP`}Y|H}MdOukzz>Zn#9*`PHtF0Ogw$)CVi%aS|F8bjey zatlOrHtnvn&ajLCr==(-=`tfpA}^2AD6P=k0X91)2@Q^FE+2D1_@`<4K#G9qM*%Nq z(~o!u8`ktnW3T8;k+adJG*iw-pK4T33zpU@eEco*G2;E`SDLc6Z{A~{2gW#t>|&8l zu7}5g)VFncSg6?m2P^G|+i-6=AAc^Y$b!<`xi+Ob`r$UHMs=~s&r17YrFWM>?M5w- zz{OhoVWCcg`(d$WEe=-h-=oaG+H&OGJmY>3+;B+iG`L&f*xf+mt7go8yERW4lzJ2; zFWQ}SilED*0)b|FR3sMFqw4!n_70PNlzY0|b#GvWc2KqHM%xS6?=X=R{Qo&g#1oI| zY0+kbI**@c|BOuO{oXwLX6$Tl<7vE~}X9@@XJ?63^w*>QQaJnei3) ze3|)NBFo5MIIlin1uJ{_UUl`?^5Ee@p^dqc-+_(pw&Ti*@+i1GTmpfdXzPs@^$}@i RgjbYt^B-cIfJX3p005R+^4I_X literal 1964 zcmV;d2UGY#RzVXfFFwp00000000BkoLg_)L=?vp5JJiu-(a$;#BLsv?xsMSkQRxAi_}tRDsE9B z&hBh$y0JI5w{1uwN=qqiDYS*&Z=|IcqE=7|2_as2=sOR59kg*g@yX649@}$fn8(Ck z{m;zrKesdE#rjmktseBRym*HOcF>?luG`Kbt6|rQ<)bq&)Z;kC4BX*69kWF)TZDF!BZ2OMY^bfd!f9y`J{SzPF zf41xSu6F5uuIq(oE_d}1QYQ1J>F%KoYl=4L+fK6;gvvkaUI5aa?w(!L_pSYOsAs#8 zF%Uw?JV7d-Br0UoC5{^q+X<-GbQ@Mc$sw!JqJH~NE}4wg(lv3ypyDVjsj6itsdEsth!!V`ego%NhxMhIL}`~af(Dv>D+ z2?y05CRpWmWR<~HtMs)SBm9-*EfM~=U;SvY1n9cH#v>+a=bGaLkbg*I4x{2gcZD`P z+>Gq7Wz`PqS|R&@@et5-b&VO!QO>o(mq5HHvVuWTAR9rO4UR@Ocu8vgu-=q4Yqx(o z4ommE^co>QZCsY$1T1gOun@>mX#TirBl2lPuV8FK?Z`U7JOFrJ zLED9r6wbBQ7eIRxe;Z0R2!d)C{mY51X#21``jssw%u$|YIbl!~xGvCVgT=@O+c0b} zQCJzA3;Qxy;Sr$eCYlw}sOQ?@6bRsRd^@Cy1lbZ=ZSjC*={Do1={C*PZO;!Dgbm`; zs_SIWQ;P&_@nZ+9Z7g30IBgc_l>%CVM4KIH+EcwvEd4 zcUhGGR?srz%c}Z4>u`Fj{01OfM7D~21YD<_2SFUu<&+fBpc=)1G36}t$smePqCLaT zrYUpna2b&F4W1p=7s{pnQ=s)-4b*QS8WnB<-wGJM$Qa(DJ~86YG-4a*(MCD!cl?LkwOuInyBzD3gxp-{0g+&eHj~U}~>F6A59VUbPnTAY)?+?JyL#^yf z1j6;i8XueC37MdXDPG|hRpZ1}<{^XxdejopYs2AR)^2x*mH~GGV?QGKMM^Z+8;61J z1K8e>3xH_%f%3W0Nya=%27028Du$iqGrb))E^1n;GEj;7t8=WsQkviHsQ7Bj58Ua!*8Yu0;cEb?jSR;G>u*x)pyE6DI~@ zf+dvm|10BvTk_pvQAf4%$~vu)adFKXPyPg4ElKVuYYc_!l0^{Bg|xfQI>R!;I2}c~ zldiFEW=iDcaT=x5G(Q8(E=rmPM>W?Uvq1P~Y5jo|0nu*-yqwLThR=$JHND)}%Q{o! zY_uuOl(W&N8kK3m(pm+_-!hI7??=BH%G$nhk9`S%aT1wgkxnkd`+(GUbTKT{YyiPZ z`{5k!Ef?eOMHN|6nmgB~R7XFYgKAWYMLa9*hn3!42DKZtKmv-j_QOJ*2KU2a%~}Xn z&hH7vueKa{FAv=B0S!mBPJ?>|#O?+fUo&I&+pBrXpp;RRyl8LIDS|FW1p>`vR3sLa zQT6>O`-aIN$~|4~y4SY?+ppVnz3m0;Zpfb%8BxkQ$czi?i8!Sa{)@ZI{# yuNA?|rBaT$lHUPF_wu;1qC5(&AFhHxPUm`~MSVn?J;E!>eDfc?QFWudbpQagrq6%? From f1d1b0507d52192858bf51a6094258a15dbfc689 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Thu, 23 Jul 2026 14:22:52 +0200 Subject: [PATCH 15/15] `no-unsafe-sqlite-interpolation`: Clarify variable binding handling --- docs/rules/no-unsafe-sqlite-interpolation.md | 2 +- rules/no-unsafe-sqlite-interpolation.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/rules/no-unsafe-sqlite-interpolation.md b/docs/rules/no-unsafe-sqlite-interpolation.md index c083f27cfe..449c4ee39e 100644 --- a/docs/rules/no-unsafe-sqlite-interpolation.md +++ b/docs/rules/no-unsafe-sqlite-interpolation.md @@ -25,7 +25,7 @@ const sql = database.createTagStore(); sql.get`SELECT * FROM users WHERE id = ${id}`; ``` -This rule only recognizes runtime `DatabaseSync` instances imported from exactly `node:sqlite`, including simple `const` aliases of constructors, namespaces, and instances, and only checks `exec()` and `prepare()`. It intentionally ignores CommonJS `require()`, TypeScript import-equals declarations, string concatenation, generic query methods, dynamic imports, factory-created databases, and mutable assignment flows. +It only recognizes `DatabaseSync` instances imported from exactly `node:sqlite`, including simple `const` aliases of constructors, namespaces, and instances. It checks only `exec()` and `prepare()` and ignores string concatenation, generic query methods, dynamic imports, factory-created databases, and mutable assignment flows. ## Examples diff --git a/rules/no-unsafe-sqlite-interpolation.js b/rules/no-unsafe-sqlite-interpolation.js index 8aa01f538b..477deaf542 100644 --- a/rules/no-unsafe-sqlite-interpolation.js +++ b/rules/no-unsafe-sqlite-interpolation.js @@ -21,6 +21,7 @@ const unwrapExpression = node => { const getNonTypeDefinitions = variable => variable.defs.filter(definition => definition.type !== 'Type'); +/** Get the unique value binding and definition for an identifier, skipping type-only bindings. */ const getVariableInfo = (node, context) => { if (node.type !== 'Identifier') { return;