Skip to content

fix(gateway): require admin scope for browser proxy invoke#85916

Merged
eleqtrizit merged 4 commits into
openclaw:mainfrom
LaPhilosophie:browser-proxy-admin-scope
Jun 29, 2026
Merged

fix(gateway): require admin scope for browser proxy invoke#85916
eleqtrizit merged 4 commits into
openclaw:mainfrom
LaPhilosophie:browser-proxy-admin-scope

Conversation

@LaPhilosophie

@LaPhilosophie LaPhilosophie commented May 24, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Problem: browser.request is admin-scoped, but the equivalent node-host browser proxy command could also be reached through direct node.invoke(browser.proxy) from an operator.write caller.
  • Solution: require operator.admin before forwarding direct node.invoke requests for browser.proxy, while keeping the bundled Google Meet browser proxy path working through an explicit official-plugin runtime scope request.
  • What changed: direct write-scoped node.invoke(browser.proxy) is rejected before node dispatch; admin-scoped callers still dispatch; bundled/trusted official plugin runtime calls may request admin scope from their real plugin metadata; third-party plugin runtime callers cannot mint admin by passing scopes through either gateway or CLI runtime paths.
  • What did NOT change (scope boundary): browser.request behavior is unchanged; node-host browser proxy implementation is unchanged; persistent browser profile mutation blocking remains unchanged.

Change Type

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes: N/A
  • Related: N/A
  • This PR fixes a bug or regression

Motivation

The browser gateway surface already treats browser.request as admin-scoped. That route can reach a node-hosted browser through the same browser.proxy backend capability when a browser-capable node is connected.

Before this patch, a scoped operator token with only operator.write could not call browser.request, but could call the underlying browser.proxy command through direct node.invoke. That created an authorization mismatch between two routes to the same browser proxy capability.

This PR aligns the route semantics: the direct node invoke route for browser.proxy now requires the same admin scope as the browser gateway route. The Google Meet integration keeps its intended bundled-plugin path by requesting admin scope through the plugin runtime; both gateway and CLI runtime scope-forwarding paths honor that request only for bundled/trusted official plugin metadata, not for arbitrary third-party plugin runtime callers.

Real behavior proof (required for external PRs)

  • Behavior or issue addressed: browser.proxy through direct node.invoke now follows the same admin boundary as browser.request; bundled Google Meet can still use the browser proxy through official plugin runtime metadata; third-party plugin runtime callers cannot mint admin scope by passing scopes through gateway or CLI runtime paths.
  • Real environment tested: local macOS development checkout, branch browser-proxy-admin-scope, head 1ceea79bec.
  • Exact steps or command run after this patch:
    • node --import tsx .artifacts/browser-proxy-current-head-proof.ts
    • node scripts/run-vitest.mjs run --config test/vitest/vitest.bundled.config.ts src/plugins/cli-gateway-nodes-runtime.test.ts src/plugins/registry.runtime-config.test.ts
    • CI shard reproduction:
      OPENCLAW_VITEST_MAX_WORKERS=2 OPENCLAW_VITEST_SHARD_NAME=agentic-control-plane-http-plugin-ws OPENCLAW_TEST_PROJECTS_PARALLEL=2 OPENCLAW_VITEST_INCLUDE_FILE=<generated include json> NODE_OPTIONS=--max-old-space-size=8192 npm exec --yes pnpm@11.1.0 -- node scripts/test-projects.mjs test/vitest/vitest.gateway-server.config.ts
    • npm exec --yes pnpm@11.1.0 -- node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.core.test.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/core-test.tsbuildinfo
    • npm exec --yes pnpm@11.1.0 -- node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.extensions.test.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/extensions-test.tsbuildinfo
    • npm exec --yes pnpm@11.1.0 -- oxfmt --check src/plugins/runtime/gateway-request-scope.ts src/plugins/registry.ts src/gateway/server-plugins.ts src/gateway/server-plugins.test.ts
  • Evidence after fix:
