Skip to content

fix(gateway): check node commands before exec-approvals invoke#97729

Closed
lzyyzznl wants to merge 14 commits into
openclaw:mainfrom
lzyyzznl:fix/issue-97578
Closed

fix(gateway): check node commands before exec-approvals invoke#97729
lzyyzznl wants to merge 14 commits into
openclaw:mainfrom
lzyyzznl:fix/issue-97578

Conversation

@lzyyzznl

@lzyyzznl lzyyzznl commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Summary

Problem: When openclaw approvals get --node <node-id> --json targets a node that does not advertise system.execApprovals.get or system.execApprovals.set, the command currently returns a raw JavaScript TypeError (Cannot read properties of undefined (reading 'socket')) instead of a structured, actionable error.

Solution: This PR adds a capability preflight check in the gateway's exec-approvals node handler before invoking the node, adds system.execApprovals.get/set to both the desktop host command allowlist and platform system commands so newly paired desktop nodes correctly include these commands in their approved surface, and adds admin-scope pairing authorization for exec-approval commands.

What changed:

  1. Added NODE_EXEC_APPROVALS_COMMANDS to PLATFORM_DEFAULTS desktop entries in node-command-policy.ts so initial desktop node pairing correctly includes exec-approval commands in the pairing allowlist (feeds into PLATFORM_DEFAULTS for macos/windows/linux).
  2. Added NODE_EXEC_APPROVALS_COMMANDS to DESKTOP_HOST_COMMANDS in node-command-policy.ts so desktop node reconnect preservation retains exec-approval commands.
  3. Added a capability preflight in respondWithExecApprovalsNodePayload that checks nodeSession.commands (approved surface) before invoking. Unknown nodes fall through to the existing invoke error mapping, preserving full details.nodeError.
  4. Updated resolveNodePairApprovalScopes to require operator.admin for NODE_EXEC_APPROVALS_COMMANDS, matching the existing admin requirement for NODE_SYSTEM_RUN_COMMANDS. Pre-paired capable nodes are now authorized via admin-only pairing scope.
  5. Added details?: unknown to the rpcReq test helper error type so integration tests can inspect error.details without narrowing casts.
  6. Wove all three evidence sources (unit tests, pre-paired capable-node integration test, unknown-node integration test).
  7. Removed scripts/live-verify-97578.ts (one-off verification script, replaced by integration tests).

What did NOT change: The normal code path when the node supports the requested command is identical. No config, API, or protocol changes. No dependencies added.

Fixes #97578

What Problem This Solves

Operators using the CLI to diagnose exec approvals on nodes that lack approvals-management capability get a confusing internal TypeError. The error hides the real problem: the node does not support the requested capability. This fix surfaces the capability mismatch as a structured error with an actionable remediation hint.

Root Cause

Two issues contributed:

  1. Missing allowlist entries: SYSTEM_COMMANDS did not include system.execApprovals.get/set, so initial desktop node pairing filtered out exec-approval commands before they reached the approved command surface. DESKTOP_HOST_COMMANDS also lacked them, preventing reconnect preservation.

  2. Missing preflight guard: respondWithExecApprovalsNodePayload called context.nodeRegistry.invoke() without checking the target node's approved command surface. The sibling node.invoke handler already performed this guard via isNodeCommandAllowed (nodes.ts:1413), but the exec-approvals relay path did not.

When a node that lacked approvals-management capability was targeted, invoke returned an unexpected result shape, causing the CLI to crash with Cannot read properties of undefined (reading 'socket').

Linked context

  • The existing sibling guard pattern: src/gateway/server-methods/nodes.ts:1413 uses isNodeCommandAllowed to check nodeSession.commands before invoking — this PR applies the same pattern to the exec-approvals relay path.
  • The preflight uses nodeSession.commands (the approved, post-pairing-allowlist surface). With the PLATFORM_DEFAULTS + DESKTOP_HOST_COMMANDS allowlist fix, paired desktop nodes now include exec-approval commands in commands.
  • Unknown nodes (not connected) fall through to nodeRegistry.invoke so the existing NOT_CONNECTED error mapping via respondUnavailableOnNodeInvokeError preserves details.nodeError.
  • Pairing authorization now requires operator.admin for exec-approval commands (via resolveNodePairApprovalScopes), matching the existing admin scope for NODE_SYSTEM_RUN_COMMANDS. This is verified by 4 node-pairing-authz.test.ts assertions.
  • Security boundary: commands is the approved surface (filtered through pairing allowlist). Using it for the preflight guard prevents invoking unapproved declared commands.

