Normalize bounded Base64 input consistently across desktop, browser, and mobile pairing paths while preserving lossless legacy padding and trailing-bit spellings. Retain offer validation and authentication behavior.
Adapted from #12027. Compatibility verified across 378 spellings with no previously accepted inputs lost; full PR workflow and aggregate gate passed for a84dfaee35.
Co-authored-by: Henry Su <henrysu4707@gmail.com>
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* feat(mobile): verified https pairing links; custom scheme carries no secret
The desktop's copy-link now emits https://www.onorca.dev/pair#<code>, with the
code only in the fragment. Orca Mobile claims that link as a verified app link
and refuses any pairing code delivered through orca://, which any app can claim;
orca://pair alone opens the scanner with an explanation. In-app QR scan and
paste still accept the custom scheme, so older desktops and phones keep pairing.
The confirm screen shows the desktop address and host key fingerprint before
pairing. config/verified-links/ holds the apple-app-site-association,
assetlinks.json and fallback page to deploy on www.onorca.dev, with a test that
keeps them, mobile/app.config.js and the link builder in agreement.
Part of #17397.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(mobile): refuse pairing codes nested in or normalized out of unverified links
The router unwraps expo-development-client ?url= links and normalizes dot
segments after +native-intent runs, so an orca:// wrapper could still deliver a
code to the confirm screen. Refuse any unverified link carrying code= at any
encoding depth, and match pairing routes on decoded, dot-free segments.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(mobile): escape the apostrophe in the refused-link notice
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(mobile): decode escapes byte by byte before refusing nested pairing codes
An invalid UTF-8 escape made the whole escape run fail to decode, hiding an
adjacent encoded &code= that the router still decodes leniently.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(mobile): decode nested pairing links to any depth before refusing codes
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(mobile): ignore URL-stripped whitespace when refusing nested pairing codes
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor(mobile): decide pairing-link refusal on the router's own parse
Stop re-implementing URL decoding. The guard now runs expo-router's own
extractExpoPathFromURL and parseUrlUsingCustomBase, the functions its linking
applies after +native-intent, and refuses any link from which the router would
read a `code` param or reach a pairing route with a payload. A link the router
cannot parse is refused. Release builds refuse expo-development-client
wrappers outright. A property-style test walks wrapping depths, encoding
layers, stripped control characters, case and unicode, and asserts that the
guard never passes on a link from which the router would read a code.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(mobile): match pairing routes on the top-level segment only
A host link with a segment named pair was sent to the scanner.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(mobile): pin line-terminator fragment links as refused
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: Claude <noreply@anthropic.com>
Fixes#5508.
Rejects mobile-scope phone-QR pairing in the full web client, keeps runtime browser access links on the instant startup path via advisory pairing scope metadata, and surfaces forbidden runtime scope errors instead of rendering empty workspaces or retry-looping setup checks.
Security note: pairing offer scope is UI metadata only. Runtime RPC authorization remains based on the server-side device token registry and mobile allowlist.