[browser-proxy-proof] head=1ceea79bec06
[browser-proxy-proof] direct write-only ok=false error=missing scope: operator.admin nodeDispatch=0
[browser-proxy-proof] direct admin ok=true payloadJSON={"proof":"admin-direct","reachedNode":true,"path":"/tabs"} nodeDispatch=1
[browser-proxy-proof] google-meet runtime ok=true nodeDispatch=1 payloadJSON={"proof":"google-meet-runtime","reachedNode":true,"path":"/tabs"} message=n/a
[browser-proxy-proof] third-party runtime ok=false error=missing scope: operator.admin nodeDispatch=0
[browser-proxy-proof] completed

CI shard reproduction after the fix:

Test Files  18 passed (18)
     Tests  176 passed (176)
  Start at  11:47:13
  Duration  5.36s (transform 3.54s, setup 326ms, import 2.34s, tests 7.66s, environment 1ms)

[test] passed 1 Vitest shard in 7.60s

CLI runtime scope-forwarding regression coverage after the fix:

node scripts/run-vitest.mjs run --config test/vitest/vitest.bundled.config.ts src/plugins/cli-gateway-nodes-runtime.test.ts src/plugins/registry.runtime-config.test.ts
exit code: 0
  • Observed result after fix:
    • A write-only operator caller cannot dispatch direct node.invoke(browser.proxy).
    • An admin-scoped operator caller still dispatches browser.proxy to the node.
    • The bundled Google Meet runtime path can dispatch browser.proxy when it explicitly requests admin scope through official plugin metadata.
    • A third-party plugin runtime caller that requests operator.admin is still downgraded and rejected before node dispatch.
    • The CLI plugin runtime path now forwards requested scopes only for bundled/trusted official plugin metadata and drops requested scopes for third-party plugin metadata.
  • What was not tested: a remote multi-device browser deployment or a live Google Meet call. The proof exercises the Gateway node.invoke auth gate, plugin runtime owner metadata propagation, and node dispatch boundary locally; targeted regression tests cover the CLI runtime scope-forwarding guardrail.
  • Before evidence: the earlier regression test failed before the patch because write-scoped direct node.invoke(browser.proxy) reached node dispatch while browser.request was blocked by operator.admin.

Root Cause

  • Root cause: direct node.invoke enforced the generic operator.write scope for node commands, but did not apply the stricter operator.admin scope used by the equivalent browser.request gateway route.
  • Missing detection / guardrail: there was no regression test comparing the authorization semantics of browser.request and direct node.invoke for the same browser.proxy backend command, and no test proving official plugin runtime scope requests cannot be forged by third-party plugin runtime callers through gateway or CLI runtime paths.
  • Contributing context: existing code already blocked persistent browser profile mutations through direct node.invoke; the remaining non-persistent browser proxy paths still needed route-level admin scope alignment.

Regression Test Plan

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file:
    • src/gateway/server.node-invoke-approval-bypass.test.ts
    • src/gateway/server-methods/nodes.invoke-wake.test.ts
    • src/gateway/server-plugins.test.ts
    • src/plugins/cli-gateway-nodes-runtime.test.ts
    • src/plugins/registry.runtime-config.test.ts
    • extensions/google-meet/src/transports/chrome-browser-proxy.test.ts
    • extensions/browser/src/browser-tool.test.ts
  • Scenario the test locks in: write-scoped callers cannot bypass the admin-scoped browser.request route by directly invoking browser.proxy; admin-scoped callers still work; bundled/trusted official plugin runtime callers can request admin scope; third-party plugin runtime callers cannot mint admin scope through gateway or CLI runtime paths.
  • Why this is the smallest reliable guardrail: the mismatch is in Gateway node invocation authorization and plugin runtime dispatch metadata, so focused Gateway/runtime tests cover the policy boundary directly.
  • Existing test that already covers this: none before this PR.

User-visible / Behavior Changes

Direct node.invoke callers now need operator.admin to invoke browser.proxy. Admin-scoped callers and browser.request behavior are unchanged. Bundled Google Meet browser proxy usage remains supported through the plugin runtime path.

Diagram

Before:
operator.write -> browser.request -> blocked: operator.admin required
operator.write -> node.invoke(browser.proxy) -> node dispatch

