# Max amount limits enforced at submission

- **Pattern:** `ab-001445` (`finserv-form-validation.account-routing.max-amount-limits`)
- **Severity:** medium
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001445
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001445)

## Why it matters

Without a server-side maximum, any user can craft a request with an arbitrarily large `amount` value and bypass whatever HTML `max` attribute the form presents. This opens the door to accidental or malicious over-drafts, integer overflow in downstream calculations, and Stripe or payment processor rejections that can cascade into failed order states. OWASP A03 and PCI DSS Req-6.2.4 require server-side input validation for all payment parameters — client constraints are cosmetic until the server enforces them independently.

## Severity rationale

Medium because unbounded server-side amounts enable over-limit transactions that the payment processor or banking rail will reject, corrupting order state.

## Remediation

Define a named constant for the limit and enforce it in the API route handler before any payment processor call.

```ts
// src/app/api/transfers/route.ts
const MAX_AMOUNT_CENTS = 99999; // $999.99 — adjust per business rules

const schema = z.object({
  amountCents: z.number().int().min(1).max(MAX_AMOUNT_CENTS),
});
const { amountCents } = schema.parse(await req.json());
```

Also set `max={MAX_AMOUNT_CENTS / 100}` on the HTML `<input type="number">` in `src/components/TransferForm.tsx` to keep client and server limits aligned.

## Detection

- **ID:** `max-amount-limits`
- **Severity:** `medium`
- **What to look for:** Count all financial amount submission endpoints (API routes that accept monetary values). For each, check whether a maximum amount limit is enforced server-side. Quote the limit value found (e.g., `max: 999999` or `MAX_AMOUNT = 10000`). Also check client-side inputs for `max` attributes. Report: "X of Y amount endpoints enforce a server-side maximum."
- **Pass criteria:** At least 100% of amount submission endpoints enforce a maximum limit server-side. The limit value is at least 1 and no more than a reasonable ceiling. Client-side also shows the constraint (HTML `max` or visible UI text). Report even on pass: "X endpoints enforce max limit of $Y."
- **Fail criteria:** Any amount endpoint accepts unbounded values server-side, or the limit is only client-side.
- **Skip (N/A) when:** The project has no concept of amount limits (0 financial submission endpoints found, or explicitly unlimited by design).
- **Detail on fail:** Example: `"Amount input has no max attribute. Server-side API accepts any amount value without limit check."` or `"HTML max='10000' set, but server-side API does not validate against this limit"`
- **Cross-reference:** The min-amount-enforcement check in this category covers the lower bound; together they define the valid amount range.
- **Remediation:** Enforce maximum amounts server-side in `src/app/api/` route handlers:

  **In `src/app/api/transfers/route.ts`:**
  ```ts
  const MAX_TRANSACTION_AMOUNT = 999999; // cents, i.e., $9,999.99

  if (amountCents > MAX_TRANSACTION_AMOUNT) {
    throw new Error('Amount exceeds maximum limit');
  }
  ```

## External references

- cwe CWE-20
- owasp A03
- pci-dss Req-6.2.4

Taxons: injection-and-input-trust

HTML version: https://auditbuffet.com/patterns/ab-001445
