fix(cabinet): harden global encoders against lone UTF-16 surrogates

A lone (unpaired) UTF-16 surrogate — a truncated emoji in a backend name/remark
embedded in a subscription/connection URL — makes encodeURI/encodeURIComponent throw
a URIError on iOS WebKit (V8: 'URI malformed'; Safari/JSC: 'String contained an
illegal UTF-16 sequence'). qrcode.react calls encodeURI internally, so such a value
crashed the QR render and tripped the page-level ErrorBoundary ('Something went
wrong / String contained an illegal UTF-16 sequence / Try again').

Rather than wrap each (current and future) call site — and third-party libs we can't
edit — sanitise at the single chokepoint: patch the global encodeURI/encodeURIComponent
at bootstrap to replace lone surrogates with U+FFFD (same remedy as toWellFormed()).
Fail-safe, not fail-broken: verified byte-identical output for all well-formed strings
(URLs unaffected) and no-throw on lone surrogates for the real call patterns (qrcode.react
and btoa(unescape(encodeURIComponent(...)))). A fast path skips the rewrite when no
surrogate code units are present, so the hot path (every request URL) is untouched.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
This commit is contained in:
c0mrade
2026-06-08 11:11:56 +03:00
parent 35428cc27d
commit d37872fd4f
3 changed files with 91 additions and 0 deletions

View File

@@ -20,6 +20,7 @@ import {
isFullscreen,
} from '@telegram-apps/sdk-react';
import { clearStaleSessionIfNeeded } from './utils/token';
import { installEncodingSurrogateGuard } from './utils/installEncodingSurrogateGuard';
import { useAuthStore } from './store/auth';
import { AppWithNavigator } from './AppWithNavigator';
import { ErrorBoundary } from './components/ErrorBoundary';
@@ -29,6 +30,12 @@ import { getCachedFullscreenEnabled, isTelegramMobile } from './hooks/useTelegra
import { applyTelegramLanguage } from './i18n';
import './styles/globals.css';
// Harden the global encoders against lone UTF-16 surrogates (truncated emoji in
// backend names/remarks) BEFORE anything renders or fetches — otherwise such a
// string crashes any encodeURI/encodeURIComponent/btoa path on iOS WebKit,
// including qrcode.react's internal encodeURI. See installEncodingSurrogateGuard.
installEncodingSurrogateGuard();
// Polyfill Object.hasOwn for older iOS/Android WebViews (Safari < 15.4, old Chrome).
// @telegram-apps/sdk v3 depends on valibot which uses Object.hasOwn internally.
// Without this, init() throws LaunchParamsRetrieveError on affected devices.