fix: fail closed on an unknown or wrong-typed command in the worker codec - #1150
fix: fail closed on an unknown or wrong-typed command in the worker codec#1150FSM1 wants to merge 3 commits into
Conversation
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…odec buildCommand had no default arm, so an unknown kind fell out as undefined and only failed by accident of the wasm-bindgen glue. A known kind carrying a wrong-typed field was worse: wasm-bindgen coerced a numeric newName through USVString and renamed the node, rather than rejecting the command. Every field the builders read is now checked against the type the protocol declares, and the default arm binds the descriptor to never so a new command kind without a builder is a compile error. Closes #1051
d681e47 to
fc548ff
Compare
|
Consolidated into #1145. Both changes are fail-closed fixes in |
buildCommandswitched overCommandDescriptorwith nodefaultarm, so an unknownkindfell out returningundefinedand only failed because the wasm-bindgen glue happened to reject it — fail-closed by accident, and inconsistent with the three sibling switches in the same file that already fail closed deliberately.The sharper case was a known kind carrying a wrong-typed field.
{ kind: 'rename', node: <16 bytes>, newName: 12345 }reachedwasm.Command.rename(nodeId, 12345)and wasm-bindgen'sUSVStringmarshalling coerced it, renaming the node to"12345"instead of rejecting the command. Reachable from a version-skewed follower build: the private-port relay validates the command shape and deliberately leaves field validation to the codec, so that division only holds if the codec enforces it.What changed
Uint8Array, strings a real string,opIda realbigint. The checkers takeunknown, because the descriptor arrives as plain data across a realm boundary and its static type is a claim, not a guarantee.nodeKindandpermissionno longer fall through toFolder/Writeon an unrecognised literal; they reject it. That was the same silent-coercion class, one layer down.defaultarm binds the descriptor toneverand throws. Verified by temporarily adding a seventeenth kind toCommandDescriptor:tscfails withType '{ kind: "probeNewKind"; … }' is not assignable to type 'never'at that binding, so a new kind cannot be added without a builder.Valid commands are unaffected — every check passes through the value it validated.
Tests
packages/client/src/worker/commandCodec.test.tsgains five refusal cases driven against a permissive fake whose every builder succeeds, so only the codec's own checks can reject: an unknown kind, a numericnewNameand a nullname, a string where a public key belongs and an array where a node id belongs, an unrecognisednodeKindandpermission, and a numberopId. A sixth asserts the accept side of both mirror enums —'folder'and'write', not only the literal each mapper tests first.Not in this PR
The byte and string arguments of
beginWrite,snapshot,downloadandopenContentStreamreach WASM from the same untrusted message with the same coercion, andpassArray8ToWasm0turns a 16-character string into sixteen zero bytes rather than rejecting it. That is #1154, which reuses the checkers this PR introduces.Gates
pnpm typecheck0,pnpm test0,pnpm lint0,pnpm lint:tracker-refs0.Closes #1051