Real behavior proof

Behavior addressed: Gateway returns structured INVALID_REQUEST error when a connected node lacks system.execApprovals.get or system.execApprovals.set in its approved commands surface. Unknown nodes receive proper UNAVAILABLE errors with details.nodeError via the existing invoke error mapping. The local exec.approvals.get handler continues to work correctly. Pre-paired nodes with admin-approved exec-approval commands connect successfully with exec-approvals in their effective command surface.

Real environment tested: Real OpenClaw Gateway WebSocket server started on Windows (localhost, loopback bind), Node 22.19. Two real node clients connected via connectGatewayClient with distinct device identities — one pre-paired via approveNodePairing with operator.admin scope. All exec-approvals handlers exercised via actual WebSocket RPC calls through the gateway dispatch layer (not mocked handler calls).

Exact steps or command run after this patch:

node scripts/run-vitest.mjs src/gateway/server-methods/exec-approvals.integration.test.ts
node scripts/run-vitest.mjs src/gateway/server-methods/exec-approvals.test.ts
node scripts/run-vitest.mjs src/gateway/node-command-policy.test.ts
node scripts/run-vitest.mjs src/infra/node-pairing-authz.test.ts

After-fix evidence: Real gateway server RPC responses (actual WebSocket JSON frames captured at 2026-06-30) for all 6 test scenarios below.

Scenario 1 — Unknown node exec.approvals.node.get

{
  "type": "res",
  "id": "fd7fb326-305d-4cac-9103-2441bad3394d",
  "ok": false,
  "error": {
    "code": "UNAVAILABLE",
    "message": "NOT_CONNECTED: node not connected",
    "details": {
      "nodeError": {
        "code": "NOT_CONNECTED",
        "message": "node not connected"
      }
    }
  }
}

Scenario 2 — Unknown node exec.approvals.node.set

{
  "type": "res",
  "id": "a5b3ae94-bfaa-4335-9f5c-757e69a5d0ff",
  "ok": false,
  "error": {
    "code": "UNAVAILABLE",
    "message": "NOT_CONNECTED: node not connected",
    "details": {
      "nodeError": {
        "code": "NOT_CONNECTED",
        "message": "node not connected"
      }
    }
  }
}

Scenario 3 — Pre-paired capable node connects and is listed in node.list

The capable node was pre-paired via approveNodePairing with operator.admin scope, matching the updated resolveNodePairApprovalScopes which now requires admin for exec-approvals commands. When the node connects, reconcileNodePairingOnConnect finds the paired entry and sets effectiveCommands to include system.execApprovals.get and system.execApprovals.set (alongside system.run, system.notify, browser.proxy).

This is verifiable indirectly: the node connects successfully through the full gateway pairing/reconnect flow and appears in node.list. The pairing authz test suite (node-pairing-authz.test.ts) independently verifies that exec-approvals commands require operator.admin scope (4 assertions). Combined with the preflight gate evidence below, the full chain is proven: admin pairing → approved commands surface → preflight gate.

Scenario 4 — Connected node without exec-approvals.get returns INVALID_REQUEST

{
  "type": "res",
  "id": "96d9e75c-54ed-4741-bfa5-5173b83361f9",
  "ok": false,
  "error": {
    "code": "INVALID_REQUEST",
    "message": "Node cf833bc0... does not support system.execApprovals.get. The node must advertise system.execApprovals.get, or the operator must edit the node host approvals file directly.",
    "details": {
      "supportedCommands": [],
      "requestedCommand": "system.execApprovals.get"
    }
  }
}

Scenario 5 — Connected node without exec-approvals.set returns INVALID_REQUEST

{
  "type": "res",
  "id": "aa0b80d5-648a-444f-95e6-0b61eba7c5d9",
  "ok": false,
  "error": {
    "code": "INVALID_REQUEST",
    "message": "Node 79f8fd92... does not support system.execApprovals.set. The node must advertise system.execApprovals.set, or the operator must edit the node host approvals file directly.",
    "details": {
      "supportedCommands": [],
      "requestedCommand": "system.execApprovals.set"
    }
  }
}

Scenario 6 — exec.approvals.get (local, non-node regression)

{
  "type": "res",
  "id": "90cae1aa-c755-4a4c-8df8-5342fc0accd9",
  "ok": true,
  "payload": {
    "path": "<temp-dir>/.openclaw/exec-approvals.json",
    "exists": true,
    "hash": "<sha256>",
    "file": {
      "version": 1,
      "socket": { "path": "<temp-dir>/.openclaw/exec-approvals.sock" },
      "defaults": {},
      "agents": {}
    }
  }
}

Pairing authorization evidence (node-pairing-authz.test.ts)

// exec-approvals commands now require operator.admin, matching system.run:
expect(resolveNodePairApprovalScopes(["system.execApprovals.get"])).toEqual([
  "operator.pairing",
  "operator.admin",
]);
expect(resolveNodePairApprovalScopes(["system.execApprovals.set"])).toEqual([
  "operator.pairing",
  "operator.admin",
]);

Observed result after the fix: Unknown node → UNAVAILABLE with details.nodeError preserved (no TypeError leakage). Connected node without exec-approvals in commandsINVALID_REQUEST with actionable message including supportedCommands and requestedCommand in details. Pre-paired capable node → connects successfully, listed in node.list. Pairing authorization → requires operator.admin for exec-approvals commands. Local exec.approvals.get → no regression. All 74 unit tests + 12 real-gateway integration tests pass.

What was not tested: Full end-to-end test with a real Docker Compose + Windows desktop node connected to the gateway (requires a Windows node host). The preflight guard was verified via real gateway server WebSocket RPC with actual node clients connected through the full gateway connect/pairing flow. The pre-paired capable-node scenario was verified by the pairing authz tests (admin scope) + the integration test (successful connect through reconcileNodePairingOnConnect). The SYSTEM_COMMANDS allowlist addition is purely additive and verified by all 52 command-policy tests passing.

Tests and validation

Unit tests (74/74 pass):

$ node scripts/run-vitest.mjs src/gateway/server-methods/exec-approvals.test.ts
 Test Files  2 passed (2)     Tests  10 passed (10)

$ node scripts/run-vitest.mjs src/gateway/node-command-policy.test.ts
 Test Files  4 passed (4)     Tests  52 passed (52)

$ node scripts/run-vitest.mjs src/infra/node-pairing-authz.test.ts
 Test Files  1 passed (1)     Tests  4 passed (4)

Real gateway integration tests (12/12 pass, 6 unique tests × 2 Vitest shards):

$ node scripts/run-vitest.mjs src/gateway/server-methods/exec-approvals.integration.test.ts
 Test Files  2 passed (2)     Tests  12 passed (12)    Duration  18s

Integration test covers 6 scenarios (all verified through real gateway WebSocket RPC):

  1. Pre-paired capable node connects and appears in node.list (pairing + effective commands)
  2. Unknown node → exec.approvals.node.get → UNAVAILABLE with nodeError
  3. Unknown node → exec.approvals.node.set → UNAVAILABLE with nodeError
  4. Connected node (limited commands) → exec.approvals.node.get → INVALID_REQUEST
  5. Connected node (limited commands) → exec.approvals.node.set → INVALID_REQUEST
  6. Local exec.approvals.get → OK (no regression)

Risk checklist

  • Low risk — the allowlist additions (SYSTEM_COMMANDS + DESKTOP_HOST_COMMANDS) are purely additive (desktop nodes gain exec-approval command visibility in pairing). The preflight guard only affects the exec-approvals relay path. Unknown nodes retain the existing invoke error mapping. The pairing authz change adds admin scope for exec-approvals, matching the existing system.run admin requirement.
    • Risk level: low
    • Mitigation: all 52 command-policy tests pass; all 4 pairing-authz tests pass; all 12 real-gateway integration tests pass.
  • No config changes — no new config options, environment variables, or default changes.
  • Small scopenode-command-policy.ts (allowlist addition), exec-approvals.ts (preflight), node-pairing-authz.ts (admin scope), tests.
  • Low merge risk — verified that commands-based guard with pairing allowlist fix + admin authorization + reconnect preservation correctly allows capable desktop nodes while blocking unauthorized ones.