After:
operator.write -> browser.request -> blocked: operator.admin required
operator.write -> node.invoke(browser.proxy) -> blocked: operator.admin required
operator.admin -> node.invoke(browser.proxy) -> node dispatch
bundled official plugin runtime -> request operator.admin -> node dispatch
third-party plugin runtime -> request operator.admin -> blocked: operator.admin required
third-party plugin CLI runtime -> request operator.admin -> scopes dropped before callGateway

Security Impact

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? Yes - this narrows direct browser proxy invocation to the same admin scope used by the browser gateway route.
  • Data access scope changed? No
  • If any Yes, explain risk + mitigation: this reduces the callable surface for scoped write-only operator tokens by preventing direct access to the node-hosted browser proxy route. The official plugin runtime compatibility path is metadata-bound to bundled/trusted official plugin records in both gateway and CLI runtime paths and does not let arbitrary plugin runtime callers mint admin scope.

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: local development checkout
  • Model/provider: N/A
  • Integration/channel: Browser extension, Google Meet extension, Gateway node invoke
  • Relevant config: local Gateway/runtime proof with a connected node command handler for browser.proxy

Steps

  1. Exercise direct node.invoke(browser.proxy) as a write-only operator caller.
  2. Exercise direct node.invoke(browser.proxy) as an admin operator caller.
  3. Exercise bundled Google Meet plugin runtime nodes.invoke with scopes: ["operator.admin"].
  4. Exercise third-party plugin runtime nodes.invoke with the same requested admin scope.
  5. Exercise CLI plugin runtime scope forwarding for bundled and third-party plugin scopes.
  6. Re-run the CI gateway server shard that previously failed on plugin runtime scope propagation.

Expected

  • Write-only direct callers are rejected before node dispatch.
  • Admin direct callers dispatch to the node.
  • Bundled/trusted official plugin runtime callers can request admin scope for this path.
  • Third-party plugin runtime callers cannot mint admin scope.
  • CLI plugin runtime callers follow the same metadata gate before passing scopes into callGateway.

Actual

  • Matches expected in the local proof, targeted CLI runtime tests, and targeted CI shard reproduction.

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers

Human Verification

  • Verified scenarios: write-only direct browser.proxy rejection before node dispatch; admin direct browser.proxy dispatch; bundled Google Meet runtime dispatch with requested admin scope; third-party runtime rejection despite requested admin scope; bundled CLI runtime forwards requested scope; third-party CLI runtime drops requested scope; CI shard agentic-control-plane-http-plugin-ws passes locally after the scope metadata fix.
  • Edge cases checked: connected node declares browser.proxy; command is allowlisted so rejection is specifically the scope check; plugin runtime metadata survives through the real registry proxy; stale global registry state does not decide official-plugin scope by itself; CLI runtime does not forward requested scopes without bundled/trusted official plugin metadata.
  • What was not verified: remote multi-device browser deployment and a live Google Meet call.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? Mostly
  • Config/env changes? No
  • Migration needed? No
  • If yes, exact upgrade steps: N/A
  • Notes: callers that directly invoke browser.proxy through node.invoke now need operator.admin, matching the existing browser.request scope.

Risks and Mitigations

  • Risk: a non-admin integration relying on direct node.invoke for browser.proxy will now be rejected.
    • Mitigation: use an admin-scoped operator token for browser proxy control, or call the existing admin-scoped browser.request route.

Copilot AI review requested due to automatic review settings May 24, 2026 03:40
@openclaw-barnacle openclaw-barnacle Bot added gateway Gateway runtime size: S proof: supplied External PR includes structured after-fix real behavior proof. labels May 24, 2026
@clawsweeper

clawsweeper Bot commented May 24, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed June 29, 2026, 7:14 PM ET / 23:14 UTC.

Summary
The branch adds an admin-scope gate for direct node.invoke(browser.proxy), updates bundled Browser and Google Meet callers to request that scope, and documents/tests the trusted-only runtime scope contract.

PR surface: Source +139, Tests +351, Docs +4. Total +494 across 16 files.

