@@ -206,23 +206,14 @@ export async function validateDatabaseHost(
206206 }
207207}
208208
209- /**
210- * Patterns that indicate SQL injection or an always-true ("tautology") condition
211- * in a free-form WHERE clause supplied to a database mutation (UPDATE/DELETE) or
212- * a filtered COUNT, where the value may originate from an LLM or untrusted
213- * upstream workflow data.
214- *
215- * IMPORTANT: this is **defense-in-depth, not a security boundary**. A free-form
216- * SQL condition cannot be exhaustively validated against every always-true
217- * expression (e.g. `OR 2 > 1`, `OR (1)`, `OR NOT 0`, `OR length(x) >= 0`). The
218- * real boundary is that the caller supplies their own database credentials and
219- * could run equivalent SQL directly (e.g. via a raw-SQL/execute operation). This
220- * guard exists to stop the easy, obvious ways an injected condition broadens a
221- * mutation to every row.
222- */
223209/**
224210 * Patterns run against the WHERE clause with string/identifier literals masked
225211 * out (so an attacker cannot smuggle `OR 1` or `; DROP` inside a quoted value).
212+ *
213+ * The connector-literal rules below are intentionally `OR`-only: only an
214+ * `OR <truthy>` term broadens a mutation to every row. `AND <number>` is a no-op
215+ * for broadening and is also exactly what `BETWEEN low AND high` produces, so
216+ * matching it would reject legitimate range filters (e.g. `id BETWEEN 1 AND 10`).
226217 */
227218const SQL_WHERE_MASKED_PATTERNS : readonly RegExp [ ] = [
228219 / ; \s * \w / , // stacked statement
@@ -234,8 +225,8 @@ const SQL_WHERE_MASKED_PATTERNS: readonly RegExp[] = [
234225 / \b (?: s l e e p | p g _ s l e e p | b e n c h m a r k ) \s * \( / i,
235226 / \b ( \w + ) \s * = \s * \1\b / i, // same (unquoted) operand both sides: x=x, 1=1
236227 / \b \d + (?: \. \d + ) ? \s * (?: = | = = | < > | ! = | < = | > = | < | > ) \s * \d + (?: \. \d + ) ? \b / , // constant vs constant: 1=1, 1<2, 2>1
237- / \b (?: o r | a n d ) \s + (?: t r u e | f a l s e ) \b / i, // OR TRUE / AND FALSE
238- / \b (?: o r | a n d ) \s + \d + (?: \. \d + ) ? \b (? ! \s * [ = < > ! + \- * / % ] ) / i, // standalone truthy literal: OR 1, AND 42
228+ / \b o r \s + (?: t r u e | f a l s e ) \b / i, // OR TRUE / OR FALSE
229+ / \b o r \s + \d + (?: \. \d + ) ? \b (? ! \s * [ = < > ! + \- * / % ] ) / i, // standalone truthy literal after OR : OR 1, OR 42
239230 / ^ \s * (?: \d + (?: \. \d + ) ? | t r u e | f a l s e ) \s * $ / i, // bare constant: "1" / "true" / "false"
240231]
241232
0 commit comments