# Tax/fee calculations pre-filled with server verification

- **Pattern:** `ab-001449` (`finserv-form-validation.calculation-accuracy.tax-fee-verification`)
- **Severity:** low
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001449
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001449)

## Why it matters

Any fee or tax value that the server accepts directly from the client request body is tamper-able: a user reduces `fee` to 0 before submitting and pays less. This is OWASP A03 (Injection) and CWE-602 (Client-Side Enforcement of Server-Side Security) in one pattern. PCI DSS Req-6.2.4 requires server-side validation of all payment parameters. The client can display a fee preview for UX purposes — but the server must independently compute and verify the fee amount before it reaches the ledger.

## Severity rationale

Low because exploiting this requires a user to intentionally modify a network request, limiting exposure to motivated actors with basic HTTP tooling.

## Remediation

Never read `fee` or `tax` from the request body. Compute them server-side from the canonical inputs and compare against any client-submitted value if you want to surface mismatches.

```ts
// src/app/api/checkout/route.ts
export async function POST(req: Request) {
  const { amountCents } = await req.json(); // only accept the base amount

  // Calculate server-side
  const feeCents = Math.round(amountCents * 0.02); // 2% platform fee
  const taxCents = Math.round(amountCents * 0.08); // 8% tax
  const totalCents = amountCents + feeCents + taxCents;

  // Commit with server-calculated values only
  await db.order.create({ data: { amountCents, feeCents, taxCents, totalCents } });
}
```

## Detection

- **ID:** `tax-fee-verification`
- **Severity:** `low`
- **What to look for:** Count all calculated fee/tax/interest display points in the UI. For each, check whether the calculation is performed client-side only, server-side only, or both (client preview + server verification). Quote the calculation expression and where it runs. Report: "X of Y calculated amounts are verified server-side before commit."
- **Pass criteria:** At least 100% of fee, tax, and interest calculations are computed or verified server-side before the transaction commits. Client-side calculations are display-only previews. No more than 0 client-calculated values are trusted by the server without recalculation.
- **Fail criteria:** Any fee/tax amount is calculated client-side and accepted by the server without server-side recalculation.
- **Do not pass when:** The server accepts a `fee` or `tax` field from the client request body and uses it directly — even if the client calculation is correct, it is tamper-able.
- **Skip (N/A) when:** The project has no calculated fees, taxes, or interest (0 calculated amount display points found).
- **Detail on fail:** Example: `"Fee is calculated in JavaScript and sent to server, which uses it directly without recalculating. User could modify the fee in network request."` or `"Interest is pre-filled client-side; no server-side verification before committing"`
- **Cross-reference:** The total-recalculation check covers the broader total verification pattern; this check focuses on individual fee/tax components.
- **Remediation:** Always verify calculated amounts server-side in `src/app/api/` route handlers:

  **In `src/app/api/transfers/route.ts`:**
  ```ts
  // Client-side: Display only (for preview)
  const estimatedFee = amountCents * 0.02; // 2% fee, display only

  // Server-side: Calculate and verify
  export async function POST(req) {
    const { amountCents, submittedFee } = req.body;

    const calculatedFee = amountCents * 0.02;
    if (submittedFee !== calculatedFee) {
      throw new Error('Fee mismatch. Recalculating...');
    }

    const total = amountCents + calculatedFee;
    // ... commit to DB
  }
  ```

## External references

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

Taxons: injection-and-input-trust

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