fix: add finite number validation - #1246
Conversation
Coverage Report for CI Build 29829824081Coverage increased (+0.1%) to 79.789%Details
Uncovered Changes
Coverage Regressions18 previously-covered lines in 2 files lost coverage.
Coverage Stats💛 - Coveralls |
170141b to
657c658
Compare
strip it from OpenAPI Signed-off-by: ferhat elmas <elmas.ferhat@gmail.com>
657c658 to
8d2c2e8
Compare
|
@claude review always |
There was a problem hiding this comment.
I didn't find any additional bugs beyond what the automated review covered, but this PR introduces a new custom AJV keyword (finite) wired into the core validator construction for both the public and admin Fastify apps and the standalone S3 router's AJV instance, and applies it across roughly 30 route schemas (S3, iceberg, vector, buckets, admin tenants, pprof). Given the security-relevant nature of the fix (closing a validation-bypass allowing Infinity/1e999 to skip limit checks) and the breadth of touched routes, this is worth a human look rather than a bot approval.
Extended reasoning...
Overview
This PR adds a shared src/http/finite.ts module exposing an AJV keyword (finite) that rejects non-finite numeric values after AJV's type coercion, plus a stripFiniteKeyword/finiteSwaggerTransform pair to remove the internal keyword from generated OpenAPI docs. It wires this keyword into three separate AJV instances: the public app (app.ts), the admin app (admin-app.ts), and the standalone S3 command router's dedicated AJV compiler (s3/router.ts). It then annotates ~30 numeric schema fields across bucket, object, iceberg, S3, and admin/tenant routes with finite: true, and separately hardens the vector route's no-coercion AJV compiler with strictNumbers: true.
Security risks
This is itself a security hardening fix: AJV's coerceTypes will coerce string inputs like "Infinity"" or "1e999"into numericInfinity, which then bypasses minimum/maximumbound checks (e.g. file size limits, pagination limits, multipart part numbers, tenant resource limits). The fix is narrowly scoped to validation and doesn't introduce new attack surface itself, but because it changes validation behavior on security-relevant limits across many endpoints, a regression (e.g. a route that should reject non-finite values but wasn't annotated, or unexpected interaction withnullable`/coercion) could silently reopen the original bypass on just that route.
Level of scrutiny
Given the change touches shared validation plumbing (three separate AJV instantiation points) and is applied broadly rather than to a single isolated route, this warrants more than a rubber-stamp pass — a human should confirm the keyword is applied consistently to all the limit-bearing fields it's meant to cover (e.g. I'd want a second pair of eyes checking for any numeric limit field that may have been missed) and that the OpenAPI-stripping logic can't leak the internal keyword in edge cases (e.g. schemas with ``/allOf composition it wasn't tested against).
Other factors
The PR ships with thorough test coverage (new finite.test.ts, finite-routes.test.ts, and per-route assertions in existing route test suites), and Coveralls reports coverage increased with only one uncovered line. The CodeQL "reflected XSS" alerts are on finite.test.ts inside a test helper that builds a Fastify inject() URL from a loop variable for local test requests — not a real HTTP response reflecting user input — so those read as false positives and don't change my assessment.
What kind of change does this PR introduce?
Bug fix
What is the current behavior?
AJV coercion (i.e.
Infinity,1e999) skips limit checks.What is the new behavior?
Add finite check and reject them.
It's mostly relevant for S3 routes but hardened it in all routes.
Strip finite keyword from from OpenAPI since it's a runtime specific and not compatible.
Additional context
Related to #1237