diff --git a/docs/rules/no-unsafe-sqlite-interpolation.md b/docs/rules/no-unsafe-sqlite-interpolation.md new file mode 100644 index 0000000000..449c4ee39e --- /dev/null +++ b/docs/rules/no-unsafe-sqlite-interpolation.md @@ -0,0 +1,57 @@ +# 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 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: + +```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}`; +``` + +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 + +```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/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..477deaf542 --- /dev/null +++ b/rules/no-unsafe-sqlite-interpolation.js @@ -0,0 +1,304 @@ +import {findVariable, getPropertyName} from '@eslint-community/eslint-utils'; +import {isRuntimeImportSpecifier, isTypeScriptExpressionWrapper} 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 unwrapExpression = node => { + while (node && (node.type === 'ChainExpression' || node.type === 'TSInstantiationExpression' || isTypeScriptExpressionWrapper(node))) { + node = node.expression; + } + + return 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; + } + + 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: definitions[0], + }; +}; + +const getConstVariableInfo = (node, context) => { + const variableInfo = getVariableInfo(node, context); + const definition = variableInfo?.definition; + if ( + !variableInfo + || definition.type !== 'Variable' + || definition.node.type !== 'VariableDeclarator' + || definition.parent.type !== 'VariableDeclaration' + || definition.parent.kind !== 'const' + ) { + return; + } + + return { + ...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; + +// 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 ( + !definition + || definition.type !== 'ImportBinding' + || definition.parent.type !== 'ImportDeclaration' + || definition.parent.source.value !== 'node:sqlite' + || !isRuntimeImportSpecifier(definition.node) + ) { + return; + } + + return definition.node; +}; + +const isDatabaseSyncImport = (node, context) => { + const specifier = getNodeSqliteImportSpecifier(node, context); + return specifier?.type === 'ImportSpecifier' + && getImportSpecifierName(specifier) === 'DatabaseSync'; +}; + +const isSqliteNamespaceImport = (node, context) => { + const specifier = getNodeSqliteImportSpecifier(node, context); + + return specifier?.type === 'ImportNamespaceSpecifier' + || specifier?.type === 'ImportDefaultSpecifier' + || ( + specifier?.type === 'ImportSpecifier' + && getImportSpecifierName(specifier) === 'default' + ); +}; + +const isDatabaseSyncConstructor = (node, context, seenVariables = new Set()) => { + const callee = unwrapExpression(node); + if (!callee) { + return false; + } + + if (callee.type === 'Identifier') { + if (isDatabaseSyncImport(callee, context)) { + return true; + } + + const variableInfo = getSimpleConstVariableInfo(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') { + return false; + } + + return isSqliteNamespace(callee.object, context); +}; + +const isSqliteNamespace = (node, context, seenVariables = new Set()) => { + node = unwrapExpression(node); + if (node?.type !== 'Identifier') { + return false; + } + + if (isSqliteNamespaceImport(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 createDatabaseInstanceChecker = context => { + const cache = new WeakMap(); + + const isDatabaseInstance = (node, seenVariables = new Set()) => { + node = unwrapExpression(node); + if (!node) { + return false; + } + + if (node.type === 'NewExpression') { + return isDatabaseSyncConstructor(node.callee, context); + } + + if (node.type !== 'Identifier') { + return false; + } + + const variableInfo = getSimpleConstVariableInfo(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 = getSimpleConstVariableInfo(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 * () { + const isDatabaseInstance = createDatabaseInstanceChecker(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..f6a69b61ea --- /dev/null +++ b/test/no-unsafe-sqlite-interpolation.js @@ -0,0 +1,133 @@ +/* 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:"); 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("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}`);', + '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}`);', + languageOptions: {parser: parsers.typescript}, + }, + { + 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}`);', + '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();', + '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}`);', + '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}`);', + '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}`);', + '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"; 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}, + }, + { + 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 ${id}`);', + 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}, + }, + { + 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 new file mode 100644 index 0000000000..be65701dfa --- /dev/null +++ b/test/snapshots/no-unsafe-sqlite-interpolation.js.md @@ -0,0 +1,669 @@ +# 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): 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(7): 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(8): 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(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 + + `␊ + 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(12): 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(13): 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(14): 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(15): 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(16): 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(17): 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(18): 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(19): 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(20): 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(21): 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(22): 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(23): 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(24): 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(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): 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(29): 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(30): 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(31): 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(32): 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(33): 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(34): 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(35): 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(36): 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(37): 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(38): 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(39): 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.␊ + ` + +## invalid(40): 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(41): 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 new file mode 100644 index 0000000000..dd01498aab Binary files /dev/null and b/test/snapshots/no-unsafe-sqlite-interpolation.js.snap differ