Skip to content

fix(android): reject IPv6 zone IDs in gateway endpoint URLs#99570

Merged
steipete merged 4 commits into
openclaw:mainfrom
ly85206559:fix/android-gateway-ipv6-zone-host
Jul 3, 2026
Merged

fix(android): reject IPv6 zone IDs in gateway endpoint URLs#99570
steipete merged 4 commits into
openclaw:mainfrom
ly85206559:fix/android-gateway-ipv6-zone-host

Conversation

@ly85206559

@ly85206559 ly85206559 commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

Gateway endpoint URLs with IPv6 interface zones — for example, wss://[fe80::1%25wlan0]:443 — passed Android validation and were saved even though OkHttp cannot canonicalize that decoded host for a WebSocket URL. The user only learned the endpoint was unusable after onboarding advanced to connection recovery.

Why This Change Was Made

Reject scoped IPv6 at the canonical endpoint parser boundary with a structured IPV6_ZONE_ID_UNSUPPORTED result. Manual entry, setup codes, and scanned QR codes now preserve that reason and show source-specific replacement guidance; other malformed QR input keeps its existing setup-QR guidance.

This is deliberately fail-closed. Silently stripping a zone could select the wrong link-local interface, while accepting it creates a persisted endpoint that the current OkHttp 5.3.2 URL path cannot dial.

User Impact

Scoped IPv6 gateway URLs are rejected before persistence or connection. The UI tells the user to use an unscoped IPv6 address or LAN hostname. Ordinary IPv4, hostname, and unscoped IPv6 parsing is unchanged.

Evidence

Exact head: e81a9e806d73870140bad0aa7c231ad88f29f8fd

Android 16 emulator before/after

Both captures use the identical input wss://[fe80::1%25wlan0]:443.

Before: accepted, saved, then failed during pairing After: rejected in Manual setup with actionable guidance
Before: scoped IPv6 endpoint was accepted and advanced to a connection failure After: scoped IPv6 endpoint remains on Manual setup with replacement guidance

Automated proof

ANDROID_HOME=$HOME/Library/Android/sdk ./gradlew :app:testPlayDebugUnitTest --tests ai.openclaw.app.ui.GatewayConfigResolverTest
ANDROID_HOME=$HOME/Library/Android/sdk ./gradlew :app:assemblePlayDebug
node --import tsx scripts/native-app-i18n.ts check

Both commands passed. The focused suite covers cleartext and TLS scoped hosts, unscoped IPv6, setup-code/QR error propagation, and all source-specific messages.

Fresh Codex autoreview passed in uncommitted and branch modes with no findings.

@clawsweeper

clawsweeper Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed July 3, 2026, 2:51 PM ET / 18:51 UTC.

Summary
The PR changes Android gateway endpoint parsing and onboarding messages so scoped IPv6 URLs are rejected with a dedicated validation reason, plus tests and native i18n inventory.

PR surface: Other +68. Total +68 across 4 files.

Reproducibility: yes. for source-level reproduction: current main accepts scoped IPv6 endpoint URLs into Android gateway config, and the runtime then hands bracketed scoped hosts to OkHttp Request.Builder.url. I did not run the Android emulator in this read-only review, but I inspected the PR’s before/after emulator screenshots for the same input.

Review metrics: none identified.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🦞 diamond lobster ✨ media proof bonus
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.

Mantis proof suggestion
A short Android visual proof would be useful independent confirmation of the visible manual-setup rejection path. A maintainer can ask Mantis to capture proof by posting this exact PR comment:

@openclaw-mantis visual task: verify Android manual setup rejects wss://[fe80::1%25wlan0]:443 with the IPv6 zone guidance and does not persist the endpoint.

Risk before merge

  • [P1] A separate current-main discovery path can still carry Android hostAddress values directly into GatewayEndpoint; I did not treat that as a PR-introduced blocker because this diff is scoped to manual/setup/QR URL validation.

Maintainer options:

  1. Decide the mitigation before merge
    Reject scoped IPv6 at the shared Android endpoint parser for manual/setup/QR inputs while preserving unscoped IPv6 and existing cleartext-host rules; handle any discovered-link-local follow-up separately if it is reproducible.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Next step before merge

  • No repair lane is needed; the PR needs normal maintainer review and CI completion rather than an automated code repair.

