Skip to content

fix(computer-use): guard unsafe Linux uinput fallback - #74171

Open
iamantonio wants to merge 2 commits into
NousResearch:mainfrom
iamantonio:fix/74148-linux-uinput-guard
Open

fix(computer-use): guard unsafe Linux uinput fallback#74171
iamantonio wants to merge 2 commits into
NousResearch:mainfrom
iamantonio:fix/74148-linux-uinput-guard

Conversation

@iamantonio

Copy link
Copy Markdown

Closes #74148

Summary

  • add a shared Linux/X11 safety predicate for the cua-driver /dev/uinput XInput-master leak fixed by fix(cua-driver): avoid leaking XInput masters without uinput trycua/cua#2631
  • augment hermes computer-use doctor with an actionable degraded check when the exact unsafe combination is detected
  • refuse native input before any driver action when Linux + X11 + cua-driver <0.13.1 + inaccessible /dev/uinput are all true
  • keep capture, app/window listing, and inspection available
  • probe the exact manifest-resolved MCP executable rather than a potentially different wrapper/PATH binary

Why

cua-driver issue #2618 documents a Linux/X11 failure in which the driver created an XInput master pair before opening /dev/uinput. If opening uinput failed, the pair was not recorded for cleanup and repeated input attempts could leak additional master keyboard/pointer pairs.

The ordering was fixed by trycua/cua#2631 and released in cua-driver-rs-v0.13.1. Because 0.13.1 is currently a pre-release while the stable updater can still report 0.12.6, Hermes needs a local fail-safe for affected Linux users.

Safety behavior

The guard activates only when all four facts are known:

  1. sys.platform == "linux"
  2. DISPLAY is set
  3. the driver version is parseable and <0.13.1
  4. /dev/uinput cannot actually be opened read/write

Unknown/malformed versions preserve current behavior. Fixed drivers, non-Linux platforms, display-less sessions, and accessible uinput preserve current behavior. Fixed/irrelevant environments do not open /dev/uinput unnecessarily.

The remediation recommends upgrading the driver. It does not change device permissions, invoke privilege escalation, install a pre-release automatically, or alter service lifecycle.

Verification

scripts/run_tests.sh \
  tests/computer_use/test_doctor.py \
  tests/computer_use/test_uinput_safety.py \
  tests/tools/test_computer_use.py \
  tests/tools/test_computer_use_cua_backend_linux.py \
  tests/tools/test_computer_use_delivery_ladder.py \
  tests/tools/test_computer_use_uinput_guard.py \
  tests/computer_use/test_cua_no_overlay.py \
  tests/computer_use/test_cua_spawn_env_sanitization.py -q

394 passed, 0 failed

Real Linux/X11 validation against installed cua-driver 0.13.1 with inaccessible /dev/uinput:

{
  "platform": "linux",
  "driver_version": "0.13.1",
  "overall": "ok",
  "risk_checks": []
}

A live Chrome/GitHub computer-use regression using 0.13.1 completed an authenticated issue submission, followed by clean teardown with no local cua-driver process and no CUA XInput devices.

Review provenance

Implementation used Tank/Claude Code under a constrained worktree. A separate local-only Codex review found that the first implementation could probe a wrapper rather than the actual manifest-relocated MCP executable. That blocking finding was fixed by recording and probing the exact launched command; the fresh post-fix review reported no blocking findings. Hermes independently reran the CI-parity test suite and live runtime checks.

@PRATHAMESH75 PRATHAMESH75 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed against #74148 — this looks like a complete, correct fix for the reported trigger (Linux/X11 + cua-driver <0.13.1 + /dev/uinput not read+write accessible). Mapping the issue's asks to the diff:

  • Detect in doctordoctor.py + the pure uinput_safety.detect_uinput_leak_risk() predicate report the input path as unsafe while capture/inspection/window discovery stay available (the guard is per-action on input only). ✅
  • Refuse input actions with an actionable errorCuaDriverBackend._uinput_leak_guard() returns a refusal ActionResult before an input session is declared or the driver is invoked, and uinput_leak_risk_action_result() carries the message. ✅
  • Version boundary is rightdriver_version_is_vulnerable() treats 0.13.0 as vulnerable and 0.13.1/0.14.0 as safe, matching cua-driver-rs-v0.13.1 as the fixed release. ✅
  • Conservative on the unknowns — an unparseable/None version and an accessible /dev/uinput both preserve current behavior (return None), so the guard never guesses or over-blocks. ✅
  • Probes the actual MCP command — the guard reads _resolved_mcp_command (the binary _lifecycle_coro really spawned) rather than independently re-resolving, so a fixed wrapper can't mask a vulnerable relocated executable (trycua/cua#1961). Nice detail.
  • The --version probe short-circuits off-Linux/no-DISPLAY and is cached per backend instance, and /dev/uinput is only opened once the platform/DISPLAY/version triple still leaves risk open — so there's no cost on the common path.

Test coverage (test_uinput_safety.py, test_doctor.py, test_computer_use_uinput_guard.py) exercises the platform/display/version/accessibility matrix directly via the DI'd predicate. I don't see a gap. Nothing further needed from my side — this closes #74148.

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/tools Tool registry, model_tools, toolsets labels Jul 29, 2026
Detect cua-driver versions affected by trycua/cua#2618 before native input on Linux/X11. Surface the unsafe combination in computer-use doctor, preserve capture-only access, and probe the exact manifest-resolved MCP executable.
@iamantonio
iamantonio force-pushed the fix/74148-linux-uinput-guard branch from ae53a41 to db808fe Compare July 30, 2026 01:28

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the focused Linux/X11 mitigation. The current backend sends native actions through _run_input_action() without this safety predicate (tools/computer_use/cua_backend.py:2648-2829), and upstream's merged fix confirms the underlying pre-0.13.1 leak.

Problems

  • tools/computer_use/doctor.py:732 bases the new diagnostic on health_report's driver_version. Existing code explicitly recognizes that this value can disagree with the resolved executable's --version (tools/computer_use/doctor.py:102-150), but hermes_identity is only built after augmentation in run_doctor() (tools/computer_use/doctor.py:843 on current main). A vulnerable executable reported as a fixed version would therefore receive no doctor warning.

Suggested changes

  • Resolve and normalize the executable version before augmentation, prefer it for this predicate, and add the mismatched-report regression.

This is an automated hermes-sweeper review.

platform_value = report.get("platform")
if not isinstance(platform_value, str):
platform_value = sys.platform
driver_version = report.get("driver_version")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please derive the guard version from the resolved binary's --version output, with this report field only as a fallback. health_report is already known to disagree with the executable version, so a report claiming 0.13.1 could hide a vulnerable 0.12.6 binary from computer-use doctor.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 30, 2026
@iamantonio

Copy link
Copy Markdown
Author

Addressed the sweeper finding in 0e70de11b:

  • build the resolved-binary identity before uinput-risk augmentation
  • prefer the executable's parseable --version for the predicate
  • fall back to health_report.driver_version when CLI output is unparseable
  • add regressions for the mismatched fixed-report/vulnerable-binary case and the unparseable-CLI fallback

Verification: scripts/run_tests.sh tests/computer_use/test_doctor.py tests/computer_use/test_uinput_safety.py tests/tools/test_computer_use_uinput_guard.py -q114 passed, 0 failed. A broader computer-use sweep also passed 377 tests locally.

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

Labels

comp/tools Tool registry, model_tools, toolsets P2 Medium — degraded but workaround exists sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Linux/X11: guard cua-driver <=0.12.6 when /dev/uinput is inaccessible

4 participants