Skip to content

Keep the native bridge and push deep links on the endpoint's origin - #10

Merged
oblomov-dev merged 3 commits into
mainfrom
claude/beautiful-turing-wxr303
Oct 12, 2026
Merged

oblomov-dev merged 3 commits into
mainfrom
claude/beautiful-turing-wxr303

Conversation

@oblomov-dev

Copy link
Copy Markdown
Member

Security pass over the shells and the push client. One commit per fix:

  1. Serve the native bridge to pages of the endpoint's origin only. Navigation stays inside the shell, so IdP login pages and any site an app links to ran with the full bridge: they could read the push token, open the scanner or ask for a biometric confirmation.
    • Android injects the shim, and answers its @JavascriptInterface transport, only while the main frame is on the configured endpoint's origin. The check is BridgeOrigin (scheme, host and effective port), which has a new JVM test, BridgeOriginTest.
    • iOS answers only main-frame messages from that origin. It rejects everything else with forbidden and ignores calls from frames inside a page, because an answer to one would land in the main frame and could settle one of that page's own pending calls.
    • The remaining Android gap is a frame inside an app page, because addJavascriptInterface cannot tell frames apart. It is written down as NEXT_STEPS E8: closing it needs WebViewCompat.addWebMessageListener, which brings in a new androidx.webkit dependency and needs a decision.
    • Updated bridge/README.md, AGENTS.md, DISTRIBUTION checklist, TESTING runbook and TEST_PROTOCOL.
  2. Escape the alert text and deep link in the push payload (abap/push/zcl_test_mobile_push). A quote in the alert made the JSON invalid, and crafted text could add its own data.url.
  3. Open push deep links only on the endpoint's origin. MainActivity is the exported launcher activity, so any app on the device could start it with the deeplink_url extra. A javascript: URL then ran in the signed-in app page, because loadUrl evaluates it in the current document, and any other URL opened a foreign site inside the trusted shell. iOS applies the same rule to notification taps.

Not device-tested; CI compiles both shells and runs the JVM tests and the bridge contract tests. TESTING.md §1 gained the foreign-origin check for the next device run.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Te3BRpRpxNhZAYnrQL6Gdt


Generated by Claude Code

The WebView keeps every navigation inside the shell, so IdP login pages and
any site an app links to ran with the full bridge: they could read the push
token, open the scanner or ask for a biometric confirmation. Android now
injects the shim and answers its transport only while the main frame is on
the configured endpoint's origin (BridgeOrigin, JVM-tested); iOS answers only
main-frame calls from that origin, rejects the rest with "forbidden" and
ignores calls from frames inside a page. The remaining Android gap (frames
inside an app page) is written down as NEXT_STEPS E8.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Te3BRpRpxNhZAYnrQL6Gdt
zcl_test_mobile_push wrote both values into JSON string literals as they
came: an alert with a quote or backslash produced invalid JSON (the push
API answered 400), and crafted text could add its own "data" member with
a different deep link the shells open on tap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Te3BRpRpxNhZAYnrQL6Gdt
MainActivity is exported as the launcher, so any app on the device could
start it with the deep-link extra: a javascript: URL ran in the signed-in
app page (loadUrl evaluates it in the current document), any other URL
opened a foreign site inside the trusted shell. Both shells now take a deep
link only when it has the configured endpoint's origin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Te3BRpRpxNhZAYnrQL6Gdt
@oblomov-dev
oblomov-dev merged commit 421aa97 into main Oct 12, 2026
8 checks passed
@oblomov-dev
oblomov-dev deleted the claude/beautiful-turing-wxr303 branch October 12, 2026 09:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants