Repository navigation
Conversation
…p requests stellar_signXDR and stellar_signAndSubmitXDR reject an xdr over 256 KB of base64, above the network's largest transaction, and stellar_signAuthEntry rejects an entry over 64 KB, before any scan or display. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… in dApp requests stellar_signAuthEntry, stellar_signXDR and stellar_signAndSubmitXDR now reject a request whose authorization trees hold more than 100 contract calls or go deeper than 20 levels, before any scan or display; the signing screens render one card per call. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ndenting them Indenting deeply nested JSON can make it hundreds of times longer than it was sent; past 20,000 characters the message is now shown as sent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…checked loader SVG icons are now downloaded with a 100 KB cap that aborts mid-stream, a 5 s timeout and https only, and drawn with SvgXml only if they hold at most 2,000 plain drawing elements; anything else shows the app's initials. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
React Native on Android sends a stale byte count with every chunk of a text response, so the progress check never passed the cap there, and a large icon was held in full until the final length check turned it away. The check now also counts the text received so far. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A request turned away by a content cap stayed set as the current request until the next one arrived, so its payload was still parsed and walked for display. It's now dropped in the same update that rejects it. The rejection is logged as a warning, like other dApp input the wallet turns away, and a failure to answer the dApp is caught. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The nested message is about 1 KB as sent and about 500 KB once indented, not 10 KB and 5 MB. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
react-native-svg follows a mask into its content with no loop check, so an icon whose mask content used the same mask recursed until the app was killed (seen on the iOS simulator). The loader now parses the icon with react-native-svg's own parser and draws it only if nothing inside a <mask> or <clipPath> is itself masked or clipped, by an attribute or an inline style. Icons that use masks and clip paths the usual way still draw. An icon that doesn't parse now shows initials too, instead of failing while it's drawn. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
|
Android Emulator preview APK is ready: https://github.com/stellar/freighter-mobile/releases/tag/untagged-b72cb25663cd7396f0f6 |
Contributor
|
iOS Simulator preview build is ready: https://github.com/stellar/freighter-mobile/releases/tag/untagged-315fad196769806590f2 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TL;DR
Stacked on #1060. A dApp's request content and its icon are now bounded before the wallet parses or draws them:
https:only, at most 100 KB (the download stops as soon as it passes the cap), with a 5-second timeout. It draws one only if it has at most 2,000 elements from a list of plain drawing elements, with no DOCTYPE or entities, and no mask or clip path applied inside a mask or clip path. Anything else shows the dApp's initials.Lines changed
Only this PR's own changes, not those of the PRs it's stacked on.
Screenshots (iPhone SE simulator on iOS 27.2, Pixel 10 Pro emulator)
<use>: initialsImplementation details (for agents)
What changed:
src/helpers/walletKitValidation.ts:SIGN_XDR_MAX_LENGTH(262,144 base64 characters),SIGN_AUTH_ENTRY_MAX_LENGTH(65,536),MAX_AUTH_INVOCATIONS(100, across all of a request's authorization trees) andMAX_AUTH_INVOCATION_DEPTH(20, a root call is depth 1).SIGN_MESSAGE_MAX_BYTESstays 10,240.validateSignXdrLength,validateSignAuthEntryLength,getTransactionAuthInvocationsandvalidateInvocationTrees(an explicit stack, so a deep tree can't overflow the call stack).freighter-mobile/src/helpers/walletKitValidation.ts
Lines 35 to 64 in 702d68c
freighter-mobile/src/helpers/walletKitValidation.ts
Lines 101 to 186 in 702d68c
src/providers/WalletKitProvider.tsx: the XDR and auth-entry prevalidation runs the length check, then the call-count and depth check, before the Blockaid scan.failRequestValidationnow logs a warning (method and reason only), rejects with a.catch, and drops the request as the current one, so a rejected request is never parsed for display.freighter-mobile/src/providers/WalletKitProvider.tsx
Lines 775 to 863 in 702d68c
src/components/screens/WalletKit/DappMessageDisplay.tsx: falls back to the message as sent when the indented JSON would passSIGN_MESSAGE_PRETTY_MAX_LENGTH(20,000). Hermes refuses JSON nested deeper than 510 levels, so the worst case that still parses is a 10 KB message 510 levels deep, about 5.2M characters once indented, built in about 13 ms on a Mac and then dropped.freighter-mobile/src/components/screens/WalletKit/DappMessageDisplay.tsx
Lines 47 to 53 in 702d68c
src/helpers/boundedSvg.ts(new):fetchBoundedSvg(url)downloads withXMLHttpRequestand aborts once the text received passes 100 KB. On Android, React Native reports a staleloadedfor text responses, so the length ofresponseTextcounts too. It resolvesnullon a timeout, an error, a non-2xx status or a URL that isn'thttps:.isRenderableSvg(text)checks the length, rejects DOCTYPE and ENTITY declarations, counts opening tags againstMAX_SVG_ELEMENTS(2,000) and the element allowlist, then parses the file with react-native-svg's ownparse()and walks its element tree: a mask or clip-path reference on a<mask>or<clipPath>, or on anything inside one, is refused. On iOS a mask that masks itself recursed natively until the app aborted.freighter-mobile/src/helpers/boundedSvg.ts
Lines 17 to 169 in 702d68c
src/components/sds/App/index.tsx: remote SVG icons render throughBoundedSvgIcon(fetch, check, thenSvgXml) instead ofSvgUri, protocol icons included. While it loads, the icon's box stays empty; on any failure it shows the initials.freighter-mobile/src/components/sds/App/index.tsx
Lines 80 to 125 in 702d68c
i18n (en, pt):
walletKit.errorXdrTooLarge,walletKit.errorAuthEntryTooLargeandwalletKit.errorTooManyInvocations.Verification:
yarn test: 353 suites pass, 5,291 tests pass and 6 are skipped. New tests cover each cap at and past its limit, the provider rejecting before the scan and never preparing a rejected request for display, the as-sent fallback, the SVG loader (abort at the cap with the stale Android progress, timeout,http:refused, each blocked element, self-referencing masks and clip paths, a file that doesn't parse) and the icon component's initials fallback.yarn lint:tspasses, and ESLint and Prettier pass on every changed file.<use>, a 1.1 MB sprite sheet and a self-masking one show initials, and the app stays up;Follow-ups / out of scope:
sodipodi:,rdf:) and Illustrator's DOCTYPE.docs/walletconnect-rpc-methods.mddoesn't list the new caps.🤖 Generated with Claude Code