{"record":{"id":"edd5fced9b41d325","repo":"can1357/oh-my-pi","slug":"violation","errorCode":null,"errorMessage":"${violation}","messagePattern":"\\$\\{violation\\}","errorType":"validation","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/sqlite-reader.ts","lineNumber":496,"sourceCode":"\t\t\tinDoubleQuote = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (char === \";\") return COMMENT_OR_TERMINATOR_ERROR;\n\t\tif ((char === \"-\" && next === \"-\") || (char === \"/\" && next === \"*\") || (char === \"*\" && next === \"/\")) {\n\t\t\treturn COMMENT_OR_TERMINATOR_ERROR;\n\t\t}\n\t}\n\n\treturn keywordViolation;\n}\n\nfunction validateWhereClause(where: string | undefined): string | undefined {\n\tif (!where) return undefined;\n\tconst trimmed = where.trim();\n\tif (!trimmed) return undefined;\n\tconst violation = findWhereClauseViolation(trimmed);\n\tif (violation) {\n\t\tthrow new ToolError(violation);\n\t}\n\treturn trimmed;\n}\n\nfunction normalizeWriteValue(value: unknown, column: string): SqliteBinding {\n\tif (value === null) return null;\n\tif (\n\t\ttypeof value === \"string\" ||\n\t\ttypeof value === \"number\" ||\n\t\ttypeof value === \"boolean\" ||\n\t\ttypeof value === \"bigint\"\n\t) {\n\t\treturn value;\n\t}\n\tthrow new ToolError(`SQLite column '${column}' only accepts JSON scalar values or null`);\n}\n\nfunction validateWriteColumns(","sourceCodeStart":478,"sourceCodeEnd":514,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/sqlite-reader.ts#L478-L514","documentation":"Thrown by validateWhereClause when the 'where' query parameter contains a disallowed construct, as detected by findWhereClauseViolation. The tool re-stricts WHERE clauses (forbidding keywords like LIMIT, and likely comments/multiple statements) so that user-supplied filters cannot alter pagination, escape the query, or execute arbitrary SQL.","triggerScenarios":"Passing where=age>18 LIMIT 5; where clauses with forbidden keywords (LIMIT/OFFSET etc.), semicolons, comments, or other dangerous patterns; stacking pagination manually inside where instead of using the tool's limit/offset params.","commonSituations":"Developers copying full SQL fragments including LIMIT into the where param; trying to sneak in ORDER BY inside where; prompt-generated SQL that includes trailing clauses.","solutions":["Remove forbidden keywords (LIMIT, OFFSET, ORDER BY, etc.) from the where clause — use the tool's own limit/offset/order params instead","Keep the where param a simple filter expression (column comparisons with AND/OR)","Read the violation message — it names the exact offending construct","If the filter can't be expressed without the forbidden clause, run a raw q=SELECT query instead"],"exampleFix":"// before\n?where=active=1 LIMIT 10\n// after\n?where=active=1&limit=10","handlingStrategy":"validation","validationCode":"const FORBIDDEN = /\\b(limit|offset|order\\s+by|union|;|--|\\/\\*)/i;\nif (where && FORBIDDEN.test(where)) {\n  throw new Error(`where clause contains forbidden construct: ${where}`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await reader.read(`db.sqlite/users?where=${encodeURIComponent(where)}`);\n} catch (err) {\n  if (err instanceof ToolError && /forbidden|not allowed/i.test(err.message)) {\n    // sanitize the where clause or move pagination to dedicated params\n  } else throw err;\n}","preventionTips":["Keep where clauses to simple boolean column expressions","Use the tool's limit/offset/order params, never SQL clauses in where","Validate user-supplied filters against a keyword blocklist before passing them"],"tags":["sqlite","validation","sql-injection-guard","where-clause"],"backgroundTag":"unsafe-sql-filter-rejected","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}