Auth and database
Treat user and AI input as untrusted
Use parameterised queries, avoid dangerouslySetInnerHTML with user or model content, sanitise with DOMPurify, and validate request bodies with zod.
Why it matters
String-built SQL lets an attacker run their own queries. Rendering raw HTML from users lets them run scripts in other users' sessions (XSS). Text produced by a model counts as untrusted input too.
How to fix it
- Use your ORM or parameterised queries. Never build SQL with template strings or concatenation.
- Don't pass user or model content to
dangerouslySetInnerHTMLorinnerHTML. Render it as text, or sanitise it with DOMPurify first. - Validate every request body on the server with a schema, for example zod, and reject what doesn't match.
// Bad
await sql.unsafe(`select * from users where email = '${email}'`);
// Good (parameterised)
await pool.query("select * from users where email = $1", [email]);
// Validate input
import { z } from "zod";
const Body = z.object({ email: z.string().email(), qty: z.number().int().min(1).max(10) });
const body = Body.parse(await req.json());How to verify
- Search the code for
dangerouslySetInnerHTML,innerHTML,.unsafe(and SQL built with backticks. - Submit
<img src=x onerror=alert(1)>in a text field; it should display as text.
Questions
- Does React protect me from XSS?
- React escapes text by default. dangerouslySetInnerHTML, href values like javascript: URLs, and direct DOM writes bypass that.
Related checks
- Check permissions on the server
- Make LLM features resist prompt injection
- Decide prices and access on the server
Tick it off in the full checklist