What it is
When user input is concatenated into SQL, NoSQL filters or shell commands, an attacker can change what the query does: read every table, bypass login or run commands on your server.
Why fast-built apps get it
Raw queries with template strings are the quickest way to make a search or filter work, and they look harmless in a code review.
Before and after the cure
const q = searchParams.get("q");
const rows = await db.$queryRawUnsafe(
`SELECT * FROM products WHERE name LIKE '%${q}%'`
);const q = searchParams.get("q") ?? "";
const rows = await db.product.findMany({
where: { name: { contains: q, mode: "insensitive" } }, // parameterised
take: 50,
});What it can cost you
- Your whole database dumped
- Login bypassed
- Commands run on your server
Check your own app
- Search for template strings inside query, $queryRawUnsafe, exec or spawn
- Check that search and filter inputs are parameterised
- Look for user input passed to file paths
The cure
- Use parameterised queries or your ORM's query builder
- Never pass user input to a shell; use safe library calls instead
- Validate input shapes with a schema at every API boundary
Other critical loopholes
Critical
Secret keys in the frontend
Payment, AI and database keys shipped to every visitor's browser.
See the cureCritical
Database open to everyone
Supabase or Firebase tables any logged-in (or anonymous) user can read and edit.
See the cureCritical
Other users' data by changing an ID
Change /invoices/123 to /invoices/124 and see someone else's invoice.
See the cureDoes your app have this one?
We'll check this and every other loophole on your app, then give you the cure.
Talk to us on WhatsApp