fix(cua-driver-rs): warn + force prompt:false for check_permissions in CLI mode - #1575
fix(cua-driver-rs): warn + force prompt:false for check_permissions in CLI mode#1575hippoley wants to merge 2 commits into
Conversation
…n CLI mode Fixes trycua#1561. When cua-driver check_permissions is called from a shell or IDE terminal without a running daemon, macOS attributes the calling process to the parent application (Terminal.app, VS Code, Cursor, etc.) rather than to CuaDriver.app. AXIsProcessTrusted() therefore reflects the parent's Accessibility grant, not CuaDriver.app's — so results can be stale or misleading after tccutil reset Accessibility com.trycua.driver. This is the same bug that exists in the Swift binary; Swift's CallCommand.swift already handles it (lines 148-171). This commit ports that fix to the Rust CLI. Changes: - cli.rs: when no daemon is listening and tool == check_permissions, emit a stderr warning and force prompt:false (same logic as Swift). Prevents a TCC dialog being attributed to the wrong bundle identity. - check_permissions.rs: add the caveat to the tool description so MCP clients and cua-driver describe output surface the limitation. - status.rs: document the AXIsProcessTrusted() attribution caveat in the accessibility_granted() doc comment. The fix is purely additive — no behaviour change when the daemon is running (requests are forwarded through the Unix socket and run inside CuaDriver.app where attribution is correct).
|
@nishantpurohit04 is attempting to deploy a commit to the Cua Team on Vercel. A member of the Team first needs to authorize it. |
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThe PR addresses a macOS TCC attribution bug where ChangesTCC Attribution Caveat for check_permissions
Estimated code review effort🎯 2 (Simple) | ⏱️ ~8 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@libs/cua-driver-rs/crates/cua-driver/src/cli.rs`:
- Around line 674-679: The current code only sets prompt:false when json_args is
an Object, leaving non-object json_args (e.g., true, [], "str") unchanged and
causing check_permissions to treat them as prompt:true; change the coercion so
that regardless of the shape of json_args the returned value is an Object with
prompt:false: capture the original json_args, create a serde_json::Map, insert
("prompt", serde_json::Value::Bool(false)), then if the original was an Object
merge its entries into the map else insert the original under a new key (e.g.,
"value") before returning Some(serde_json::Value::Object(map)); update the code
around the variables json_args and coerced to implement this behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 81fa220e-dbfe-4a36-986d-f773fe3fb071
📒 Files selected for processing (3)
libs/cua-driver-rs/crates/cua-driver/src/cli.rslibs/cua-driver-rs/crates/platform-macos/src/permissions/status.rslibs/cua-driver-rs/crates/platform-macos/src/tools/check_permissions.rs
| let mut coerced = json_args | ||
| .unwrap_or_else(|| serde_json::Value::Object(serde_json::Map::new())); | ||
| if let serde_json::Value::Object(ref mut map) = coerced { | ||
| map.insert("prompt".into(), serde_json::Value::Bool(false)); | ||
| } | ||
| Some(coerced) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify the current coercion only mutates object-shaped args and that tool default is prompt=true.
rg -n -C3 'if let serde_json::Value::Object\(ref mut map\)' libs/cua-driver-rs/crates/cua-driver/src/cli.rs
rg -n -C3 'unwrap_or\(true\)' libs/cua-driver-rs/crates/platform-macos/src/tools/check_permissions.rsRepository: trycua/cua
Length of output: 1526
🏁 Script executed:
#!/bin/bash
# Check where json_args comes from and if non-object values are realistic
rg -n -B15 'let mut coerced = json_args' libs/cua-driver-rs/crates/cua-driver/src/cli.rs | head -40Repository: trycua/cua
Length of output: 973
🏁 Script executed:
#!/bin/bash
# See initial json_args assignment and full context of this block
sed -n '630,690p' libs/cua-driver-rs/crates/cua-driver/src/cli.rsRepository: trycua/cua
Length of output: 2648
🏁 Script executed:
#!/bin/bash
# Find function signature to see json_args parameter type/source
rg -n -B20 'let json_args = if tool == "check_permissions"' libs/cua-driver-rs/crates/cua-driver/src/cli.rs | grep -E '(fn|json_args)'Repository: trycua/cua
Length of output: 111
🏁 Script executed:
#!/bin/bash
# Find the function signature containing this code
sed -n '600,680p' libs/cua-driver-rs/crates/cua-driver/src/cli.rs | head -40Repository: trycua/cua
Length of output: 2237
🏁 Script executed:
#!/bin/bash
# Find the function containing this code block
rg -n 'fn.*json_args|fn main|pub fn' libs/cua-driver-rs/crates/cua-driver/src/cli.rs | head -10Repository: trycua/cua
Length of output: 691
🏁 Script executed:
#!/bin/bash
# Get the run_call function signature and initial lines
sed -n '576,620p' libs/cua-driver-rs/crates/cua-driver/src/cli.rsRepository: trycua/cua
Length of output: 2476
🏁 Script executed:
#!/bin/bash
# Find where run_call is invoked to see what json_args values are passed
rg -n 'run_call\(' libs/cua-driver-rs/crates/cua-driver/src/cli.rsRepository: trycua/cua
Length of output: 75
🏁 Script executed:
#!/bin/bash
# Search main and command handlers for where run_call is invoked
rg -n 'run_call' libs/cua-driver-rs/crates/cua-driver/src/cli.rsRepository: trycua/cua
Length of output: 75
🏁 Script executed:
#!/bin/bash
# Search entire crate for invocations of run_call
rg -n 'run_call' libs/cua-driver-rs/crates/cua-driver/src/Repository: trycua/cua
Length of output: 510
🏁 Script executed:
#!/bin/bash
# Check main.rs to see where json_args comes from
sed -n '100,130p' libs/cua-driver-rs/crates/cua-driver/src/main.rs
sed -n '340,360p' libs/cua-driver-rs/crates/cua-driver/src/main.rsRepository: trycua/cua
Length of output: 2810
🏁 Script executed:
#!/bin/bash
# Find the Command enum definition to see json_args type
rg -n 'enum Command|struct Call' libs/cua-driver-rs/crates/cua-driver/src/cli.rs | head -20Repository: trycua/cua
Length of output: 76
🏁 Script executed:
#!/bin/bash
# Show the Command enum definition
sed -n '19,100p' libs/cua-driver-rs/crates/cua-driver/src/cli.rsRepository: trycua/cua
Length of output: 4037
🏁 Script executed:
#!/bin/bash
# Find where json_args is parsed from command line arguments
rg -n 'json_args' libs/cua-driver-rs/crates/cua-driver/src/cli.rs | head -20Repository: trycua/cua
Length of output: 695
🏁 Script executed:
#!/bin/bash
# See how json_args is parsed from command line input
sed -n '200,220p' libs/cua-driver-rs/crates/cua-driver/src/cli.rs
sed -n '245,265p' libs/cua-driver-rs/crates/cua-driver/src/cli.rsRepository: trycua/cua
Length of output: 1773
🏁 Script executed:
#!/bin/bash
# Verify the suggested fix logic is sound
cat <<'EOF' > /tmp/test_fix.rs
// Current buggy code:
let json_args_buggy = Some(serde_json::json!(true)); // or [], "str", etc.
let mut coerced = json_args_buggy
.unwrap_or_else(|| serde_json::Value::Object(serde_json::Map::new()));
if let serde_json::Value::Object(ref mut map) = coerced {
map.insert("prompt".into(), serde_json::Value::Bool(false));
}
// Result: coerced is still true, no "prompt" key inserted
// Suggested fixed code:
let json_args_fixed = Some(serde_json::json!(true)); // or [], "str", etc.
let mut map = match json_args_fixed {
Some(serde_json::Value::Object(map)) => map,
_ => serde_json::Map::new(),
};
map.insert("prompt".into(), serde_json::Value::Bool(false));
let result = Some(serde_json::Value::Object(map));
// Result: always a new Object with "prompt": false
EOF
cat /tmp/test_fix.rsRepository: trycua/cua
Length of output: 833
Force prompt: false for all arg shapes, not only object args.
When json_args is a non-object JSON value (e.g., true, [], or "string"), this branch leaves it unchanged, and check_permissions falls back to prompt=true. That re-enables the misattributed TCC prompt in exactly the path this PR is trying to prevent. Users can pass non-object JSON via the command line, so this is not an edge case.
Suggested fix
- let mut coerced = json_args
- .unwrap_or_else(|| serde_json::Value::Object(serde_json::Map::new()));
- if let serde_json::Value::Object(ref mut map) = coerced {
- map.insert("prompt".into(), serde_json::Value::Bool(false));
- }
- Some(coerced)
+ let mut map = match json_args {
+ Some(serde_json::Value::Object(map)) => map,
+ _ => serde_json::Map::new(),
+ };
+ map.insert("prompt".into(), serde_json::Value::Bool(false));
+ Some(serde_json::Value::Object(map))📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| let mut coerced = json_args | |
| .unwrap_or_else(|| serde_json::Value::Object(serde_json::Map::new())); | |
| if let serde_json::Value::Object(ref mut map) = coerced { | |
| map.insert("prompt".into(), serde_json::Value::Bool(false)); | |
| } | |
| Some(coerced) | |
| let mut map = match json_args { | |
| Some(serde_json::Value::Object(map)) => map, | |
| _ => serde_json::Map::new(), | |
| }; | |
| map.insert("prompt".into(), serde_json::Value::Bool(false)); | |
| Some(serde_json::Value::Object(map)) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@libs/cua-driver-rs/crates/cua-driver/src/cli.rs` around lines 674 - 679, The
current code only sets prompt:false when json_args is an Object, leaving
non-object json_args (e.g., true, [], "str") unchanged and causing
check_permissions to treat them as prompt:true; change the coercion so that
regardless of the shape of json_args the returned value is an Object with
prompt:false: capture the original json_args, create a serde_json::Map, insert
("prompt", serde_json::Value::Bool(false)), then if the original was an Object
merge its entries into the map else insert the original under a new key (e.g.,
"value") before returning Some(serde_json::Value::Object(map)); update the code
around the variables json_args and coerced to implement this behavior.
…sions Address CodeRabbit review on PR trycua#1575. The previous coercion used 'if let Object' which silently skipped non-object json_args (e.g. true, [], "str"), leaving prompt defaulting to true and re-enabling the misattributed TCC dialog. Replace with a match that always produces a fresh Object with prompt:false, regardless of the original arg shape.
|
Good catch — fixed in 992e512. Replaced the |
Problem
cua-driver check_permissionsreports stale/wrong results aftertccutil reset Accessibility com.trycua.driverwhen called from a shell or IDE terminal without a running daemon.Root cause: macOS attributes the calling CLI process to the responsible parent application (Terminal.app, VS Code, Cursor, etc.) rather than to CuaDriver.app.
AXIsProcessTrusted()therefore reflects the parent's Accessibility grant, not CuaDriver.app's.The Swift binary already handles this in
CallCommand.swiftlines 148-171. The Rust CLI had no equivalent guard.Fix
Ports the Swift fix to the Rust CLI:
cli.rs: when no daemon is listening andtool == check_permissions, emit a stderr warning and forceprompt:false. Prevents a TCC dialog being attributed to the wrong bundle identity.check_permissions.rs: add the caveat to the tool description so MCP clients andcua-driver describesurface the limitation.status.rs: document theAXIsProcessTrusted()attribution caveat in theaccessibility_granted()doc comment.No behaviour change when the daemon is running (requests are forwarded through the Unix socket and run inside CuaDriver.app where attribution is correct).
Fixes #1561
Summary by CodeRabbit