{"record":{"id":"3c36cfdb883b433f","repo":"FlowiseAI/Flowise","slug":"invalid-sql-statement-only-read-only-select-with","errorCode":null,"errorMessage":"Invalid SQL statement: only read-only SELECT/WITH statements are allowed","messagePattern":"Invalid SQL statement: only read-only SELECT/WITH statements are allowed","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/components/src/validator.ts","lineNumber":393,"sourceCode":" *\n * @param {string} sql The SQL statement to validate\n * @throws {Error} If the statement is not a single read-only SELECT/WITH statement\n */\nexport const assertReadOnlySqlStatement = (sql: string): void => {\n    if (!sql || typeof sql !== 'string') {\n        throw new Error('Invalid SQL statement: statement is required and must be a string')\n    }\n\n    let trimmed = sql.trim()\n    // Strip at most one trailing semicolon (+ trailing whitespace)\n    trimmed = trimmed.replace(/;\\s*$/, '')\n\n    if (trimmed.includes(';')) {\n        throw new Error('Invalid SQL statement: multiple statements are not allowed')\n    }\n\n    if (!/^(SELECT|WITH)\\b/i.test(trimmed)) {\n        throw new Error('Invalid SQL statement: only read-only SELECT/WITH statements are allowed')\n    }\n\n    if (/load_extension\\s*\\(/i.test(trimmed)) {\n        throw new Error('Invalid SQL statement: load_extension is not allowed')\n    }\n}\n\n/**\n * Sanitize a file name to prevent path traversal attacks.\n * Strips common storage prefixes, extracts the basename, runs it through\n * the `sanitize-filename` package, and rejects anything that still looks unsafe.\n *\n * @param {string} name The file name to sanitize\n */\nexport const sanitizeFileName = (name: string): string => {\n    if (!name || typeof name !== 'string') {\n        throw new Error('Invalid file name: name is required')\n    }","sourceCodeStart":375,"sourceCodeEnd":411,"githubUrl":"https://github.com/FlowiseAI/Flowise/blob/abe4a8601a058047b350c260676826e21dd14101/packages/components/src/validator.ts#L375-L411","documentation":"Thrown by assertReadOnlySqlStatement (packages/components/src/validator.ts:393) when the trimmed statement does not start with SELECT or WITH (case-insensitive, word boundary). The guard ensures only read-only queries run, blocking INSERT/UPDATE/DELETE/PRAGMA/ATTACH/VACUUM and similar — defenses against a compromised LLM writing files via ATTACH DATABASE or VACUUM INTO.","triggerScenarios":"The LLM returns an INSERT/UPDATE/DELETE/CREATE/PRAGMA/ATTACH/VACUUM/etc. statement, or a statement prefixed by a comment/whitespace so it doesn't start with SELECT/WITH.","commonSituations":"LLM tries to 'fix' the schema with CREATE/ALTER; prompt encourages write operations; comment-prefixed statements; WITH clauses wrapped in leading parens.","solutions":["Restrict the LLM via system/prompt instructions to SELECT-only against this chain.","Ensure the database user has read-only grants as defense-in-depth.","Strip leading line comments before validation if you intentionally allow comments.","If writes are genuinely needed, use a different node/endpoint — this chain is read-only by design."],"exampleFix":"// before\nsql = 'INSERT INTO logs VALUES (1)'   // LLM tried a write\n\n// after\nsql = 'SELECT * FROM logs WHERE id = 1'   // read-only","handlingStrategy":"validation","validationCode":"const t = String(sql ?? '').trim();\nif (!/^(SELECT|WITH)\\b/i.test(t)) throw new Error('only SELECT/WITH allowed');\nassertReadOnlySqlStatement(t);","typeGuard":"const isReadOnlyStart = (s: unknown): s is string => typeof s === 'string' && /^(SELECT|WITH)\\b/i.test(s.trim());","tryCatchPattern":"try { assertReadOnlySqlStatement(sql) } catch (e) { if (e instanceof Error && /only read-only/.test(e.message)) { throw new Error('chain is read-only; use a write-capable node') } else throw e }","preventionTips":["Prompt the LLM to emit SELECT-only against this chain.","Grant read-only DB permissions as defense-in-depth.","Strip leading comments before validation if you allow comments."],"tags":["sql","security","validation","sql-injection","read-only","sqlite","database","llm","flowise"],"backgroundTag":null,"analyzedSha":"abe4a8601a058047b350c260676826e21dd14101","analyzedAt":"2026-08-12T16:04:40.823Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}