Security
Cleared: No security or supply-chain concern is visible; the diff touches Android Kotlin parsing, tests, and generated i18n inventory only.

Review details

Best possible solution:

Reject scoped IPv6 at the shared Android endpoint parser for manual/setup/QR inputs while preserving unscoped IPv6 and existing cleartext-host rules; handle any discovered-link-local follow-up separately if it is reproducible.

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

Yes for source-level reproduction: current main accepts scoped IPv6 endpoint URLs into Android gateway config, and the runtime then hands bracketed scoped hosts to OkHttp Request.Builder.url. I did not run the Android emulator in this read-only review, but I inspected the PR’s before/after emulator screenshots for the same input.

Is this the best way to solve the issue?

Yes for the changed surface: the shared parser is the right boundary for manual, setup-code, and scanned-QR endpoint URLs, and rejecting is safer than stripping an interface zone. Discovered-gateway link-local handling is an adjacent existing path, not something this PR needs to solve before merge.

AGENTS.md: found and applied where relevant.

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

Label changes

Label changes:

  • add proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes before/after Android emulator screenshots, which I inspected, showing the same scoped IPv6 input accepted before and rejected after with guidance.
  • add proof: 📸 screenshot: Contributor real behavior proof includes screenshot evidence. The PR body includes before/after Android emulator screenshots, which I inspected, showing the same scoped IPv6 input accepted before and rejected after with guidance.
  • 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 (screenshot): The PR body includes before/after Android emulator screenshots, which I inspected, showing the same scoped IPv6 input accepted before and rejected after with guidance.

Label justifications:

  • P2: The PR targets a bounded Android onboarding endpoint-validation bug with limited blast radius.
  • 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 (screenshot): The PR body includes before/after Android emulator screenshots, which I inspected, showing the same scoped IPv6 input accepted before and rejected after with guidance.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes before/after Android emulator screenshots, which I inspected, showing the same scoped IPv6 input accepted before and rejected after with guidance.
  • proof: 📸 screenshot: Contributor real behavior proof includes screenshot evidence. The PR body includes before/after Android emulator screenshots, which I inspected, showing the same scoped IPv6 input accepted before and rejected after with guidance.
Evidence reviewed

PR surface:

Other +68. Total +68 across 4 files.

View PR surface stats
Area Files Added Removed Net
Source 0 0 0 0
Tests 0 0 0 0
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 4 181 113 +68
Total 4 181 113 +68

What I checked:

Likely related people:

  • obviyus: Ayaan Zaidi has the heaviest adjacent history across Android gateway config, onboarding, and auth state, including cleartext LAN and setup-code endpoint changes. (role: feature-history owner; confidence: high; commits: 4b188dcf975e, 3e2e010952e5, 945b198c7696; files: apps/android/app/src/main/java/ai/openclaw/app/ui/GatewayConfigResolver.kt, apps/android/app/src/main/java/ai/openclaw/app/ui/OnboardingFlow.kt, apps/android/app/src/test/java/ai/openclaw/app/ui/GatewayConfigResolverTest.kt)
  • steipete: Peter Steinberger authored the current PR head commits and has prior adjacent pairing setup-code history in Android gateway/onboarding surfaces. (role: recent area contributor; confidence: medium; commits: bf89947a8e9e, 9a85c540be6e, e81a9e806d73; files: apps/android/app/src/main/java/ai/openclaw/app/ui/GatewayConfigResolver.kt, apps/android/app/src/main/java/ai/openclaw/app/ui/OnboardingFlow.kt, apps/.i18n/native-source.json)
  • eleqtrizit: Agustin Rivera has adjacent history requiring TLS for remote Android gateway endpoints, which shares the parser validation invariant touched here. (role: adjacent security/endpoint owner; confidence: medium; commits: a941a4fef9bc; files: apps/android/app/src/main/java/ai/openclaw/app/ui/GatewayConfigResolver.kt, apps/android/app/src/test/java/ai/openclaw/app/ui/GatewayConfigResolverTest.kt)
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 backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. labels Jul 3, 2026
@ly85206559

Copy link
Copy Markdown
Contributor Author

Local proof (Windows)