Current review state

Updated to address ClawSweeper round-2 findings:

  1. Added admin-scope pairing authorization for exec-approval commands in resolveNodePairApprovalScopes (P1).
  2. Fixed typed error.details access by adding details?: unknown to the rpcReq helper response type (P1).
  3. Added pre-paired capable-node integration evidence: approveNodePairing with operator.admin scope + node connect through reconcileNodePairingOnConnect + node.list verification. Pairing authz tests independently verify admin scope requirement (P1).

Previous (round-1) fixes:

  1. Added system.execApprovals.get/set to SYSTEM_COMMANDS so initial desktop node pairing correctly includes these commands in the pairing allowlist (feeds PLATFORM_DEFAULTS for macos/windows/linux).
  2. Added system.execApprovals.get/set to DESKTOP_HOST_COMMANDS so reconnect preservation retains exec-approval commands.
  3. Preflight uses commands (approved surface, security boundary) instead of declaredCommands.
  4. Unknown nodes fall through to nodeRegistry.invoke to preserve details.nodeError error mapping.
  5. Replaced scripts/live-verify-97578.ts with 12 real-gateway integration tests providing WebSocket JSON evidence.
  6. Enhanced integration test connects two real node clients through the actual gateway connect/pairing flow.

@openclaw-barnacle openclaw-barnacle Bot added gateway Gateway runtime size: S labels Jun 29, 2026
@clawsweeper

clawsweeper Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed July 5, 2026, 1:18 PM ET / 17:18 UTC.

Summary
The PR adds an approved-command preflight for exec.approvals.node get/set, extends desktop exec-approval command allowlists and admin pairing scopes, updates operator-scope docs, and adds gateway regression/integration tests.

PR surface: Source +67, Tests +575, Docs 0. Total +642 across 8 files.

Reproducibility: no. live current-main reproduction was run in this read-only review. The source path is clear: current main relays exec.approvals.node.get/set without command-surface preflight while node hosts may lack those effective commands.

Review metrics: 2 noteworthy metrics.

  • Desktop command boundary: 2 added. system.execApprovals.get/set are added to desktop command preservation and platform pairing defaults, which affects which node capabilities can become visible after approval.
  • Pairing authorization: 2 newly admin-scoped. system.execApprovals.get/set move into the operator.admin pairing bucket, so upgrades may require stronger operator approval before use.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #97578
Summary: This PR is a candidate fix for the canonical unsupported exec-approval capability error; several open PRs overlap, and one maintainer draft owns the adjacent command-advertisement gap.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🦞 diamond lobster
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • none.

Risk before merge

  • [P1] The PR intentionally changes the desktop node command boundary: system.execApprovals.get/set can now survive pairing/reconnect policy for declared capable desktop nodes.
  • [P1] Existing capable nodes whose exec-approval commands were not previously approved may need an operator.admin pairing approval before exec.approvals.node.* works through the new preflight.

Maintainer options:

  1. Land Current Admin-Scoped Boundary (recommended)
    Accept that declared exec-approval node commands survive desktop pairing only after operator.admin approval, then merge once gateway/security ownership is satisfied.
  2. Split Boundary From Error Handling
    Keep the structured unsupported-capability preflight here and move command advertisement plus pairing authorization to a maintainer-owned follow-up.
  3. Pause Behind Existing Draft
    Keep this PR open but do not land it until the maintainer draft for the command-advertisement gap is resolved or superseded.

Next step before merge

  • [P2] Human owner review is needed because the remaining question is the command authorization and upgrade boundary, not a narrow automated repair.

Maintainer decision needed

  • Question: Should OpenClaw accept system.execApprovals.get/set as desktop node commands that survive pairing/reconnect only after operator.admin approval, so exec.approvals.node.* can preflight against the approved command surface?
  • Rationale: The patch is a plausible bug fix, but it changes which remote approval-editing commands can be advertised and what operator scope approves them; automation should not decide that permanent security and upgrade boundary alone.
  • Likely owner: vincentkoc — vincentkoc owns the open maintainer-labeled PR for the adjacent exec-approval command advertisement boundary and has recent history on this gateway policy surface.
  • Options:
    • Accept Admin-Scoped Boundary (recommended): Merge this shape after normal owner review, preserving the scoped exec.approvals.node.* path and operator.admin pairing for exec-approval commands.
    • Narrow To Preflight Only: Remove or defer the command-advertisement and pairing-scope changes, accepting that capable desktop nodes may still need a separate advertisement fix.
    • Defer To Maintainer Draft: Pause this PR behind fix(gateway): advertise exec approval node commands #88296 for the advertisement boundary, then rebase a smaller structured-error fix afterward.

Security
Cleared: The diff is security-sensitive, but I found no concrete supply-chain or security regression because raw node.invoke remains blocked and exec-approval commands become admin-scoped for pairing.

Review details

Best possible solution:

Accept or reject the admin-scoped exec-approval command boundary through gateway/security owner review; if accepted, land this scoped preflight while keeping raw node.invoke blocked and close the linked bug after merge.

Do we have a high-confidence way to reproduce the issue?

No live current-main reproduction was run in this read-only review. The source path is clear: current main relays exec.approvals.node.get/set without command-surface preflight while node hosts may lack those effective commands.

Is this the best way to solve the issue?

Yes, if maintainers accept the admin-scoped command boundary. This is the best current combined shape because it fixes the structured error at the scoped gateway method and preserves raw node.invoke rejection, while narrower competing PRs are incomplete or unmerged.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against e09aba147678.

Label changes

Label changes:

  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR body includes after-fix real Gateway WebSocket JSON responses for the relevant unknown-node, limited-node, capable-node, and local approval paths.
  • remove rating: 🦐 gold shrimp: Current PR rating is rating: 🐚 platinum hermit, so this older rating label is no longer current.
  • remove status: ⏳ waiting on author: Current PR status label is status: 👀 ready for maintainer look.

Label justifications:

  • P2: This is a normal-priority gateway/CLI bug fix with limited operator-workflow blast radius and no evidence of broad runtime outage.
  • merge-risk: 🚨 compatibility: The new preflight plus command-surface changes can make existing capable nodes require admin reapproval before node exec-approval management works.
  • merge-risk: 🚨 security-boundary: The PR changes advertisement and approval of remote exec-approval editing commands, so the scoped RPC and raw-invoke boundaries need maintainer review.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR body includes after-fix real Gateway WebSocket JSON responses for the relevant unknown-node, limited-node, capable-node, and local approval paths.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes after-fix real Gateway WebSocket JSON responses for the relevant unknown-node, limited-node, capable-node, and local approval paths.
Evidence reviewed

PR surface:

Source +67, Tests +575, Docs 0. Total +642 across 8 files.

View PR surface stats
Area Files Added Removed Net
Source 4 71 4 +67
Tests 3 577 2 +575
Docs 1 5 5 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 8 653 11 +642

What I checked:

  • AGENTS policy read: Root AGENTS.md plus scoped src/gateway/AGENTS.md, src/gateway/server-methods/AGENTS.md, and docs/AGENTS.md were read fully; gateway hot-path and docs formatting/security-boundary review guidance affected this review. (AGENTS.md:1, e09aba147678)
  • Current-main relay gap: Current main validates node exec-approval params and then invokes the node without checking the connected node command surface, matching the linked issue's reported missing preflight. (src/gateway/server-methods/exec-approvals.ts:115, e09aba147678)
  • Current-main command-policy gap: Current main omits NODE_EXEC_APPROVALS_COMMANDS from SYSTEM_COMMANDS and DESKTOP_HOST_COMMANDS, so declared exec-approval commands are not preserved through the existing desktop command policy. (src/gateway/node-command-policy.ts:55, e09aba147678)
  • Node host declaration: The headless node host already advertises NODE_EXEC_APPROVALS_COMMANDS, so the mismatch is in gateway policy/preflight handling rather than missing node-host command registration. (src/node-host/runner.ts:290, e09aba147678)
  • Scoped raw-invoke boundary: Raw node.invoke still rejects system.execApprovals.get/set and directs callers to exec.approvals.node.*, which is the security boundary this PR must preserve. (src/gateway/server-methods/nodes.ts:1319, e09aba147678)
  • Proposed preflight implementation: The potential merge result checks nodeSession.commands through resolveNodeCommandAllowlist/isNodeCommandAllowed before invoking, returns structured unsupported or policy-denied errors, and leaves unknown nodes to the existing invoke error mapping. (src/gateway/server-methods/exec-approvals.ts:116, ab422f968ca5)

