Base price is correctly set and numeric
Why it matters
Price stored as a string type (VARCHAR, TypeScript string) bypasses all numeric comparison operators: sorting by price returns lexicographic order ('9' > '100'), and arithmetic in checkout produces NaN or '$50' + '$10' = '$50$10'. CWE-20 (Improper Input Validation) applies when the schema accepts price values that the business logic cannot safely process. A zero or negative price — even for a free product — creates a Stripe charge for $0 or triggers a refund flow, and no validation to prevent it means a data entry error or API misuse propagates directly to customer receipts and revenue reports.
Severity rationale
Critical because a string-typed or unvalidated price field corrupts sort order, arithmetic, and payment charges — defects that appear in customer invoices and financial reporting.
Remediation
Use Decimal in prisma/schema.prisma (not Float, which has floating-point precision loss, and not String) and enforce price > 0 validation in src/app/api/products/route.ts before every create or update.
model Product {
id String @id @default(cuid())
price Decimal @db.Decimal(10, 2)
}
if (!price || price <= 0) {
return Response.json({ error: 'Price must be greater than 0' }, { status: 400 })
}
For free products, use a separate isFree boolean flag rather than price = 0 — this makes the intent explicit and prevents accidental zero charges.
Detection
-
ID:
base-price-valid -
Severity:
critical -
What to look for: Count all price-related fields in the product schema. Enumerate the data type used (Decimal, Float, Int, String). Check all product creation and update paths in
src/app/api/products/for price validation logic. Count the number of products in seed data or fixtures that have price <= 0, price = null, or non-numeric price values. -
Before evaluating: Extract and quote the price field definition from the product schema (e.g.,
price Decimal @db.Decimal(10, 2)fromprisma/schema.prisma). Also quote any price validation logic found in API routes. -
Pass criteria: The price field is a numeric type (Decimal or Float, not String) in the schema, all products in seed data have price > 0, and at least 1 validation path prevents zero or negative prices. Report: "Price type: [type], X of Y products have valid prices > 0."
-
Fail criteria: Price is stored as a string type, or products exist with zero or negative prices in seed data, or no validation prevents invalid prices in any product creation/update path.
-
Do NOT pass when: Price is stored as a Decimal/Float but no validation exists anywhere in the codebase to prevent price = 0 or price < 0 — the type alone is insufficient without validation.
-
Skip (N/A) when: Never — all products must have a valid price.
-
Cross-reference: For currency handling and internationalization, the Geo-Readiness audit covers multi-currency support and locale-aware formatting.
-
Cross-reference: For pricing display accuracy, the Pre-Launch audit covers content correctness and data integrity checks.
-
Cross-reference: For payment amount validation, the Subscription Compliance audit covers price accuracy in transaction flows.
-
Detail on fail:
"Price field is stored as String type in prisma/schema.prisma"or"2 of 15 products have price = 0 in prisma/seed.ts and no validation in src/app/api/products/route.ts" -
Remediation: Use a numeric type for prices in
prisma/schema.prismaand enforce validation insrc/app/api/products/route.ts:model Product { id String @id @default(cuid()) price Decimal @db.Decimal(10, 2) }if (price <= 0) { throw new Error('Price must be greater than 0') }
External references
- cwe · CWE-20 — Improper Input Validation
- iso-25010:2011 · functional-correctness — Functional Correctness (Functional Suitability)
Taxons
History
- 2026-04-18·v1.0.0·Initial import from ecommerce-catalog·automated