Environment

  • Host: CONAN (Windows)
  • Branch HEAD: 09d351773e
  • JAVA_HOME: F:\Program Files\Android\Android Studio\jbr
  • ANDROID_HOME / ANDROID_SDK_ROOT: F:\Android\Sdk
  • GRADLE_USER_HOME: F:\Android\Sdk\.gradle

Command

cd d:\WorkSpace\AI\openclaw\.worktrees\android-gateway-ipv6-zone-host\apps\android
.\gradlew.bat :app:testPlayDebugUnitTest `
  --tests ai.openclaw.app.ui.GatewayConfigResolverTest.parseGatewayEndpointAllowsLinkLocalIpv6ZoneCleartextWsUrls `
  --tests ai.openclaw.app.ui.GatewayConfigResolverTest.parseGatewayEndpointAllowsSecureIpv6ZoneUrls

Result: BUILD SUCCESSFUL in 53s

JUnit (GatewayConfigResolverTest): 2 tests, 0 failures, 0 errors

  • parseGatewayEndpointAllowsLinkLocalIpv6ZoneCleartextWsUrls
  • parseGatewayEndpointAllowsSecureIpv6ZoneUrls

@ly85206559 ly85206559 changed the title fix(android): normalize IPv6 zone IDs in gateway endpoint hosts fix(android): reject IPv6 zone IDs in gateway endpoint URLs Jul 3, 2026
@ly85206559

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Addressed correctness feedback in 56f96f406b: replaced zone stripping with explicit rejection of scoped IPv6 hosts (% in authority) because OkHttp URL strings cannot dial zones and stripping could pick the wrong link-local interface. PR title/body + tests updated; local Gradle proof green (2/2 reject tests).

@clawsweeper

clawsweeper Bot commented Jul 3, 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 self-assigned this Jul 3, 2026
ly85206559 and others added 3 commits July 3, 2026 19:26
Strip interface zones during endpoint parsing so link-local URLs like
ws://[fe80::1%25eth0] connect using socket-ready literals such as fe80::1.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@steipete steipete force-pushed the fix/android-gateway-ipv6-zone-host branch from 56f96f4 to 9a85c54 Compare July 3, 2026 18:39
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. 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: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jul 3, 2026
@steipete

steipete commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Land-ready maintainer proof for exact head e81a9e806d73870140bad0aa7c231ad88f29f8fd:

  • Rebased and rewrote the fix around the canonical endpoint parser. Scoped IPv6 hosts now return the structured IPV6_ZONE_ID_UNSUPPORTED reason before persistence, while manual URL, setup-code, and QR flows retain source-specific guidance.
  • Expanded table-driven parser and onboarding-copy coverage; GatewayConfigResolverTest, :app:assemblePlayDebug, and scripts/native-app-i18n.ts check all pass.
  • Fresh Codex autoreview: no actionable findings.
  • Exact-head CI is green: https://github.com/openclaw/openclaw/actions/runs/28678170574 (one unrelated timing test initially failed, then passed unchanged on the job rerun).
  • Android 16 emulator proof used the same wss://[fe80::1%25wlan0]:443 input. Baseline persisted the unusable endpoint and advanced to pairing; candidate stays in Manual setup with actionable replacement guidance.
Before After
Before: scoped IPv6 endpoint accepted and pairing attempted After: scoped IPv6 endpoint rejected with replacement guidance

Known proof boundary: no physical link-local IPv6 interface was available. The reproduced defect is the production UI accepting and persisting an endpoint that the Android HTTP stack cannot represent for dialing; rejection now occurs before that boundary.

@steipete steipete merged commit 6b253eb into openclaw:main Jul 3, 2026
180 of 191 checks passed
@steipete

steipete commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 4, 2026
…#99570)

* fix(android): normalize IPv6 zone IDs in gateway endpoint hosts

Strip interface zones during endpoint parsing so link-local URLs like
ws://[fe80::1%25eth0] connect using socket-ready literals such as fe80::1.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(android): reject IPv6 zone IDs in gateway endpoint URLs

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(android): reject scoped IPv6 gateway hosts

* chore(android): sync native i18n inventory

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
@ly85206559 ly85206559 deleted the fix/android-gateway-ipv6-zone-host branch July 6, 2026 07:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: android App: android P2 Normal backlog priority with limited blast radius. proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: M 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.

2 participants