User input that flows directly into a database query, HTML output, or LDAP filter without server-side validation is the root cause of SQL injection, stored XSS, and LDAP injection — three of the most consistently exploited vulnerability classes. NIST 800-53 rev5 SI-10 (Information Input Validation) requires validation of all inputs for type, length, format, and range; SC-18 restricts execution of mobile or untrusted code. FedRAMP rev5 SI-10 applies to all federal cloud systems. CWE-89 (SQL Injection) and OWASP A03 (Injection) have appeared in every OWASP Top 10 since its inception. A single unvalidated search field in a Prisma raw query is sufficient to dump the entire database.
High because unvalidated input directly enables injection attacks that can exfiltrate or destroy all database contents without requiring any additional privileges.
Parse every API request body with a Zod schema before touching the database. Use Prisma or another ORM for all queries — never construct SQL strings from user input.
// app/api/search/route.ts
import { z } from 'zod'
import { prisma } from '@/lib/prisma'
const SearchInput = z.object({
query: z.string().min(1).max(200).trim(),
category: z.enum(['projects', 'audits', 'users'])
})
export const POST = async (req: Request) => {
const parsed = SearchInput.safeParse(await req.json())
if (!parsed.success) {
return Response.json({ error: 'Invalid input' }, { status: 400 })
}
const { query, category } = parsed.data
// Prisma uses parameterized queries — no injection risk
const results = await prisma.project.findMany({
where: { name: { contains: query, mode: 'insensitive' } }
})
return Response.json(results)
}
For any HTML rendering of user content, use DOMPurify.sanitize() before assignment. Never use innerHTML with unescaped user data. Validate on both POST and GET (query-string parameters are equally dangerous).
ID: gov-fisma-fedramp.system-protection.input-validation-injection
Severity: high
What to look for: Enumerate all API route handlers that accept user input. For each, classify whether server-side validation is present. Look for input validation using libraries like zod, joi, yup, or framework validators. Check for parameterized queries (prepared statements) in database interactions. Search for direct string concatenation in SQL queries, DOM manipulation without sanitization, or LDAP query construction. Count the ratio of validated endpoints to total input-accepting endpoints.
Pass criteria: At least 100% of API routes that accept user input have server-side schema validation (zod, joi, etc.). Database queries use parameterized queries or ORM (Prisma, Sequelize, etc.) that prevent SQL injection. No direct string concatenation in SQL queries. DOM manipulation uses safe methods (textContent, createElement) or DOMPurify for HTML content. Report the ratio of validated routes to total input-accepting routes.
Fail criteria: Inputs are only validated client-side, or validation is missing on some API routes. SQL queries use string concatenation or user input directly. HTML content is set via innerHTML without sanitization. Do not pass when only some routes have validation but others accept raw user input.
Skip (N/A) when: Never — input validation is fundamental to security.
Detail on fail: Name specific endpoints or handlers without validation. Example: "POST /api/search has no input validation — user query is directly passed to database search. POST /api/profile/update validates email but not bio field (accepts raw HTML)."
Cross-reference: For CSP-based XSS mitigation that complements input validation, see the security-headers check in the System Protection category.
Remediation: Validate all inputs server-side using schema validators and use parameterized queries:
// lib/validation.ts
import { z } from 'zod'
export const searchSchema = z.object({
query: z.string().min(1).max(100),
category: z.enum(['projects', 'users', 'audits'])
})
// app/api/search/route.ts
import { searchSchema } from '@/lib/validation'
export const POST = async (req: Request) => {
const body = await req.json()
const parsed = searchSchema.safeParse(body)
if (!parsed.success) {
return Response.json({ error: 'Invalid input' }, { status: 400 })
}
const { query, category } = parsed.data
// Use Prisma (parameterized queries automatically)
const results = await prisma.project.findMany({
where: { name: { contains: query } }
})
return Response.json(results)
}
For DOM manipulation, use textContent instead of innerHTML. When rendering user-provided HTML is unavoidable, use DOMPurify to sanitize the content before rendering.