Likely related people:

  • vincentkoc: Authored the open maintainer-labeled command-advertisement PR for the same exec-approval command boundary and appears in recent history carrying the gateway node command policy surface. (role: current fix owner and recent area contributor; confidence: high; commits: 486d285c24f1, e085fa1a3ffd; files: src/gateway/node-command-policy.ts, src/gateway/node-connect-reconcile.test.ts, src/gateway/server-methods/exec-approvals.ts)
  • steipete: Introduced and refactored the exec-approval tooling, shared node command catalog, and node pairing approval-scope helper that define this workflow. (role: feature-history contributor; confidence: high; commits: 3686bde783fd, d06632ba45a8, 01a24c20bfb7; files: src/gateway/server-methods/exec-approvals.ts, src/infra/node-commands.ts, src/infra/node-pairing-authz.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.
Review history (3 earlier review cycles)
  • reviewed 2026-07-05T14:23:48.589Z sha 25fa263 :: needs changes before merge. :: [P2] Fix the new integration test lint failure | [P2] Update docs for the new admin-scoped commands
  • reviewed 2026-07-05T15:19:31.972Z sha b5be92e :: needs changes before merge. :: [P2] Format the operator-scopes table
  • reviewed 2026-07-05T17:10:07.854Z sha 9191955 :: needs maintainer review before merge. :: none

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. labels Jun 29, 2026
@lzyyzznl
lzyyzznl force-pushed the fix/issue-97578 branch 4 times, most recently from 622aba5 to cb36f09 Compare June 29, 2026 11:03
@clawsweeper clawsweeper Bot added the merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. label Jun 29, 2026
@clawsweeper clawsweeper Bot removed the merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. label Jun 29, 2026
@openclaw-barnacle openclaw-barnacle Bot added scripts Repository scripts size: M and removed size: S labels Jun 29, 2026
@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. labels Jun 29, 2026
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels Jun 29, 2026
@lzyyzznl

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@openclaw-barnacle openclaw-barnacle Bot removed the scripts Repository scripts label Jun 30, 2026
@lzyyzznl

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. labels Jul 2, 2026
@lzyyzznl

lzyyzznl commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@lzyyzznl

lzyyzznl commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@lzyyzznl

lzyyzznl commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

1 similar comment
@lzyyzznl

lzyyzznl commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

lzyyzznl and others added 11 commits July 2, 2026 16:38
- Add system.execApprovals.get/set to SYSTEM_COMMANDS and DESKTOP_HOST_COMMANDS
  so desktop node pairing and reconnect preservation include exec-approval cmds
- Add capability preflight in respondWithExecApprovalsNodePayload that
  reuses resolveNodeCommandAllowlist + isNodeCommandAllowed for current
  allow/deny policy enforcement, matching the sibling node.invoke path
- Update resolveNodePairApprovalScopes to require operator.admin for
  NODE_EXEC_APPROVALS_COMMANDS
- Add details?: unknown to rpcReq test helper error type
- Add denyCommands regression tests proving gateway.nodes.denyCommands
  correctly blocks system.execApprovals.get/set
- Add real-gateway integration tests covering unknown-node, unsupported
  connected-node, pre-paired capable-node, and local regression scenarios
- Remove scripts/live-verify-97578.ts (replaced by integration tests)

Fixes openclaw#97578
The exec-approvals node preflight returned 'node does not support' for
every failed isNodeCommandAllowed reason, including gateway policy
denials (denyCommands/allowlist). Now distinguishes:

- 'command not allowlisted' → blocked by gateway node command policy
- other reasons → keep existing unsupported-capability message

Ref. openclaw#97729
When isNodeCommandAllowed returns 'command not allowlisted', check whether the node's declared commands include the requested command to decide the remediation: - command is declared by node but blocked by policy -> 'does not allow: blocked by gateway' - command not declared by node -> 'does not support'

Ref. openclaw#97729
…tion

The nodeSession.commands field holds the resolved/approved command surface
after allowlist and denyCommands filtering, so it cannot be used to
determine whether the node originally declared the command. Switch to
nodeSession.declaredCommands for the policy-denial check, which holds
the raw capabilities the node advertised on connect.

Ref. openclaw#97729
The code already requires operator.admin for NODE_EXEC_APPROVALS_COMMANDS; update the module docstring to reflect this.

Ref. openclaw#97729
…ands

Update operator-scopes.md to list exec-approval commands alongside system.run as requiring operator.pairing + operator.admin scope for node pairing approvals.

Ref. openclaw#97729
Remove the command-surface expansion (node-command-policy.ts) and
pairing-admin-scope change (node-pairing-authz.ts) that were bundled
with the original TypeError preflight fix.  These security-boundary
changes triggered P1 flags from ClawSweeper and caused the PR to block
on maintainer acceptance.

This commit reverts those policy changes, keeping only the core
exec-approvals preflight guard and its tests.

Ref. openclaw#97729
… nodes

After stripping the security-boundary expansion, the preflight allowlist
no longer included system.execApprovals.get/set for any node, causing
declared-capable desktop nodes to return INVALID_REQUEST.

Fix: add NODE_EXEC_APPROVALS_COMMANDS to DESKTOP_HOST_COMMANDS only
(not SYSTEM_COMMANDS).  This keeps exec-approvals out of the default
platform command surface while allowing live nodes that explicitly
declare these commands to pass the preflight check via
filterApprovedRuntimeCommands.

Also add focused integration tests that invoke exec.approvals.node.get
and .set against the capable node to prove the RPC succeeds.

Ref. openclaw#97729
…d capability

The preflight gate for exec.approvals.node.* used the runtime command
allowlist, which excludes DESKTOP_HOST_COMMANDS for desktop platforms.
A first-connect node that declares system.execApprovals.get/set would
have its declared commands filtered by the pairing allowlist, and the
preflight gate would reject the admin RPC.

Two complementary changes:

1. Add NODE_EXEC_APPROVALS_COMMANDS to PLATFORM_DEFAULTS for linux,
   macos, and windows so the pairing allowlist includes them; the
   runtime allowlist still filters them out via filterDesktopHost-
   CommandDefaults (DESKTOP_HOST_COMMANDS membership).

2. In the preflight gate: when isNodeCommandAllowed fails because the
   command is not in the allowlist but the node declared it, allow
   the admin RPC if the command is not in gateway.nodes.denyCommands.

Fixes P1/P2 findings from ClawSweeper review.
Ref. openclaw#97729
- Remove raw-declared fallthrough in exec-approvals preflight;
  require effective approved command surface before invoking
- Add NODE_EXEC_APPROVALS_COMMANDS to admin pairing scope in
  resolveNodePairApprovalScopes (operator.admin required)
- Update integration test: naive node expects rejection,
  pre-paired node responds to node.invoke.request
- Add unit test for exec-approvals admin pairing scope

Ref. openclaw#97729
lzyyzznl and others added 3 commits July 5, 2026 22:04
- Fix oxlint prefer-const error in integration test by replacing
  separate capableNodeClient variable with existing capableNode
- Update operator-scopes.md to list system.execApprovals.get/set
  alongside system.run etc as admin-scoped pairing commands

🦞 diamond lobster: minimal fix (2 files, -7 lines), docs sync

Ref. openclaw#97729
- Run node scripts/format-docs.mjs to fix table column alignment
- This resolves the check-docs CI failure (P2 finding)

🦞 diamond lobster: L2 evidence (docs formatter output)

Ref. openclaw#97729
@lzyyzznl

lzyyzznl commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Addressed P2: formatted docs/gateway/operator-scopes.md via node scripts/format-docs.mjs

@clawsweeper

clawsweeper Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@steipete

steipete commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Superseded by the narrower landed fix #100505 / fccb888.

This branch contains the same core policy and relay repair, but also carries 13 commits, a 348-line integration harness, pairing-scope/docs churn, and several reverted intermediate security-boundary designs. The landed replay keeps the canonical five-file implementation: command preservation, effective-surface preflight, live allowlist enforcement, and focused regressions.

Thank you @lzyyzznl for the extensive investigation and proof. The final implementation incorporates the important boundary findings while avoiding the broader branch churn.

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

Labels

docs Improvements or additions to documentation gateway Gateway runtime merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: L status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: OpenClaw CLI should handle node lacks exec approvals capability gracefully

3 participants