Reproducibility: yes. Source inspection shows current main has browser.request as admin-scoped while direct non-mutating node.invoke(browser.proxy) lacks the same admin gate; the PR body also includes before/after terminal proof, though I did not rerun it in this read-only review.

Review metrics: 2 noteworthy metrics.

  • Gateway authorization gates: 1 command-specific gate added. The PR changes a shipped direct node invocation path from write-scoped to admin-scoped for browser.proxy, so maintainers should review upgrade impact separately from test success.
  • Plugin runtime API params: 1 optional param added. RuntimeNodeInvokeParams.scopes is a new plugin runtime type surface whose trusted-only semantics must be clear before merge.

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:

  • [P2] Let exact-head GitHub CI finish successfully before merge.
  • Confirm the trusted-only runtime scope contract remains the intended maintainer direction.

Risk before merge

  • [P1] Existing write-scoped direct node.invoke(browser.proxy) integrations can fail with missing scope: operator.admin after upgrade; the discussion accepts that as hardening, but it remains a compatibility break to own.
  • [P1] RuntimeNodeInvokeParams.scopes becomes a public plugin runtime signal while privileged behavior is honored only for bundled/trusted official plugins, so this merge establishes a security-sensitive contract even though it is now documented and tested.

Maintainer options:

  1. Land with accepted hardening (recommended)
    After exact-head CI is green, maintainers can merge while explicitly owning that write-scoped direct browser proxy callers must move to admin-scoped access or browser.request.
  2. Add a compatibility window
    If maintainers do not want the upgrade break yet, add a temporary compatibility path or migration guidance before enforcing the admin-only direct route.
  3. Pause the runtime scope API
    If trusted admin scope requests should not be exposed through the plugin runtime type, pause this PR and replace that part with an internal-only bundled-caller path.

Next step before merge

  • No automated repair is indicated; maintainers should wait for exact-head CI and make the final compatibility/security-boundary merge call.

Security
Cleared: No concrete supply-chain, secret-handling, or auth-bypass regression was found; the security-sensitive authorization change is covered by focused tests and remains surfaced as merge risk.

Review details

Best possible solution:

Land the refreshed branch only after exact-head CI is green and maintainers intentionally own the stricter browser proxy boundary plus the documented trusted-only runtime scope contract.

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

Yes. Source inspection shows current main has browser.request as admin-scoped while direct non-mutating node.invoke(browser.proxy) lacks the same admin gate; the PR body also includes before/after terminal proof, though I did not rerun it in this read-only review.

Is this the best way to solve the issue?

Yes, with an explicit maintainer acceptance requirement. The gate belongs at the Gateway node.invoke enforcement point and the PR updates first-party trusted callers, while the documented runtime scope field is the remaining compatibility/security contract maintainers must own.

AGENTS.md: found and applied where relevant.

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

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 (terminal): The PR body includes terminal proof for write-only rejection, admin success, trusted bundled runtime success, and third-party runtime non-elevation, plus maintainer takeover validation on the refreshed head.
  • 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:

  • P1: The PR addresses a security-sensitive gateway authorization mismatch and changes upgrade behavior for browser proxy callers.
  • merge-risk: 🚨 compatibility: Merging can make existing write-scoped direct node.invoke(browser.proxy) callers fail unless they use admin scope or the admin-scoped browser route.
  • merge-risk: 🚨 security-boundary: The diff changes the browser proxy authorization boundary and adds a trusted-only path for runtime-requested operator scopes.
  • 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 (terminal): The PR body includes terminal proof for write-only rejection, admin success, trusted bundled runtime success, and third-party runtime non-elevation, plus maintainer takeover validation on the refreshed head.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes terminal proof for write-only rejection, admin success, trusted bundled runtime success, and third-party runtime non-elevation, plus maintainer takeover validation on the refreshed head.
Evidence reviewed

PR surface:

Source +139, Tests +351, Docs +4. Total +494 across 16 files.

View PR surface stats
Area Files Added Removed Net
Source 8 149 10 +139
Tests 7 355 4 +351
Docs 1 4 0 +4
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 16 508 14 +494

What I checked:

  • Current main authorization mismatch: Current main registers browser.request as operator.admin, while the direct node.invoke path only blocks persistent browser profile mutations and does not require admin before forwarding non-persistent browser.proxy calls. (extensions/browser/index.test.ts:255, 5dae3d49e623)
  • PR admin gate: The PR head adds clientHasOperatorAdminScope and rejects browser.proxy direct node invokes with missing scope: operator.admin before node dispatch. (src/gateway/server-methods/nodes.ts:1331, a62d0fd084f8)
  • First-party caller compatibility: The PR head updates the Browser tool and Google Meet browser proxy transport to request operator.admin when they invoke browser.proxy. (extensions/browser/src/browser-tool.ts:361, a62d0fd084f8)
  • Trusted-only runtime contract: The runtime type now exposes scopes?: OperatorScope[], and the docs warning states scope requests are honored only for bundled or trusted official plugin installations. (src/plugins/runtime/types.ts:78, a62d0fd084f8)
  • Regression coverage: The PR head adds tests for write-scoped rejection, admin-scoped success, trusted official runtime elevation, third-party runtime non-elevation, and CLI runtime scope dropping. (src/gateway/server.node-invoke-approval-bypass.test.ts:586, a62d0fd084f8)
  • Maintainer takeover context: A maintainer-side takeover comment accepted the direct browser.proxy admin requirement, documented the trusted-only runtime scope contract, refreshed the branch, and reported focused Gateway, Browser, Google Meet, plugin-runtime, type, build, lint, docs, and import-boundary validation. (a62d0fd084f8)

