Refuse a scroll whose deltaY is not a finite number - #452
Open
kevin9327 wants to merge 1 commit into
Open
Conversation
`POST /:botId/scroll` and `POST /:botId/human/scroll` guarded `deltaY` with `typeof value === "number"`, which is true of `Infinity`. `1e999` is valid JSON and parses to exactly that, so the value passed the check, travelled to the gateway, and was written by `JSON.stringify` as `null` on the hop to the Bot's computer -- where `typeof body.deltaY === "number"` is now false and the computer scrolls its own default instead (600 pixels for the tool path, 400 for a person's). The caller got 200 and a scroll it did not ask for. Every other number on this surface is already checked at the edge: `timeoutMs` on `exec` is rejected unless it is a whole number in range (CopilotKit#427), and the coordinates behind `human/click` are rejected unless `Number.isFinite` accepts them. `deltaY` was the one that was not, so it is checked the same way, at the same place, with the same 400. Wrong types are refused rather than silently dropped, which is what `exec` already does with a `timeoutMs` of `"3000"`. Measured: with the routes unchanged and only the new test applied, 10 of 14 cases fail -- every rejection case on both paths answers 200 and reaches the gateway. With the change, 14 pass.
kevin9327
requested review from
MikeRyanDev,
davidmckayv,
guidovizoso and
tylerslaton
as code owners
September 8, 2026 22:11
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What is wrong
Both scroll routes guard
deltaYwith a baretypeofcheck:typeof Infinity === "number", andInfinityis reachable from the wire —1e999is valid JSON:So the value passes the check, reaches the gateway, and is written as
nullbyJSON.stringifyonthe hop to the Bot's computer. There,
typeof body.deltaY === "number"is now false, andagent-computer/src/index.tsfalls back to its own default — 600 pixels on/scroll, 400 on/human/scroll. The caller is answered 200 with a scroll it did not ask for.POST /:botId/human/:kindis worse in one respect: it spreads the whole body through with novalidation at all, so nothing on that path looks at
deltaYbefore the computer does.Why this is the odd one out
Every other number on this surface is already checked at the edge:
timeoutMsonexecis refused unless it is a whole number between 1000 and 600000 (Validate timeoutMs on POST /computers/:botId/exec #427), andthat check explicitly rejects
Infinity,nulland"3000".human/clickare refused unlessNumber.isFiniteaccepts them.deltaYwas the remaining one. This checks it the same way, in the same place, with the same 400.The change
A shared
usableDeltaYpredicate — absent, orNumber.isFinite— used by/:botId/scrolland bythe
scrollcase of/:botId/human/:kind. Wrong types are refused rather than silently dropped,matching what
execalready does with atimeoutMsof"3000".Verification
bun test server/tests/computer-scroll-delta.test.ts, both paths covered bydescribe.each:server/src/computer/routes.tsreverted tomainand only the new test applied:10 fail, 4 pass. Every rejection case on both routes answers
200instead of400and thefake gateway records the call.
The test posts the body as raw text rather than a stringified object, because
JSON.stringify({ deltaY: Infinity })is{"deltaY":null}and cannot express what a client actuallysends.
Also green:
bun test server/tests/computer-routes.test.ts server/tests/computer-exec-timeout.test.ts server/tests/computer-policy-route.test.ts server/tests/computer-fleet-route.test.ts(26 pass),bun run --filter server typecheck,bunx biome check.Note
The
CHANGELOG.mdentry lands at the top of## Unreleased, the same anchor as #450, so whichevermerges second needs a one-line rebase. Happy to do it on request.