Uh oh!
There was an error while loading. Please reload this page.
fix(runtime): enforce ListRunsRequestSchema's declared limit range (#8054) - #8204
Conversation
…8054) `parseIntegerParam` gains optional (min, max) bounds, threaded through from `ListRunsRequestSchema.shape.limit`'s own `.min()`/`.max()` at the one call site that declares a range (GET /automation/:name/runs), rather than re-listing (1, 100) as literals. `?limit=0`/`-5` no longer silently answer "no runs", and `?limit=101` is no longer served uncapped -- both now refused as 400 VALIDATION_FAILED with the ADR-0114 min_value/max_value field code. Callers that pass no bounds (e.g. notifications.ts) are unaffected. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B3Kurx8qufrDzNjk4rag7V
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckThis PR changes 1 package(s): 20 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
⛔ 2 release-owned page(s) also reference the affected code. These are read-only:
|
hotlong
commented
Aug 12, 2026
PM review — |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#8054
ListRunsRequestSchema.limithas always declared.min(1).max(100), but theruntime boundary that reads it (
parseIntegerParam) only checked that thevalue was a whole number, never that it fell inside the declared range.
Measured, twice, identical both passes:
?limit=0200, zero rows (a confidently wrong "this flow has never run")400 VALIDATION_FAILED, fieldlimit, codemin_value?limit=-5200, zero rows400 VALIDATION_FAILED, fieldlimit, codemin_value?limit=101200, cap not applied400 VALIDATION_FAILED, fieldlimit, codemax_value?limit=1/?limit=100200, forwarded unchanged?limit=25)200, forwarded unchangedThe fix
parseIntegerParam(packages/runtime/src/query-param.ts) gains an optionalthird
bounds: { min?, max? }argument. It is opt-in per call site — everyexisting caller that omits it (
notifications.ts'slimit) is byte-for-byteunaffected, pinned by the untouched
?limit=1000semantics there.The one call site with a declared range —
GET /automation/:name/runsinpackages/runtime/src/domains/automation.ts— now threads
ListRunsRequestSchema.shape.limit's own.min()/.max()through, rather than re-listing
(1, 100)as literals:This is the same discipline #7359 already applies to
status(
ExecutionStatus.options): the wire's declared range and the boundary'senforced range are the same read, so they cannot drift apart the next time
ListRunsRequestSchema's.min()/.max()changes. The schema's boundswere directly readable at the call site — confirmed via a standalone probe
against the real (lazy-proxied)
ListRunsRequestSchema:ListRunsRequestSchema.shape.limit.unwrap().minValue === 1,.maxValue === 100, both official public Zod v4ZodNumberaccessors(
v4/classic/schemas.d.ts), not internals.A value outside the range is refused in the module's house shape —
VALIDATION_FAILED(ADR-0112) with adetails.fields[]entry carrying theADR-0114 field code the property names already mirror:
min_value/max_value. Those two codes were already used bypackages/objectql/src/validation/record-validator.tsfor the identicalrefuse-not-clamp semantics, which is the precedent this PR follows rather
than clamping.
Not in scope
.min(1),.max(100)) — unchanged.parseIntegerParam's behaviour for callers that pass no bounds — unchanged,pinned by the
notifications.tscall site's own untouched test coverage.GET /automation/:name/runs/:runId/screenstill discloses record-derived values to any authenticated caller who knows a run id #7968's/runs/:runId/screengating in the same file — untouched.Tests
packages/runtime/src/domains/automation-runs-query-validation.test.tsgainsa
#8054block pinning the four refusal cases (0,-5,101,1000) withthe full ADR-0112 envelope (
statusandcode) plus the ADR-0114 fieldcode, and asserts
listRunsis never called. The existing preservationit.eachtable is updated: the three rows that used to pin?limit=1000,?limit=-5and?limit=0as forwarded-unchanged are removed (superseded,same as #7359 superseded the
status-ignored case) and a new?limit=25 (ordinary, mid-range)row is added as the over-block guard,alongside the untouched
?limit=1/?limit=100boundary rows.Reverse-verified. Reverted
query-param.ts+automation.tsto theirpre-fix content (
git checkout HEAD~1for those two files, test file left atHEAD).
All four new
#8054cases went red for the expected reason — the request wassilently accepted instead of refused:
All 43 other cases in the file stayed green under the revert — nothing else
moved. Restored the fix (
git checkout HEADfor those two files);git diff --stat HEADcame back empty (byte-identical), and the full 47-case suite isgreen again.
Generated by Claude Code