Likely related people:

  • Rain: Current-main blame for the central node invoke handler, gateway nodes runtime, Browser proxy caller, and Google Meet proxy caller points to the same recent commit; older provenance is limited by the shallow/squashed checked-out history. (role: recent area contributor; confidence: medium; commits: 0635fa5ecaab; files: src/gateway/server-methods/nodes.ts, src/gateway/server-plugins.ts, extensions/browser/src/browser-tool.ts)
  • eleqtrizit: Assigned themselves to this PR, accepted the intentional browser proxy admin boundary, and authored the final takeover commits that document and tighten the trusted-only runtime scope contract. (role: recent PR takeover contributor; confidence: high; commits: 98af90b5271b, 8f24cedc8a11, a62d0fd084f8; files: docs/plugins/sdk-runtime.md, src/plugins/runtime/types.ts, extensions/browser/src/browser-tool.test.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.

Copilot AI 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.

Pull request overview

Closes an authorization-mismatch gap where direct node.invoke(browser.proxy) could be reached by operator.write callers even though the equivalent browser.request gateway method is operator.admin. The fix adds an explicit admin-scope check in the node.invoke handler for command === "browser.proxy" and locks it in with two new Gateway-level tests.

Changes:

  • Add BROWSER_PROXY_REQUIRED_SCOPE admin gate inside the node.invoke handler, just after the persistent-profile-mutation guard.
  • Add a local clientHasOperatorAdminScope helper that reads scopes from client.connect.scopes.
  • Extend server.node-invoke-approval-bypass.test.ts with a connectDeviceTokenOperator helper and tests for both write-rejected and admin-allowed direct browser.proxy flows; also adds browser.proxy to the test gateway allowlist.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/gateway/server-methods/nodes.ts Adds admin-scope guard for node.invoke(browser.proxy) and a local scope helper.
src/gateway/server.node-invoke-approval-bypass.test.ts Adds scoped device-token helper and write/admin scope tests for direct browser.proxy invocation.

Comment on lines +1105 to +1112
if (command === "browser.proxy" && !clientHasOperatorAdminScope(client)) {
respond(
false,
undefined,
errorShape(ErrorCodes.INVALID_REQUEST, `missing scope: ${BROWSER_PROXY_REQUIRED_SCOPE}`),
);
return;
}
Comment on lines +152 to +155
function clientHasOperatorAdminScope(client: GatewayClient | null): boolean {
const scopes = Array.isArray(client?.connect?.scopes) ? client.connect.scopes : [];
return scopes.includes(BROWSER_PROXY_REQUIRED_SCOPE);
}
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels May 24, 2026
@clawsweeper

clawsweeper Bot commented May 24, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper PR egg

🔥 Warming up: real-behavior proof passed; findings, security review, or rank-up moves are still in progress.

Hatch command

Comment @clawsweeper hatch when this PR is hatchable.

Hatchability rules:

  • Merged PRs are hatchable.
  • Open PRs are hatchable when they are status: 👀 ready for maintainer look, status: 🚀 automerge armed, or labeled clawsweeper:automerge.
  • Closed unmerged PRs are hatchable only when one of those hatchable labels is still present in the durable record.
What is this egg doing here?
  • Eggs appear after the PR passes real-behavior proof. It is here for vibes, not verdicts: it does not change labels, ratings, merge decisions, or automation.
  • The shell reacts to review momentum: open follow-up work warms it up, re-review makes it wobble, and a clean final review lets it hatch.
  • Hatchability usually comes from sufficient real-behavior proof, no blocking P0/P1/P2 findings, no security attention needed, and clean correctness. A merged PR is already final, so merge makes the egg hatchable independently.
  • The hatch is seeded from this repository and PR number, so the same PR keeps the same creature; the reviewed head SHA can only change safe visual details.
  • Rarity is just collectible sparkle: 🥚 common, 🌱 uncommon, 💎 rare, ✨ glimmer, and 🌈 legendary.

@LaPhilosophie

Copy link
Copy Markdown
Contributor Author

Follow-up for the ClawSweeper P1 compatibility finding.

I kept the new external direct-invoke boundary intact and updated the first-party browser proxy paths to use explicit admin-scoped dispatch:

  • extensions/browser/src/browser-tool.ts now passes scopes: ["operator.admin"] when it calls node.invoke for browser.proxy.
  • extensions/google-meet/src/transports/chrome-browser-proxy.ts now requests operator.admin through runtime.nodes.invoke.
  • PluginRuntime["nodes"].invoke can carry explicit requested scopes through the in-process Gateway dispatch path, so official plugin/runtime callers can keep working without weakening the direct node.invoke(browser.proxy) guard.

Local verification after the follow-up patch:

npm exec --yes pnpm@11.1.0 -- node scripts/run-vitest.mjs run --config test/vitest/vitest.gateway.config.ts src/gateway/server.node-invoke-approval-bypass.test.ts --reporter verbose
npm exec --yes pnpm@11.1.0 -- node scripts/run-vitest.mjs run --config test/vitest/vitest.extension-browser.config.ts extensions/browser/src/browser-tool.test.ts --reporter verbose
npm exec --yes pnpm@11.1.0 -- node scripts/run-vitest.mjs run --config test/vitest/vitest.extensions.config.ts extensions/google-meet/src/transports/chrome-browser-proxy.test.ts --reporter verbose
npm exec --yes pnpm@11.1.0 -- node scripts/run-vitest.mjs run --config test/vitest/vitest.gateway.config.ts src/gateway/server-plugins.test.ts -t "nodes runtime" --reporter verbose
npm exec --yes pnpm@11.1.0 -- oxfmt --check src/gateway/server-methods/nodes.ts src/gateway/server.node-invoke-approval-bypass.test.ts src/gateway/server-plugins.ts src/gateway/server-plugins.test.ts src/plugins/runtime/types.ts src/plugins/cli-gateway-nodes-runtime.ts extensions/browser/src/browser-tool.ts extensions/browser/src/browser-tool.test.ts extensions/google-meet/src/transports/chrome-browser-proxy.ts extensions/google-meet/src/transports/chrome-browser-proxy.test.ts
npm exec --yes pnpm@11.1.0 -- node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.core.test.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/core-test-browser-proxy-followup.tsbuildinfo
npm exec --yes pnpm@11.1.0 -- node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.extensions.test.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/extensions-test.tsbuildinfo

Observed locally:

src/gateway/server.node-invoke-approval-bypass.test.ts: 1 file passed, 7 tests passed
extensions/browser/src/browser-tool.test.ts: 1 file passed, 51 tests passed
extensions/google-meet/src/transports/chrome-browser-proxy.test.ts: 1 file passed, 1 test passed
src/gateway/server-plugins.test.ts -t "nodes runtime": 1 file passed, 1 test passed, 45 skipped
oxfmt --check: all matched files use the correct format
core-test tsgo: exit 0, no diagnostics
extensions-test tsgo: exit 0, no diagnostics

This should address both rank-up items: official browser proxy callers now request an admin-scoped route, and there is focused regression coverage for the browser tool, Google Meet transport, and plugin runtime scope handoff.

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented May 24, 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.

Re-review progress:

@clawsweeper clawsweeper Bot added P1 High-priority user-facing bug, regression, or broken workflow. 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. labels May 24, 2026
@martingarramon

Copy link
Copy Markdown
Contributor

Clean, minimal scope fix. The gap is real: browser.request registers as operator.admin on the gateway, but the same browser.proxy path was reachable via node.invoke which only requires operator.write — so a downgraded-scope caller could reach proxy dispatch by going around the gateway method.

The fix is correctly layered. clientHasOperatorAdminScope mirrors the existing scope-check idiom at nodes.ts:753 (Array.isArray(client?.connect?.scopes) ? client.connect.scopes : []) — consistent with the file's established pattern and fails closed (null client → empty array → check fails). The new admin check fires before the existing isForbiddenBrowserProxyMutation check, and since it returns early on failure, the mutation guard still runs for admin-scoped callers — so persistent profile mutation protection is unchanged, as stated.

The { scopes: ["operator.admin"] } additions in browser-tool.ts and chrome-browser-proxy.ts correctly set admin scope on outbound node-invoke calls, and the updated tests assert extra?.scopes is set. CI green.

@BingqingLyu

This comment was marked as spam.

@RomneyDa

Copy link
Copy Markdown
Member

Heads up: this PR needs to be updated against current main before the new required Dependency Guard check can pass.

@LaPhilosophie
LaPhilosophie force-pushed the browser-proxy-admin-scope branch from 1d16d0d to d0e4e78 Compare May 29, 2026 08:15
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 29, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 29, 2026
@LaPhilosophie
LaPhilosophie force-pushed the browser-proxy-admin-scope branch from d0e4e78 to faa29c9 Compare May 29, 2026 15:42
@clawsweeper clawsweeper Bot removed proof: sufficient ClawSweeper judged the real behavior proof convincing. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels May 29, 2026
@clawsweeper clawsweeper Bot added rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 8, 2026
@eleqtrizit eleqtrizit self-assigned this Jun 29, 2026
@eleqtrizit

Copy link
Copy Markdown
Contributor

@LaPhilosophie Thanks for the work here. I’m taking maintainer ownership to finish this security hardening.

The earlier Dependency Guard warning is no longer the blocker; that check now passes. The branch conflicts with current main, and the remaining maintainer decision is the trusted-only runtime scope contract.

I accept the intentional behavior change that direct node.invoke(browser.proxy) requires operator.admin. I’ll update this branch against current main, resolve the conflict, tighten the runtime scope contract, and rerun the focused Gateway, Browser, Google Meet, and plugin-runtime validation. Since maintainer edits are enabled, I’ll push those updates directly to this PR while preserving your attribution.

@eleqtrizit
eleqtrizit force-pushed the browser-proxy-admin-scope branch from 1ceea79 to a62d0fd Compare June 29, 2026 23:08
@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation plugin: file-transfer labels Jun 29, 2026
@eleqtrizit

Copy link
Copy Markdown
Contributor

Maintainer takeover update: refreshed onto current main and force-updated the contributor branch with lease protection. The PR is now mergeable at a62d0fd084f8909aa5f1f2a30b5a6074103ddc14.

Changes made during takeover:

  • Resolved the current-main conflict in src/plugins/cli-gateway-nodes-runtime.ts.
  • Constrained RuntimeNodeInvokeParams.scopes to the closed OperatorScope union.
  • Documented that requested runtime scopes are honored only for bundled or trusted official plugin installations.
  • Updated the stale Browser Gateway helper assertion for the explicit admin-scope argument.
  • Fixed the new Gateway test helper's lint violation.

Validation:

  • node scripts/run-vitest.mjs src/gateway/server.node-invoke-approval-bypass.test.ts src/gateway/server-methods/nodes.invoke-wake.test.ts src/gateway/server-plugins.test.ts src/plugins/cli-gateway-nodes-runtime.test.ts src/plugins/registry.runtime-config.test.ts extensions/browser/src/browser-tool.test.ts extensions/google-meet/src/transports/chrome-browser-proxy.test.ts
    • Gateway: 148 passed
    • Plugin runtime: 8 passed
    • Browser: 71 passed
    • Google Meet: 2 passed
  • Focused Gateway rerun after lint fix: 14 passed
  • Four direct tsgo lanes: core, extensions, core tests, extension tests — clean
  • node scripts/build-all.mjs — clean, including Plugin SDK declaration/export checks
  • Targeted scripts/run-oxlint.mjs across every changed TypeScript file — clean
  • Docs format, MDX, links, and i18n glossary checks — clean
  • All three extension/SDK import-boundary inventories — clean
  • git diff --check — clean
  • Codex autoreview against current main — no findings; patch correct, confidence 0.89

Remote Testbox proof could not be started from this checkout because neither the Crabbox nor Blacksmith CLI is installed; the wrapper failed before allocation. Fresh GitHub CI is now running on the exact pushed SHA and will provide the remote gates.

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 29, 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: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Jun 29, 2026
@eleqtrizit

Copy link
Copy Markdown
Contributor

Exact-head verification is complete for a62d0fd084f8909aa5f1f2a30b5a6074103ddc14.

  • Full PR CI completed successfully: https://github.com/openclaw/openclaw/actions/runs/28408659934
  • No failed or pending jobs remain on that run.
  • Security High, plugin-trust, Gateway/plugin quality, build artifacts, lint, production/test types, docs, boundaries, contracts, QA Smoke, and the full selected Node matrix are green.
  • Fresh ClawSweeper review has no contributor-facing blocker, marks the PR status: 👀 ready for maintainer look, and recommends landing with the accepted hardening after exact-head CI—which is now satisfied.

The PR is mergeable and clean. The remaining action is the final maintainer merge decision; no further code repair is indicated.

@eleqtrizit
eleqtrizit merged commit 456c48f into openclaw:main Jun 29, 2026
128 of 136 checks passed
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jun 30, 2026
…85916)

