Skip to content

Fix canvas WebView2 surface auth via plugin-node cap URL#588

Closed
christineyan4 wants to merge 2 commits into
openclaw:mainfrom
christineyan4:canvas-bug
Closed

Fix canvas WebView2 surface auth via plugin-node cap URL#588
christineyan4 wants to merge 2 commits into
openclaw:mainfrom
christineyan4:canvas-bug

Conversation

@christineyan4

Copy link
Copy Markdown
Contributor

Problem

The Windows tray's Canvas WebView2 hits 401 Unauthorized when navigating to /__openclaw__/canvas/* paths because it builds URLs against the gateway origin only, omitting the plugin-node capability token the gateway requires for cross-node plugin surface routes.

Fix

The gateway already advertises a cap-scoped surface URL in its hello-ok response under pluginSurfaceUrls.canvas (e.g. http://<gateway>/__openclaw__/cap/<token>). This PR plumbs that URL through to CanvasWindow and uses it as the base for /__openclaw__/canvas/* rewrites.

Additionally:

  • Treats the cap URL as a trusted origin prefix in IsUrlSafe so a cap URL whose host doesn't match _trustedGatewayOrigin (e.g. 127.0.0.1 cap vs localhost origin) isn't rejected by the safety check.
  • Clears the cached cap URL when a subsequent hello-ok/health update omits the canvas key, so a rotated/revoked token doesn't leave the node using a stale URL.

Files changed

File Change
src/OpenClaw.Shared/Models.cs Parse pluginSurfaceUrls.canvas from hello-ok
src/OpenClaw.Shared/WindowsNodeClient.cs Expose CanvasSurfaceUrl; clear when canvas key absent
src/OpenClaw.Tray.WinUI/Services/NodeService.cs Push URL into CanvasWindow on connect + on update
src/OpenClaw.Tray.WinUI/Windows/CanvasWindow.xaml.cs Rewrite /__openclaw__/canvas/* via cap base; trust as IsUrlSafe prefix

Net: +128 / -14 across 4 files. No new files, no API changes outside the tray.

Testing

  • ✅ Existing tray test suite passes (859 tests)
  • ✅ Build clean on net10.0-windows10.0.22621.0 / win-arm64 (0 warnings, 0 errors)
  • ✅ Verified E2E: canvas.present with /__openclaw__/canvas/generative-art.html now renders the real content instead of the dashboard SPA shell / 401 page.

Known limitations (deferred)

  • TOCTOU race between canvas.present and the dispatcher-enqueued SetTrustedGatewayOrigin — theoretical, never observed.
  • Absolute-form canvas URLs (https://gw/__openclaw__/canvas/*) still go through the gateway origin path; gateway doesn't currently emit those.

The Windows tray's Canvas WebView2 was hitting 401 when navigating
to /__openclaw__/canvas/* paths because it built URLs against the
gateway origin only, omitting the plugin-node capability token the
gateway requires for cross-node plugin surface routes.

The gateway already advertises a cap-scoped surface URL in its
hello-ok response (pluginSurfaceUrls.canvas), e.g.
  http://<gateway>/__openclaw__/cap/<token>

This change plumbs that URL through to CanvasWindow and uses it as
the base for /__openclaw__/canvas/* rewrites, and as an additional
trusted origin prefix in IsUrlSafe so a cap URL whose host doesn't
match _trustedGatewayOrigin (e.g. 127.0.0.1 cap vs localhost origin)
is not rejected by the safety check.

Also clears the cached cap URL when a subsequent hello-ok/health
update omits the canvas key, so a rotated/revoked token doesn't
leave the node using a stale URL.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@clawsweeper

clawsweeper Bot commented May 29, 2026

Copy link
Copy Markdown

Codex review: needs real behavior proof before merge. Reviewed June 26, 2026, 11:57 AM ET / 15:57 UTC.

Summary
The PR plumbs pluginSurfaceUrls.canvas from gateway self-info into the Windows node client and CanvasWindow so /__openclaw__/canvas/* WebView2 navigations can use a cap-scoped surface URL.

Reproducibility: yes. from source, not live: current main rewrites relative canvas routes to the effective gateway origin and does not read pluginSurfaceUrls, which matches the reported 401 path when the gateway requires a plugin-node cap URL. The PR still needs inspectable real behavior proof for the after-fix WebView2 path.

Review metrics: 2 noteworthy metrics.

  • Changed production surface: 4 files, +151/-11. The diff crosses shared handshake parsing, node-client cache state, tray service propagation, and WebView URL safety.
  • Regression tests in PR: 0 test files changed. The auth-sensitive URL validation and revocation behavior lacks focused test coverage in the submitted diff.

Merge readiness
Overall: 🧂 unranked krab
Proof: 🧂 unranked krab
Patch quality: 🦪 silver shellfish
Result: blocked until real behavior proof is added.

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

Rank-up moves:

  • [P1] Add inspectable after-fix WebView2 proof with private endpoints, IPs, tokens, API keys, phone numbers, and other private details redacted.
  • [P1] Validate cap URLs before WebView trust and preserve/remit removal signals through the node client and open CanvasWindow.
  • [P1] Add focused regression tests for malformed cap URLs, empty/canvas-absent surface maps, cap removal, and cap-origin bridge messages.

Proof guidance:

  • [P1] Needs real behavior proof before merge: The PR body claims E2E verification, but no inspectable after-fix proof is attached; add a screenshot, recording, terminal output, live output, linked artifact, or redacted log to the PR body with private endpoints, IPs, tokens, API keys, phone numbers, and other private details removed, then ClawSweeper should re-review automatically or a maintainer can comment @clawsweeper re-review.

Mantis proof suggestion
A real WebView2 canvas smoke would materially help verify that the cap-scoped route renders content instead of the 401/dashboard fallback. A maintainer can ask Mantis to capture proof by posting this exact PR comment:

@openclaw-mantis visual task: verify Windows tray canvas.present for /__openclaw__/canvas/generative-art.html renders through the cap-scoped surface URL with diagnostics and private endpoints redacted.

Risk before merge

  • [P1] Merging as-is broadens WebView navigation trust to a gateway-advertised string before validating that it is an absolute HTTP(S) cap route for the active gateway or equivalent loopback endpoint.
  • [P1] Cap rotation or removal can leave WindowsNodeClient and an already-open CanvasWindow using and trusting stale cap URLs.
  • [P1] The PR body claims E2E verification, but maintainers still do not have inspectable after-fix proof for the real WebView2 canvas auth path.
  • [P1] When the cap URL origin differs from _trustedGatewayOrigin, the canvas may load while native bridge messages are rejected, leaving part of the WebView2 canvas behavior broken.

Maintainer options:

  1. Repair cap trust and revocation before merge (recommended)
    Validate the cap URL shape/origin/path, preserve explicit surface-map removals, clear open windows, cover the bridge-origin case, and add focused regression tests before this PR lands.
  2. Accept gateway-advertised WebView trust explicitly
    Maintainers could decide that hello-ok surface URLs are authoritative WebView trust inputs, but that should be a documented compatibility/security choice with tests for malformed and revoked values.
  3. Hold until real proof is attached
    If the contributor cannot provide redacted real WebView2 proof, keep this draft out of automation and revisit after the runtime path is demonstrable.

Next step before merge

  • [P1] Contributor-visible proof is missing, and the remaining blockers include security-sensitive trust and cap-revocation behavior that need maintainer review before any repair or merge automation should take over.

Security
Needs attention: The diff broadens WebView trust to a gateway-advertised capability URL without enough validation or revocation propagation.

Review findings

  • [P1] Validate cap URLs before trusting them — src/OpenClaw.Tray.WinUI/Windows/CanvasWindow.xaml.cs:166-168
  • [P1] Preserve explicit surface-map removals — src/OpenClaw.Shared/Models.cs:747-748
  • [P1] Clear cap removal in open canvas windows — src/OpenClaw.Tray.WinUI/Services/NodeService.cs:1018-1020
Review details

Best possible solution:

Keep the cap-scoped routing approach, but validate the advertised cap URL, propagate explicit removal through the client and any open CanvasWindow, include bridge-origin handling for validated cap URLs, add focused regression tests, and require redacted real WebView2 proof before merge.

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

Yes from source, not live: current main rewrites relative canvas routes to the effective gateway origin and does not read pluginSurfaceUrls, which matches the reported 401 path when the gateway requires a plugin-node cap URL. The PR still needs inspectable real behavior proof for the after-fix WebView2 path.

Is this the best way to solve the issue?

No for the current branch. The cap-scoped routing direction is plausible and narrow, but the implementation must validate the cap URL, fully clear revoked cap state, and keep cap-origin bridge behavior working before it is the best safe fix.

Full review comments:

  • [P1] Validate cap URLs before trusting them — src/OpenClaw.Tray.WinUI/Windows/CanvasWindow.xaml.cs:166-168
    canvasSurfaceUrl is copied straight into _canvasSurfaceBaseUrl, and IsUrlSafe accepts that prefix before the dangerous-scheme/private-network checks. Parse it first and only trust the expected absolute HTTP(S) cap route for the active gateway or an equivalent loopback endpoint.
    Confidence: 0.9
  • [P1] Preserve explicit surface-map removals — src/OpenClaw.Shared/Models.cs:747-748
    Dropping an empty pluginSurfaceUrls object makes {} indistinguishable from a field that was never sent, and Merge keeps the old map when the update is null. A revocation or rotation update can therefore leave WindowsNodeClient.CanvasSurfaceUrl stuck on the old cap URL.
    Confidence: 0.9
  • [P1] Clear cap removal in open canvas windows — src/OpenClaw.Tray.WinUI/Services/NodeService.cs:1018-1020
    This handler only refreshes an existing CanvasWindow when a non-empty canvas URL is present. If a later update removes the canvas surface, the already-open window keeps trusting and using the old cap URL, which is the stale-token case the PR says it fixes.
    Confidence: 0.92
  • [P2] Allow validated cap origins to use the bridge — src/OpenClaw.Tray.WinUI/Windows/CanvasWindow.xaml.cs:223-225
    The PR explicitly supports cap URLs whose host differs from _trustedGatewayOrigin, but after rewriting to _canvasSurfaceBaseUrl, IsTrustedBridgeSource still accepts only the gateway origin. A 127.0.0.1 cap URL with a localhost gateway can load content while rejecting native bridge messages such as fullscreen and SPA events.
    Confidence: 0.78

Overall correctness: patch is incorrect
Overall confidence: 0.9

AGENTS.md: found and applied where relevant.

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

Label changes

Label justifications:

  • P2: This is a bounded canvas auth fix with normal priority, but it has concrete merge blockers before landing.
  • merge-risk: 🚨 security-boundary: The diff changes the WebView URL safety boundary by trusting a cap URL string from the gateway handshake.
  • merge-risk: 🚨 auth-provider: The diff changes token-scoped canvas routing and can leave stale cap credentials in an open window after removal or rotation.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🦪 silver shellfish.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR body claims E2E verification, but no inspectable after-fix proof is attached; add a screenshot, recording, terminal output, live output, linked artifact, or redacted log to the PR body with private endpoints, IPs, tokens, API keys, phone numbers, and other private details removed, then ClawSweeper should re-review automatically or a maintainer can comment @clawsweeper re-review.
Evidence reviewed

Security concerns:

  • [medium] Unvalidated cap URL becomes a trusted WebView prefix — src/OpenClaw.Tray.WinUI/Windows/CanvasWindow.xaml.cs:166
    The gateway-provided string is stored as _canvasSurfaceBaseUrl and allowed by IsUrlSafe before dangerous URL checks, so it needs absolute HTTP(S), origin, and cap-path validation before trust.
    Confidence: 0.9
  • [medium] Revoked cap URLs can remain trusted — src/OpenClaw.Tray.WinUI/Services/NodeService.cs:1018
    Empty or missing surface-map updates are not represented and open CanvasWindow instances are updated only for new non-empty canvas URLs, so revoked capability URLs can remain trusted in the WebView.
    Confidence: 0.9

What I checked:

  • Repository policy applied: AGENTS.md was read fully; its connection, node, tray UX, credential, and validation guidance is relevant because the PR changes gateway self-info, node client state, and canvas WebView auth routing. (AGENTS.md:37, e70868d82bf0)
  • Architecture docs applied: Connection architecture docs make gateway connection lifecycle and credential precedence sensitive surfaces, so this cap URL and token-routing change needs compatibility and security review. (docs/CONNECTION_ARCHITECTURE.md:1, e70868d82bf0)
  • Current main lacks cap-surface parsing: GatewaySelfInfo.FromHelloOk on current main parses server, policy, and snapshot fields and returns without any pluginSurfaceUrls handling. (src/OpenClaw.Shared/Models.cs:702, e70868d82bf0)
  • Current main lacks cap URL cache: WindowsNodeClient.PublishGatewaySelf on current main only emits GatewaySelfUpdated; it has no CanvasSurfaceUrl cache or surface-map clearing path. (src/OpenClaw.Shared/WindowsNodeClient.cs:1230, e70868d82bf0)
  • Latest release lacks cap plumbing: Tag v0.6.3 has no matches for pluginSurfaceUrls, PluginSurfaceUrls, CanvasSurfaceUrl, or __openclaw__/cap in the touched source files. (85445c78066b)
  • Unvalidated cap URL becomes trusted: At PR head, canvasSurfaceUrl is assigned directly to _canvasSurfaceBaseUrl, which is then accepted as a trusted prefix by IsUrlSafe before the dangerous URL fallback runs. (src/OpenClaw.Tray.WinUI/Windows/CanvasWindow.xaml.cs:166, ce04fd7ec24f)

Likely related people:

  • Vincent Koc: Current-main blame and history show commit 6811d6a introduced the CanvasWindow, NodeService, WindowsNodeClient, Models, and CanvasCapability surfaces this PR extends. (role: feature introducer; confidence: high; commits: 6811d6a3e21b; files: src/OpenClaw.Tray.WinUI/Windows/CanvasWindow.xaml.cs, src/OpenClaw.Tray.WinUI/Services/NodeService.cs, src/OpenClaw.Shared/WindowsNodeClient.cs)
  • liorb-mountapps: Merged PR Fix canvas navigation URL handling #711 added the current canvas URL rewriter and tests immediately adjacent to this cap-surface routing change. (role: recent area contributor; confidence: high; commits: c338d4111dc5; files: src/OpenClaw.Tray.WinUI/Windows/CanvasWindow.xaml.cs, src/OpenClaw.Tray.WinUI/Helpers/CanvasGatewayUrlRewriter.cs, tests/OpenClaw.Tray.Tests/CanvasGatewayUrlRewriterTests.cs)
  • christineyan4: Separate from this PR authorship, git history shows prior merged Canvas/NodeService work including bringing an existing Canvas window to front and localization changes touching the relevant files. (role: prior canvas contributor; confidence: medium; commits: 82408b8d7fa0, 85445c78066b; files: src/OpenClaw.Tray.WinUI/Services/NodeService.cs, src/OpenClaw.Tray.WinUI/Windows/CanvasWindow.xaml.cs, src/OpenClaw.Shared/Models.cs)
  • Mike Harsh: Earlier connection lifecycle work unified node ownership under GatewayConnectionManager, which is relevant context for routing cap state through NodeService rather than parallel clients. (role: connection lifecycle contributor; confidence: medium; commits: 0816eb0eefc0, 389f499eb618; files: src/OpenClaw.Tray.WinUI/Services/NodeService.cs)
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.

@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 priority bug or improvement with limited blast radius. merge-risk: 🚨 auth-provider 🚨 Merging this PR could break OAuth, tokens, provider routing, model choice, or credentials. merge-risk: 🚨 security-boundary 🚨 Merging this PR could weaken sandboxing, authorization, credentials, or sensitive data. labels May 29, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 auth-provider 🚨 Merging this PR could break OAuth, tokens, provider routing, model choice, or credentials. merge-risk: 🚨 security-boundary 🚨 Merging this PR could weaken sandboxing, authorization, credentials, or sensitive data. P2 Normal priority bug or improvement with limited blast radius. 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.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant