An APY input that accepts 500% or a discount input that accepts -20% doesn't just produce wrong results — it can crash downstream calculations, trigger integer overflow in cents conversion, or be exploited to generate artificially favorable rates. OWASP A03 covers unvalidated numeric inputs. Even without malicious intent, an accidental entry of 5 instead of 0.05 for a decimal rate doubles or quintuples a fee, producing silent data corruption that's hard to detect in logs.
Low because unrealistic percentage values typically fail at a downstream calculation step, limiting direct financial impact but creating data corruption risk.
Add explicit range constraints to all percentage fields in both the form schema and the API validation. Determine whether the field uses 0-100 percent notation or 0-1 decimal notation and constrain accordingly.
// src/lib/schemas.ts
// For user-facing APY / interest rate inputs (0-100%)
export const aprSchema = z.number()
.min(0, 'Rate cannot be negative')
.max(100, 'Rate cannot exceed 100%');
// For internal decimal representation (0-1)
export const decimalRateSchema = z.number().min(0).max(1);
Apply these schemas in the relevant API routes under src/app/api/ and add corresponding min / max attributes to the form inputs in src/components/.
ID: finserv-form-validation.calculation-accuracy.percentage-range-validation
Severity: low
What to look for: Count all percentage input fields (APY, interest rate, discount, fee rate, tax rate). For each, check whether client-side and server-side validation constrain the range to at least 0% and no more than 100% (or 0-1 for decimal representation). Quote the constraint found (e.g., min: 0, max: 100 or z.number().max(1)). Report: "X of Y percentage inputs have range validation."
Pass criteria: At least 100% of percentage inputs are constrained to a realistic range (0-100% or 0-1 decimal). Validation rejects values like 150% or negative percentages. Both client and server enforce the range.
Fail criteria: Any percentage input accepts values over 100% or under 0%, or no validation is present.
Skip (N/A) when: The project has no percentage inputs (0 percentage fields found after searching for rate, percent, APY, discount inputs).
Detail on fail: Example: "APY input has no max attribute. Server accepts 500% APY without validation." or "Discount percentage allows negative values"
Cross-reference: The client-server-parity check verifies that percentage constraints match between layers; this check focuses on the range itself being realistic.
Remediation: Validate percentage ranges in src/lib/schemas.ts:
In src/lib/schemas.ts:
const percentSchema = z.number().min(0).max(100).describe('0-100%');
// Or for decimal (0-1):
const decimalPercentSchema = z.number().min(0).max(1).describe('0-1 (0-100%)');