← back to PoppyPetitions
lib/validate.ts
49 lines
// 2026-05-30 (P1-B): defensive input-length caps on write endpoints.
// Prompt-injection / abuse hardening — oversized free-text fields get
// rejected with HTTP 400 BEFORE any DB write or Gemini call, so a caller
// can't smuggle a multi-megabyte payload into the LLM prompt or PG.
//
// These are *guards*, not validators — they only reject when a value is
// present AND a string AND longer than the cap. Absent / non-string values
// pass through untouched so existing required-field checks keep their own
// error messages and valid-input behavior is unchanged.
/** Canonical caps for PoppyPetitions write fields. */
export const LIMITS = {
TITLE: 200,
SUMMARY: 2000,
REASON: 2000,
COMMENT: 2000,
BODY: 5000,
ID: 200,
} as const;
/**
* Returns an error string if `value` is a string longer than `max`,
* otherwise null. Non-string / nullish values are not length-checked here.
*/
export function clampLen(
value: unknown,
max: number,
fieldName: string,
): string | null {
if (typeof value === 'string' && value.length > max) {
return `${fieldName} exceeds maximum length of ${max} characters`;
}
return null;
}
/**
* Runs clampLen over a list of [value, max, fieldName] tuples and returns
* the FIRST violation message, or null if all pass.
*/
export function firstLengthViolation(
checks: Array<[unknown, number, string]>,
): string | null {
for (const [value, max, fieldName] of checks) {
const err = clampLen(value, max, fieldName);
if (err) return err;
}
return null;
}