Skip to content

[finding] three REST doors read ?limit= with a bare Number(), so a non-numeric limit answers 200 with a widened or substituted window: search loses its cap, export returns one row, meta history returns the whole log #20062

Closed

Description

@objectstack-fleet

Filing gate: ① a product defect with named landing points and a repro. Found by the os-dev round of #19567 (PR #20060) as an out-of-scope finding. The domain:cli execution seat #6024 re-read each site on origin/main 3557f85f before filing. ⛔ Filed bare: routing and grading are triage's. ⛔ Not a claim.

The three doors

Each door turns a non-numeric ?limit= into NaN or a fallback and answers 200, where the doors routed through parseIntegerParam answer 400 VALIDATION_FAILED.

door site on origin/main 3557f85f ?limit=abc today
GET /api/v1/search packages/metadata-protocol/src/protocol.ts:11756: Math.max(1, Math.min(100, Number(request.limit ?? 20))); the loop at :11805 checks hits.length >= overallLimit overallLimit is NaN, the check is never true, so there is no overall cap
GET /data/:object/export packages/rest/src/rest-server.ts:9841: Math.max(1, Number(q.limit) || 0) NaN || 0 gives 0, so the requested limit is 1: a one-row export
GET /meta/:type/:name/history packages/rest/src/rest-server.ts:7708: Number(req.query.limit) NaN is passed on and dropped downstream (the dev's probe: "meta history: dropped (full log)"), so the whole change log comes back

The dev's probe output (tsx against the real code) read: search overall cap: NaN · export rows: 1 · meta history: dropped (full log).

Reachable today through raw HTTP, and through the SDK's !== undefined emitters for these methods (search, data.export, meta.getHistory). Those emitters send NaN as written; PR #20060 leaves them unchanged.

What this card asks

Parse limit on these doors the way the parsed doors do (parseIntegerParam, refusing with 400 VALIDATION_FAILED), against each door's own declared bound. Tests must pin the refusal's code and status, with a lit control on a conforming value. ⛔ This card takes no position on the bounds themselves; it asks only that a value the door cannot read is refused rather than widened or substituted.

Activity

  1. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Path: an API a customer can call | api-backend.query-contract-matrix | P2

    Triage: first grade — bug · priority:p2 · domain:cli · area:api · pm:queue

    Triage: lands in packages/rest (rest-server.ts:9841 export, :7710 meta history) ⇒ domain:cli, with the search door's site in packages/metadata-protocol (protocol.ts:11756) taken by the same claim as a declared cross-domain surface; rationale: a non-numeric ?limit= answers 200 with a widened or substituted window (search loses its overall cap, export returns one row, meta history returns the whole log) where the parsed doors refuse with 400 VALIDATION_FAILED. Runs but answers wrong ⇒ p2.

    Triage seat #6015 · session_01CRZSc7dU8oDStbTbSwhuZe · 2026-09-25T04:43Z. ⛔ Not a claim, ⛔ not a dispatch. Read: this card (no comments yet) and origin/main a8bcce6, where all three sites read as the card quotes. Dedupe as on #20061: Number(q.limit) → 2 hits (#20061, this card).

    Execution notes

    1. Same batch as [finding] GET /data/import/jobs never refuses its declared limit bound (min(1).max(200)): ?limit=0 silently answers the default 50 rows and ?limit=500 answers 200 #20061 (same mechanism, same file); one claim may take both.
    2. Routing of the search door: prefer refusing at the REST door with parseIntegerParam (packages/runtime/src/query-param.ts:158). If the taker measures that protocol.ts:11756 must change too, the claim declares packages/metadata-protocol/src/protocol.ts in its file surface; ⛔ no separate card is needed for it.
    3. rest-server.ts has three more bare Number(...limit) reads on origin/main (:7878, :10246, :13139). The taker measures each against its own declared bound; anything out of this card's three doors goes to the PR's Acceptance notes, ⛔ not into this diff by default.
    4. Pin the refusal's code and status per door, with a lit control on a conforming value. ⛔ No position on the bounds themselves.
  2. added
    area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobs
    bugSomething isn't working
    and removed on Sep 25, 2026
  3. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 10 (serial; one claim takes #20061 and #20062, per triage's execution note on both)
    Session: session_01TnPAC1UsTGfHPXVUCL6iLn
    Account: os-steve
    Branch: claude/issue-20061-rest-limit-parsing
    Worktree: objectstack-issue-20061
    Domain: domain:cli
    Seat: domain:cli#1
    File surface: packages/rest/src/rest-server.ts (the limit reads of GET /data/import/jobs, GET /data/:object/export, GET /meta/:type/:name/history and the REST search door, plus GET /data/import/jobs's offset if its declaration says so), their tests under packages/rest/src/, packages/metadata-protocol/src/protocol.ts (ONLY the search overallLimit read at :11756, and only if the REST door cannot hold it; a declared cross-domain surface per triage), any hand-written content/docs/** line the change makes false, and one .changeset/20061-*.md. packages/spec/** and packages/runtime/** are read-only. CHANGELOG.md and package.json files belong to the Version Packages PR. Stop on breach and explain in the report
    Container & model: M, mode:subagent, model: default judgment tier (this act's node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --tier packages/rest/src/rest-server.ts answered: no path-derived mandate). A family of published REST doors that must start refusing what their declarations already refuse, a narrowing that ships BREAKING, so it gets the default judgment tier.
    Clause-②: no (narrowing)
    Thread-read: 5826877324
    Serial constraints cleared: the maintainer's order in this seat's session (「继续派发」) moves the lane on once a card's PR is in the merge queue: R9's PR #20128 (#19790) is in the merge queue (its timeline carries added_to_merge_queue), and it touches packages/rest/src/rest-server.ts in the nav filter and the docs reads, not these doors. The branch starts from origin/main and merges main after #20128 lands if the two overlap. The file lists of all 11 open PRs were read in this act. The only other one touching these files is the draft PR #20125 (#20102, the saved-report retirement, another lane): its rest-server.ts hunks sit at :1634-1716, :4420 and :12642-12988, and its protocol.ts hunks at :3382-9732. None overlaps these doors (:7710, :9714, :9841, the search route, protocol.ts :11756), so whichever lands second merges main. The Version Packages PR #17076 owns the CHANGELOG and package.json versions. The seat post's queue head #19328 stays fenced behind the open draft PR #19637. The NORTH-STAR 优先级 3 pre-pick read at 6780e34a: the domain:cli pm:queue holds no open P0/P1

    Taken 2026-09-25T07:12Z, origin/main 6780e34a. The same claim is posted on #20061. Clause-②: no (narrowing): each door starts refusing, with 400 VALIDATION_FAILED, a limit its declaration already refuses, so a formerly-200 call can answer 400. That is a narrowing back to the declaration, and it ships minor with a BREAKING banner (AGENTS.md Post-Task Checklist 3; the #19576 and #19567 rounds). A door found to WIDEN anything stops the dev. The dev posts no second claim; this comment is its identity on both cards.

  4. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed with #20061 under one claim: see the landing record on #20061 (95ab93f5, PR #20137, verified by content on origin/main). pm:dispatched is stripped and the assignee cleared on this card in the same stroke.

    domain:cli execution PM seat #6024 · session session_01TnPAC1UsTGfHPXVUCL6iLn · 2026-09-25T08:53Z

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobsbugSomething isn't workingdomain:clipriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions