Problem
The A03-SQL-INJECTION native rule fires on parameterized query libraries that use tagged template literals, producing a large volume of false positives.
Reproduction
Using @neondatabase/serverless (the standard Neon Postgres driver):
import { neon } from "@neondatabase/serverless";
const sql = neon(process.env.DATABASE_URL!);
// This line is flagged as "SQL Injection" — but it is SAFE
const rows = await sql`SELECT * FROM users WHERE id = ${userId}`;
The ${userId} interpolation is handled by the sql tag function, which converts it to a parameterized query ($1) and passes the value separately to Postgres. The user input is never concatenated into the SQL string.
OWASP.WTF currently flags this because the rule matches:
- A SQL keyword (
SELECT)
- A template literal with
${...} interpolation
Impact
In our production monorepo (~80 packages, 200k LOC), this pattern produces 28 critical "SQL Injection" findings, all of which are false positives. This drowns out the 2-3 real issues we actually want to catch.
Proposed Solutions
Option A: Detect known-safe tag functions (recommended)
Maintain a list of known-safe tagged-template query builders:
sql\...`(from@neondatabase/serverless`)
db\...`(fromdrizzle-orm, kysely`, other ORMs)
query\...`` (generic convention)
When the line matches a SQL keyword + template literal, check if the preceding token on the same line (or within 2-3 lines above) is a tagged template invocation with a known-safe tag. Skip the finding if so.
Option B: Check for explicit concatenation only
Only flag SQL injection when there's actual string concatenation (+ operator, .concat(), or template literals used as function arguments to raw SQL methods). Skip tagged template literals entirely, since most modern JS SQL libraries use them safely.
Option C: Configurable allowlist
Add a config option for consumers to register safe tag names:
Preference
Option A gives the best out-of-box experience for the most common libraries. Option C is a good fallback for less-common libraries.
Reported from Dial-WTF/x402-dial production rollout — PR #120
Problem
The
A03-SQL-INJECTIONnative rule fires on parameterized query libraries that use tagged template literals, producing a large volume of false positives.Reproduction
Using
@neondatabase/serverless(the standard Neon Postgres driver):The
${userId}interpolation is handled by thesqltag function, which converts it to a parameterized query ($1) and passes the value separately to Postgres. The user input is never concatenated into the SQL string.OWASP.WTF currently flags this because the rule matches:
SELECT)${...}interpolationImpact
In our production monorepo (~80 packages, 200k LOC), this pattern produces 28 critical "SQL Injection" findings, all of which are false positives. This drowns out the 2-3 real issues we actually want to catch.
Proposed Solutions
Option A: Detect known-safe tag functions (recommended)
Maintain a list of known-safe tagged-template query builders:
sql\...`(from@neondatabase/serverless`)db\...`(fromdrizzle-orm,kysely`, other ORMs)query\...`` (generic convention)When the line matches a SQL keyword + template literal, check if the preceding token on the same line (or within 2-3 lines above) is a tagged template invocation with a known-safe tag. Skip the finding if so.
Option B: Check for explicit concatenation only
Only flag SQL injection when there's actual string concatenation (
+operator,.concat(), or template literals used as function arguments to raw SQL methods). Skip tagged template literals entirely, since most modern JS SQL libraries use them safely.Option C: Configurable allowlist
Add a config option for consumers to register safe tag names:
Preference
Option A gives the best out-of-box experience for the most common libraries. Option C is a good fallback for less-common libraries.
Reported from Dial-WTF/x402-dial production rollout — PR #120