fix(cua-driver): reject malformed tool arguments found by fuzzing - #3545
Open
r33drichards wants to merge 2 commits into
Open
fix(cua-driver): reject malformed tool arguments found by fuzzing#3545r33drichards wants to merge 2 commits into
r33drichards wants to merge 2 commits into
Conversation
r33drichards
force-pushed
the
fix/cua-driver-tool-boundary-fuzz-findings
branch
from
September 4, 2026 19:30
f901bcd to
f8ed5ca
Compare
r33drichards
marked this pull request as ready for review
September 4, 2026 23:00
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
The changes directly address confirmed crash/contract defects with focused boundary checks plus regression tests and fuzz seeds, without introducing risky behavioral changes outside malformed-input handling.
Pull request overview
Hardens the Cua Driver MCP tool-call dispatch boundary based on new fuzzing results, preventing malformed client requests from crashing the driver and aligning typed parsing behavior with the published contract where the runtimes already enforce stricter rules.
Changes:
- Reject non-object
argumentsat theToolRegistry::invoke_authorizedboundary with a structuredinvalid_argumentsrefusal (instead of panicking). - Enforce the published
minItems: 2requirement forhotkey.keysduring typed parsing via a customdeserialize_withvalidator. - Keep discovered crash inputs as fuzz regression seeds, and narrow the fuzz schema invariant to structural agreement by filtering out range/length keyword failures until parser enforcement is completed.
File summaries
| File | Description |
|---|---|
| libs/cua-driver/rust/crates/cua-driver-core/src/tool.rs | Adds a top-level guard rejecting non-object arguments before any indexing/normalization work occurs. |
| libs/cua-driver/rust/crates/cua-driver-core/tests/tool_arguments_boundary.rs | Adds regression tests ensuring malformed arguments are rejected (and unknown tools still error as unknown). |
| libs/cua-driver/rust/crates/cua-driver-contract/src/inputs.rs | Enforces hotkey.keys minimum length during deserialization and adds a unit test for the schema/parser agreement. |
| libs/cua-driver/rust/crates/cua-driver-testkit/src/boundary_fuzz.rs | Adjusts the fuzz invariant to ignore range/length keyword mismatches while keeping structural validation. |
| libs/cua-driver/docs/2026-09-04-tool-call-boundary-fuzzing-design.md | Updates the design note with first results and documents the temporary invariant relaxation and follow-up. |
| libs/cua-driver/rust/fuzz/corpus/mcp_request/bool_arguments.json | Adds a seed that previously triggered the dispatcher panic via non-object arguments. |
| libs/cua-driver/rust/fuzz/corpus/tool_arguments/hotkey_empty_keys.bin | Adds a seed covering the hotkey.keys too-short case. |
| libs/cua-driver/rust/fuzz/corpus/typed_input_json/hotkey_empty_keys_sequence.json | Adds a typed-input JSON seed covering the hotkey.keys too-short case. |
Review details
- Files reviewed: 8/8 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
injaneity
approved these changes
Sep 5, 2026
Fix the two defects the new tool-call boundary fuzzer found on its first
runs and keep the crash inputs as regression seeds.
A non-object `arguments` value (for example `true`) for any registered tool
reached the session-stamping index assignment in
`ToolRegistry::invoke_authorized` and panicked. The stdio proxy dispatches
inline, so one malformed request from an MCP client terminated the driver
process. Dispatch now refuses non-object arguments with an
`invalid_arguments` refusal; unknown tool names still win.
`HotkeyInput` advertised `minItems: 2` for `keys` and every platform runtime
refuses fewer, but the contract parser accepted `{"keys": []}`. The parser
now enforces the minimum.
The fuzzer then hit the same class on other advertised range and length
bounds (nine fields) that the parser does not enforce and the runtimes check
later. The schema-agreement invariant now checks structural agreement only
and the design note lists every affected field as a follow-up.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UcJkf8QAvMxw7YoPZ1SvFv
The doc comment added with the `deserialize_with` check became a schema description and made the checked-in contract manifest stale. Use a plain comment so the parser-only fix leaves the published contract unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UcJkf8QAvMxw7YoPZ1SvFv
r33drichards
force-pushed
the
fix/cua-driver-tool-boundary-fuzz-findings
branch
from
September 5, 2026 18:38
2680f62 to
a1823c9
Compare
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.
Summary
Stacked on #3544. Fixes the two defects the new tool-call boundary fuzzer found on its first runs, keeps the crash inputs as regression seeds, and narrows the schema-agreement invariant to what the contract parser actually enforces so the fuzz workflow is green.
Fix 1: non-object
argumentspanicked the dispatcherMCP types
argumentsas an object andRequest::tool_callsubstitutes{}when it is absent, but a client can still send any JSON value. A value liketruefor a registered tool reached the session-stamping index assignment inToolRegistry::invoke_authorizedand panicked. The stdio proxy dispatches inline, so one malformed request from an MCP client terminated the driver process.invoke_authorizednow refuses non-object arguments with aninvalid_argumentsrefusal (same structured shapeparse_typed_inputuses). Unknown tool names still win over malformed arguments.crates/cua-driver-core/tests/tool_arguments_boundary.rsfuzz/corpus/mcp_request/bool_arguments.jsonFix 2:
hotkey.keysparser ignored the advertised two-key minimumThe published schema says
minItems: 2and every platform runtime refuses fewer than two keys, butHotkeyInputaccepted{"keys": []}and{"keys": ["ctrl"]}. The parser now enforces the minimum through adeserialize_withcheck, so a typed input can never carry a combination the runtimes would refuse.cua-driver-contracthotkey_parser_enforces_the_published_two_key_minimumfuzz/corpus/tool_arguments/hotkey_empty_keys.bin,fuzz/corpus/typed_input_json/hotkey_empty_keys_sequence.jsonTriage: advertised range/length bounds are runtime-enforced
After the hotkey fix the fuzzer immediately hit the same class on
set_agent_cursor_theme.theme_id(schemaminLength: 1, parser accepts""). This is systemic: the contract schemas advertise numeric range and length bounds on nine fields (pids, click count, drag steps, scroll amount, theme id and detail lengths, menu path shape) that the typed parser does not enforce and the platform runtimes check later. The fuzz invariant now checks structural agreement only (types, required keys, enums, unknown properties) and ignores those keywords; the design note lists every affected field. Enforcing them in the parser the wayhotkey.keysnow is, then dropping the filter inboundary_fuzz::is_value_bound_error, is a follow-up.Validation
cargo test -p cua-driver-core -p cua-driver-contract -p cua-driver-testkit --all-targetspasses on nixpkgs Rust 1.91 (core: 587 tests, contract: 33, smoke: 6).cargo test -p cua-driver --all-targets --no-runcompiles.mcp_request1.27M execs,registry_invoke0.94M,tool_arguments1.51M,typed_input_json1.76M.cargo fmt --all -- --checkclean.Releasing as a patch: the dispatcher panic is user-visible (a malformed MCP request could kill the driver).
🤖 Generated with Claude Code
https://claude.ai/code/session_01UcJkf8QAvMxw7YoPZ1SvFv