fix: allow safe Windows companion node commands (#71884) · openclaw/openclaw@8f277e4
shanselman
·
2026-04-28
·
via Recent Commits to openclaw:main
| Original file line number | Diff line number | Diff line change |
|---|
@@ -62,6 +62,10 @@ Docs: https://docs.openclaw.ai
|
62 | 62 | - Agents/failover: seed non-claude-cli fallback prompts with Claude Code session context when a claude-cli attempt fails, so fallback models do not restart cold after billing or quota failover. (#72069) Thanks @stainlu. |
63 | 63 | - Agents/CLI runner: transfer bundle-MCP tempDir cleanup from the per-turn runner finally to the Claude live-session lifecycle, so persistent Claude CLI sessions keep their `--mcp-config` directory until the live subprocess closes. Fixes #73244. Thanks @edwin-rivera-dev. |
64 | 64 | |
| 65 | +### Fixes |
| 66 | + |
| 67 | +- Gateway/nodes: allow Windows companion nodes to use safe declared commands such as canvas, camera list, location, device info, and screen snapshot by default while keeping dangerous media commands opt-in. (#71884) Thanks @shanselman. |
| 68 | + |
65 | 69 | ## 2026.4.27 |
66 | 70 | |
67 | 71 | ### Changes |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -451,7 +451,7 @@ See [Plugins](/tools/plugin).
|
451 | 451 | - `trustedProxies`: reverse proxy IPs that terminate TLS or inject forwarded-client headers. Only list proxies you control. Loopback entries are still valid for same-host proxy/local-detection setups (for example Tailscale Serve or a local reverse proxy), but they do **not** make loopback requests eligible for `gateway.auth.mode: "trusted-proxy"`. |
452 | 452 | - `allowRealIpFallback`: when `true`, the gateway accepts `X-Real-IP` if `X-Forwarded-For` is missing. Default `false` for fail-closed behavior. |
453 | 453 | - `gateway.nodes.pairing.autoApproveCidrs`: optional CIDR/IP allowlist for auto-approving first-time node device pairing with no requested scopes. It is disabled when unset. This does not auto-approve operator/browser/Control UI/WebChat pairing, and it does not auto-approve role, scope, metadata, or public-key upgrades. |
454 | | -- `gateway.nodes.allowCommands` / `gateway.nodes.denyCommands`: global allow/deny shaping for declared node commands after pairing and allowlist evaluation. |
| 454 | +- `gateway.nodes.allowCommands` / `gateway.nodes.denyCommands`: global allow/deny shaping for declared node commands after pairing and platform allowlist evaluation. Use `allowCommands` to opt into dangerous node commands such as `camera.snap`, `camera.clip`, and `screen.record`; `denyCommands` removes a command even if a platform default or explicit allow would otherwise include it. After a node changes its declared command list, reject and re-approve that device pairing so the gateway stores the updated command snapshot. |
455 | 455 | - `gateway.tools.deny`: extra tool names blocked for HTTP `POST /tools/invoke` (extends default deny list). |
456 | 456 | - `gateway.tools.allow`: remove tool names from the default HTTP deny list. |
457 | 457 | |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -188,6 +188,23 @@ openclaw nodes invoke --node <idOrNameOrIp> --command canvas.eval --params '{"ja
|
188 | 188 | |
189 | 189 | Higher-level helpers exist for the common “give the agent a MEDIA attachment” workflows. |
190 | 190 | |
| 191 | +## Command policy |
| 192 | + |
| 193 | +Node commands must pass two gates before they can be invoked: |
| 194 | + |
| 195 | +1. The node must declare the command in its WebSocket `connect.commands` list. |
| 196 | +2. The gateway's platform policy must allow the declared command. |
| 197 | + |
| 198 | +Windows and macOS companion nodes allow safe declared commands such as |
| 199 | +`canvas.*`, `camera.list`, `location.get`, and `screen.snapshot` by default. |
| 200 | +Dangerous or privacy-heavy commands such as `camera.snap`, `camera.clip`, and |
| 201 | +`screen.record` still require explicit opt-in with |
| 202 | +`gateway.nodes.allowCommands`. `gateway.nodes.denyCommands` always wins over |
| 203 | +defaults and extra allowlist entries. |
| 204 | + |
| 205 | +After a node changes its declared command list, reject the old device pairing |
| 206 | +and approve the new request so the gateway stores the updated command snapshot. |
| 207 | + |
191 | 208 | ## Screenshots (canvas snapshots) |
192 | 209 | |
193 | 210 | If the node is showing the Canvas (WebView), `canvas.snapshot` returns `{ format, base64 }`. |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -718,6 +718,31 @@ describe("resolveNodeCommandAllowlist", () => {
|
718 | 718 | expect(allow.has("screen.record")).toBe(false); |
719 | 719 | }); |
720 | 720 | |
| 721 | +it("allows safe Windows companion commands by default but keeps dangerous media gated", () => { |
| 722 | +const allow = resolveNodeCommandAllowlist( |
| 723 | +{}, |
| 724 | +{ |
| 725 | +platform: "Windows_NT", |
| 726 | +deviceFamily: "Windows", |
| 727 | +}, |
| 728 | +); |
| 729 | + |
| 730 | +expect(allow.has("canvas.present")).toBe(true); |
| 731 | +expect(allow.has("canvas.a2ui.pushJSONL")).toBe(true); |
| 732 | +expect(allow.has("camera.list")).toBe(true); |
| 733 | +expect(allow.has("location.get")).toBe(true); |
| 734 | +expect(allow.has("device.info")).toBe(true); |
| 735 | +expect(allow.has("device.status")).toBe(true); |
| 736 | +expect(allow.has("screen.snapshot")).toBe(true); |
| 737 | +expect(allow.has("system.run")).toBe(true); |
| 738 | +expect(allow.has("system.which")).toBe(true); |
| 739 | +expect(allow.has("system.notify")).toBe(true); |
| 740 | + |
| 741 | +for (const cmd of DEFAULT_DANGEROUS_NODE_COMMANDS) { |
| 742 | +expect(allow.has(cmd)).toBe(false); |
| 743 | +} |
| 744 | +}); |
| 745 | + |
721 | 746 | it("can explicitly allow dangerous commands via allowCommands", () => { |
722 | 747 | const allow = resolveNodeCommandAllowlist( |
723 | 748 | { |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -115,7 +115,14 @@ const PLATFORM_DEFAULTS: Record<string, string[]> = {
|
115 | 115 | ...SCREEN_COMMANDS, |
116 | 116 | ], |
117 | 117 | linux: [...SYSTEM_COMMANDS], |
118 | | -windows: [...SYSTEM_COMMANDS], |
| 118 | +windows: [ |
| 119 | + ...CANVAS_COMMANDS, |
| 120 | + ...CAMERA_COMMANDS, |
| 121 | + ...LOCATION_COMMANDS, |
| 122 | + ...DEVICE_COMMANDS, |
| 123 | + ...SYSTEM_COMMANDS, |
| 124 | + ...SCREEN_COMMANDS, |
| 125 | +], |
119 | 126 | // Fail-safe: unknown metadata should not receive host exec defaults. |
120 | 127 | unknown: [...UNKNOWN_PLATFORM_COMMANDS], |
121 | 128 | }; |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。