Skip to content

feat(rules): avoid SQL injection false positives on parameterized tagged template literals #78

Description

@jeremyjs

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:

  1. A SQL keyword (SELECT)
  2. 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:

// ~/.owasp-wtf/config.json
{
  "rules": {
    "A03-SQL-INJECTION": {
      "safeTemplateTags": ["sql", "db", "query"]
    }
  }
}

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions