Summary
/calculate has no request-schema validation, no payload-size limits, and no rate limit. household["axes"] in particular is unbounded, so a caller can submit arbitrarily large axes arrays that force the country model to allocate enormous numpy grids and block a worker for minutes.
Location
policyengine_household_api/endpoints/household.py:20-22
payload = request.json
household_json = payload.get("household", {})
policy_json = payload.get("policy", {})
What goes wrong
- No Pydantic (or equivalent) schema validates
household, policy, or enable_ai_explainer.
axes in the household is passed directly into country.calculate; the country model then builds a cartesian grid across the specified axes. With no upper bound on axis count or per-axis count, a single request can trigger huge numpy allocations and long simulation runs.
- No
@limiter.limit(...) decorator is attached to /calculate (only /calculate_demo has one).
Suggested fix
- Add a Pydantic model for the request payload (
CalculateRequest) and validate request.json against it before calling country.calculate. Reject payloads exceeding app.config["MAX_CONTENT_LENGTH"] or an explicit size budget.
- Constrain
axes: max_length on the axes list, max on per-axis count, and possibly a cap on the product of axis counts.
- Apply a reasonable
@limiter.limit("N per minute") decorator on /calculate (auth-tenant-aware key if possible).
Severity
Low/medium - DoS vector via unauthenticated-friendly payload shapes; belongs in a general-hardening pass.
Relates to
#1460 / #1461 (fail-closed auth)
Summary
/calculatehas no request-schema validation, no payload-size limits, and no rate limit.household["axes"]in particular is unbounded, so a caller can submit arbitrarily largeaxesarrays that force the country model to allocate enormous numpy grids and block a worker for minutes.Location
policyengine_household_api/endpoints/household.py:20-22What goes wrong
household,policy, orenable_ai_explainer.axesin the household is passed directly intocountry.calculate; the country model then builds a cartesian grid across the specified axes. With no upper bound on axis count or per-axiscount, a single request can trigger huge numpy allocations and long simulation runs.@limiter.limit(...)decorator is attached to/calculate(only/calculate_demohas one).Suggested fix
CalculateRequest) and validaterequest.jsonagainst it before callingcountry.calculate. Reject payloads exceedingapp.config["MAX_CONTENT_LENGTH"]or an explicit size budget.axes:max_lengthon the axes list,maxon per-axiscount, and possibly a cap on the product of axis counts.@limiter.limit("N per minute")decorator on/calculate(auth-tenant-aware key if possible).Severity
Low/medium - DoS vector via unauthenticated-friendly payload shapes; belongs in a general-hardening pass.
Relates to
#1460 / #1461 (fail-closed auth)