* fix(gateway): require admin scope for browser proxy invoke

* fix(gateway): document trusted node scopes

* fix(gateway): align browser scope test

* fix(gateway): satisfy node test lint

---------

Co-authored-by: Agustin Rivera <agustin@rivera-web.com>
QiuYuang pushed a commit to QiuYuang/openclaw that referenced this pull request Jul 1, 2026
…85916)

* fix(gateway): require admin scope for browser proxy invoke

* fix(gateway): document trusted node scopes

* fix(gateway): align browser scope test

* fix(gateway): satisfy node test lint

---------

Co-authored-by: Agustin Rivera <agustin@rivera-web.com>
chenyangjun-xy pushed a commit to chenyangjun-xy/openclaw that referenced this pull request Jul 1, 2026
…85916)

* fix(gateway): require admin scope for browser proxy invoke

* fix(gateway): document trusted node scopes

* fix(gateway): align browser scope test

* fix(gateway): satisfy node test lint

---------

Co-authored-by: Agustin Rivera <agustin@rivera-web.com>
chenyangjun-xy pushed a commit to chenyangjun-xy/openclaw that referenced this pull request Jul 3, 2026
…85916)

* fix(gateway): require admin scope for browser proxy invoke

* fix(gateway): document trusted node scopes

* fix(gateway): align browser scope test

* fix(gateway): satisfy node test lint

---------

Co-authored-by: Agustin Rivera <agustin@rivera-web.com>
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. P1 High-priority user-facing bug, regression, or broken workflow. plugin: file-transfer plugin: google-meet proof: sufficient ClawSweeper judged the real behavior proof convincing. proof: supplied External PR includes structured after-fix real behavior proof. 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.

6 participants