# Sale prices are validated to be less than base prices

- **Pattern:** `ab-001123` (`ecommerce-catalog.variant-pricing.sale-price-valid`)
- **Severity:** medium
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001123
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001123)

## Why it matters

A sale price equal to or greater than the base price (`salePrice >= basePrice`) is a deceptive pricing display: the customer sees a crossed-out "original" price that is lower than or equal to the "sale" price. The FTC Act prohibits deceptive pricing, and several state consumer protection statutes specifically require that a reference price must reflect a genuine prior offer. CWE-20 (Improper Input Validation) covers this at the technical layer — the API accepts a logically invalid price relationship with no rejection. Even without legal exposure, a visible sale price of $60 crossed out next to a regular price of $50 destroys customer trust immediately.

## Severity rationale

Medium because the business impact — deceptive pricing display and potential FTC consumer protection exposure — requires authenticated admin access to trigger, limiting blast radius but not eliminating it.

## Remediation

Add a validation guard in `src/app/api/products/route.ts` that rejects product updates where `salePrice >= basePrice`.

```ts
if (salePrice !== undefined && salePrice >= basePrice) {
  return Response.json(
    { error: 'Sale price must be strictly less than base price' },
    { status: 400 }
  )
}
```

Also add a database-level check constraint if your Postgres version supports it:

```sql
ALTER TABLE products
  ADD CONSTRAINT sale_price_less_than_base
  CHECK (sale_price IS NULL OR sale_price < price);
```

## Detection

- **ID:** `sale-price-valid`
- **Severity:** `medium`
- **What to look for:** Count all sale price or discount price fields in the product and variant schemas (`salePrice`, `discountedPrice`, `compareAtPrice`). Enumerate all product update paths in `src/app/api/products/` that accept sale price values. For each path, check whether validation exists to ensure `salePrice < basePrice`.
- **Pass criteria:** If sale prices exist in the schema, at least 1 validation path ensures `salePrice < basePrice` and rejects or errors when the constraint is violated. Count all product update paths and report: "X of Y update paths validate sale price < base price."
- **Fail criteria:** Products can be created or updated with `salePrice >= basePrice`, or no validation prevents this logical error in any code path.
- **Skip (N/A) when:** The project has no sale price functionality — confirmed by finding no `salePrice`, `discountedPrice`, or `compareAtPrice` fields in the schema.
- **Cross-reference:** For pricing display on promotional pages, the Marketing Content Quality audit covers promotional accuracy.
- **Cross-reference:** For sale price compliance in app stores, the App Store IAP & Subscriptions audit covers pricing rule enforcement.
- **Cross-reference:** For consumer protection around sale pricing, the FTC Consumer Protection audit covers deceptive pricing practices.
- **Detail on fail:** `"No validation in src/app/api/products/route.ts — 3 products have salePrice greater than or equal to basePrice (e.g., base=$50, sale=$60 in prisma/seed.ts)"`
- **Remediation:** Add validation in `src/app/api/products/route.ts` when updating product prices:

  ```ts
  if (salePrice && salePrice >= basePrice) {
    throw new Error('Sale price must be less than base price')
  }
  ```

## External references

- cwe CWE-20
- iso-25010 functional-correctness

Taxons: data-integrity

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