ShipSafeFull checklist (23 checks)

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

  1. Use your ORM or parameterised queries. Never build SQL with template strings or concatenation.
  2. Don't pass user or model content to dangerouslySetInnerHTML or innerHTML. Render it as text, or sanitise it with DOMPurify first.
  3. 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

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

Tick it off in the full checklist

Get new checks by email

New checks and real leak post-mortems, about once a month. No spam.