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.
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.
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.
ID: ecommerce-catalog.product-data.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) from prisma/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.prisma and enforce validation in src/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')
}