Skip to content

fix(android): block loopback canvas navigation#99874

Merged
steipete merged 6 commits into
openclaw:mainfrom
ly85206559:codex/android-bug-scan
Jul 6, 2026
Merged

fix(android): block loopback canvas navigation#99874
steipete merged 6 commits into
openclaw:mainfrom
ly85206559:codex/android-bug-scan

Conversation

@ly85206559

@ly85206559 ly85206559 commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

Android Canvas navigation could reach loopback services on the device. The original guard covered gateway-driven canvas.navigate calls, but WebView user/JavaScript navigation, redirects, unspecified addresses, Unicode-normalized hosts, and non-GET main-frame requests left bypasses or ambiguous coverage.

Why This Change Was Made

The maintainer rewrite gives direct loads and WebView main-frame navigation one policy owner:

  • Blocks localhost and subdomains, unspecified addresses, IPv4/IPv6 loopback, IPv4-compatible/mapped IPv6, Chromium decimal/octal/hex/shorthand IPv4 forms, encoded equivalents, and malformed HTTP(S) hosts.
  • Rejects raw non-ASCII web hosts before Chromium's UTS Feature wish: custom model config #46 normalization, preventing fullwidth localhost or 127.0.0.1 bypasses while keeping ASCII punycode usable.
  • Applies the policy to gateway-driven loads plus user-, JavaScript-, and redirect-driven GET main-frame navigation.
  • Fails closed on non-GET main-frame requests before dispatch because WebView does not expose their redirect targets to shouldOverrideUrlLoading.
  • Preserves normal HTTPS, LAN/mDNS gateways, Android's 10.0.2.2 emulator host bridge, bundled app-owned Canvas URLs, blank input, and /.

This is intentionally a main-frame Canvas navigation boundary, not a claim that the WebView client is a complete subresource network firewall. No permission, config, protocol, dependency, or command surface is added.

User Impact

Blocked device-local main-frame targets return to the bundled Canvas instead of loading the target. Remote and gateway-host Canvas pages continue to work, including the Android emulator host bridge.

Evidence

Final head: dc58b11f7f148b86b188acbc5ff2e22caccd845e.

  • Sanitized public/no-IAM AWS Crabbox cbx_e9f25743d263, run run_a58919388153: exact code head 802a3cc39808c59af270d2da818d86bc1b30acbb passed both debug-flavor unit suites and assemblePlayDebug (78 tasks); a subsequent full ktlintFormat left both touched policy/test files unchanged. The final head differs only by syncing generated docs/docs_map.md and the hosted-gate verifier to current main after upstream drift.
  • Final-head hosted CI 28773495496 and Workflow Sanity 28773495483.
  • Five GPT-5.5 autoreview passes. Accepted findings closed POST/main-frame coverage, removed an overbroad subresource-firewall claim, and blocked Chromium IDNA/Unicode compatibility-host bypasses; final full-PR review was clean at 0.82 and the formatting-only closeout was clean at 0.98.
  • Contributor emulator proof demonstrates direct 127.0.0.1 rejection and normal remote Canvas retention: fix(android): block loopback canvas navigation #99874 (comment)

Source-blind exact-head validation is blocked because no interactive Android emulator/device with controlled redirect, non-GET, LAN/mDNS, and loopback fixtures was available. The final mechanism is covered by focused source-aware regression tests, exact-head Android build/unit proof, and deep review.

AI-assisted: yes. The contributor implementation was deeply reviewed and substantially hardened by a maintainer agent before exact-head validation.

@clawsweeper

clawsweeper Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Codex review: stale review; fresh review needed.

Summary
The latest durable ClawSweeper review was for head c0e39fd245843d231ecae2d981309c0771dde914, but the PR head is now dc58b11f7f148b86b188acbc5ff2e22caccd845e. Its old verdict and PR readiness labels are no longer current.

Next step
Run or wait for a fresh ClawSweeper review on the current PR head.

Review history (3 earlier review cycles)
  • reviewed 2026-07-05T12:15:18.042Z sha 6ccbaa8 :: needs real behavior proof before merge. :: none
  • reviewed 2026-07-05T16:06:56.087Z sha 6ccbaa8 :: needs maintainer review before merge. :: none
  • reviewed 2026-07-05T16:13:54.579Z sha 6ccbaa8 :: needs maintainer review before merge. :: none

@ly85206559

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@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.

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. 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 Jul 5, 2026
@ly85206559

Copy link
Copy Markdown
Contributor Author

Live proof (local Android emulator)

Environment: Windows, Android Studio JBR, AVD Medium_Phone (API 17), device emulator-5554.

Unit (PR branch codex/android-bug-scan)

./gradlew :app:testPlayDebugUnitTest --tests ai.openclaw.app.node.CanvasControllerSnapshotParamsTest
→ BUILD SUCCESSFUL

Device (debug APK from PR branch)

Used a local debug-only canvasProof injector (not part of this PR diff) to open the Screen tab, attach the canvas WebView, and run canvas.navigate for loopback vs remote targets.

Case Input URL Blocked Resolved load
loopback http://127.0.0.1:18789 true scaffold (currentUrl=null)
remote https://example.com/canvas false remote (currentUrl=https://example.com/canvas)

Device JSON result:

{"ok":true,"mode":"canvasProof","cases":[{"label":"loopback","inputUrl":"http://127.0.0.1:18789","blocked":true,"currentUrl":null,"loadKind":"scaffold"},{"label":"remote","inputUrl":"https://example.com/canvas","blocked":false,"currentUrl":"https://example.com/canvas","loadKind":"remote"}]}

Redacted logcat excerpts:

I/CanvasE2E: PASS {"ok":true,"mode":"canvasProof",...}
D/OpenClawCanvas: load scaffold: file:///android_asset/CanvasScaffold/scaffold.html
D/OpenClawCanvas: load url: https://example.com/canvas

This confirms the PR behavior on device: loopback canvas navigation falls back to the bundled scaffold, while a normal remote canvas URL is still accepted.

Repro script (local): scripts/repro/android-canvas-loopback-live-proof.ps1

@clawsweeper re-review

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. 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: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jul 5, 2026
@steipete steipete self-assigned this Jul 6, 2026
@openclaw-barnacle openclaw-barnacle Bot added the docs Improvements or additions to documentation label Jul 6, 2026
@steipete

steipete commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Land-ready maintainer rewrite for final head dc58b11f7f148b86b188acbc5ff2e22caccd845e:

  • Replaced the submitted direct-load-only parser with one shared policy for gateway loads and WebView main-frame user, JavaScript, and redirect navigation.
  • Closed unspecified-address, localhost-subdomain, Chromium numeric IPv4, mapped/compatible IPv6, malformed/encoded host, non-GET main-frame, and Unicode/IDNA compatibility-host bypasses.
  • Preserved normal HTTPS, LAN/mDNS gateways, Android's 10.0.2.2 emulator host bridge, ASCII punycode, bundled Canvas URLs, blank input, and slash.
  • Kept scope explicit: this is a main-frame navigation boundary, not a claim of a complete subresource firewall.

Proof:

  • Sanitized public/no-IAM AWS Crabbox cbx_e9f25743d263, run run_a58919388153: exact code head 802a3cc39808c59af270d2da818d86bc1b30acbb passed both debug-flavor unit suites and assemblePlayDebug (78 tasks); full ktlintFormat left both touched policy/test files unchanged. Final head differs only by syncing generated docs/docs_map.md and the hosted-gate verifier to current main after upstream drift.
  • Final-head CI 28773495496 and Workflow Sanity 28773495483: passed.
  • Five GPT-5.5 autoreview passes; three accepted hardening findings resolved, final full-PR review clean at 0.82, formatting-only closeout clean at 0.98.
  • Contributor emulator proof covers direct loopback rejection and normal remote retention.

Known gap: source-blind exact-head validation is blocked without an interactive Android runtime plus controlled redirect, non-GET, LAN/mDNS, and loopback fixtures; those clauses are supported by source-aware tests/build/review, not claimed as live proof. Maintainer-owned changelog credit will follow in the consolidated Android batch. Thanks @ly85206559 for the original report and patch.

@openclaw-barnacle openclaw-barnacle Bot added scripts Repository scripts size: L and removed size: M labels Jul 6, 2026
@clawsweeper clawsweeper Bot removed proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. labels Jul 6, 2026
@clawsweeper clawsweeper Bot removed the status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. label Jul 6, 2026
@steipete
steipete merged commit 86d5389 into openclaw:main Jul 6, 2026
74 of 85 checks passed
vincentkoc added a commit to zhangqueping/openclaw that referenced this pull request Jul 6, 2026
* origin/main: (1287 commits)
  fix(android): block loopback canvas navigation (openclaw#99874)
  fix(hooks): suppress unhandled stdout/stderr stream errors in gmail watcher (openclaw#100519)
  fix(memory-core): guard qmd mcporter JSON.parse against non-JSON stdout (openclaw#98381)
  fix(build): fall back to tsx for build TypeScript scripts (openclaw#91262)
  feat(skills): suggest saving detected reusable workflows by default (openclaw#95477) (openclaw#100692)
  docs(changelog): remove generated release-note entries
  feat(telegram): offer BotFather web app flow in setup help and docs (openclaw#100540)
  fix(ios): unify Talk and Settings row typography on one branded detail row (openclaw#100515)
  feat(gateway): add persisted crash-loop breaker and fatal-config exit contract
  refactor(macos): lock and unify PortGuardian tunnel record persistence so concurrent app instances cannot lose orphan records (openclaw#100601)
  fix: stop reconnecting on protocol mismatch (openclaw#98414)
  fix(maint): reuse recent hosted gates after rebase (openclaw#100663)
  fix(ui): reopen web terminals without stale content (openclaw#100665)
  fix(browser): diagnose empty WSL2 Chrome replies (openclaw#100590)
  fix(ios): chat snaps back to bottom when scrolling to top via status-bar tap (openclaw#100502)
  Treat already-compacted CLI compaction as no-op (openclaw#99136)
  docs(changelog): remove direct main fix entry
  fix(feishu): strip internal tool-trace banners from outbound text (openclaw#98705)
  fix(message): thread --limit through to CLI formatter and surface provider pagination hints (openclaw#99089)
  fix(voyage): close response body stream when batch output JSONL parsing throws (openclaw#98840)
  ...

# Conflicts:
#	extensions/memory-wiki/package.json
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 6, 2026
* fix(android): block loopback canvas navigation

* fix(android): reject Unicode canvas hosts

* chore(android): sync app changelog

* style(android): format canvas navigation policy

* chore(docs): sync generated docs map

* chore(ci): sync hosted gate verifier

---------

Co-authored-by: Peter Steinberger <[email protected]>
giodl73-repo pushed a commit to giodl73-repo/openclaw that referenced this pull request Jul 8, 2026
* fix(android): block loopback canvas navigation

* fix(android): reject Unicode canvas hosts

* chore(android): sync app changelog

* style(android): format canvas navigation policy

* chore(docs): sync generated docs map

* chore(ci): sync hosted gate verifier

---------

Co-authored-by: Peter Steinberger <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: android App: android docs Improvements or additions to documentation P2 Normal backlog priority with limited blast radius. scripts Repository scripts size: L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants