Client-side validation matches server-side
Why it matters
Client-side validation is UX; server-side validation is security. When the two diverge, the gap is exploitable: a client that allows $0.01-$9,999.99 but a server with no upper bound means any user who bypasses the form (curl, Burp, modified JS) can submit arbitrary amounts. CWE-602 (Client-Side Enforcement of Server-Side Security) and OWASP A03 both flag this pattern. Parity isn't just a correctness concern — inconsistent constraints produce confusing UX when server validation rejects what the client accepted.
Severity rationale
Low because mismatched constraints require an attacker to bypass client validation, which is trivial but limits casual exploitation to motivated users.
Remediation
Extract shared validation into a single Zod schema in src/lib/schemas.ts that both the React form resolver and the API route import. This mechanically prevents drift.
// src/lib/schemas.ts
export const transferSchema = z.object({
amountCents: z.number().int().min(1, 'Minimum $0.01').max(99999, 'Maximum $999.99'),
routingNumber: z.string().refine(isValidABARoutingNumber, 'Invalid routing number'),
});
// src/components/TransferForm.tsx
const form = useForm({ resolver: zodResolver(transferSchema) });
// src/app/api/transfers/route.ts
const data = transferSchema.parse(await req.json());
DetectionPro
The detection method for this pattern is part of AuditBuffet Pro.
The full method (what to look for, pass and fail conditions, and how results get reported) ships inside the Financial Form Validation Audit. The pattern definition, severity rationale, and remediation remain openly licensed under CC-BY-4.0.
External references
- cwe · CWE-602 — Client-Side Enforcement of Server-Side Security
- owasp:2021 · A03
Taxons
History
- 2026-04-18·v1.0.0·Initial import from finserv-form-validation·automated