feat: make MM Connect deeplink parsing more robust#26648
Conversation
| if (metadata.dapp.icon) { | ||
| if ( | ||
| typeof metadata.dapp.icon !== 'string' || | ||
| metadata.dapp.icon.length > 2048 // this seems long still |
There was a problem hiding this comment.
Maybe we drop it to 1000 and add validation that its only ever https:// ?
|
CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes. |
| !metadata.dapp.url || | ||
| typeof metadata.dapp.url !== 'string' | ||
| typeof metadata.dapp.url !== 'string' || | ||
| metadata.dapp.url.length > 2048 |
There was a problem hiding this comment.
this seems long. thoughts?
There was a problem hiding this comment.
seems like a reasonable limit to me!
| metadata.sdk.version.length > 256 || // this seems long still | ||
| !metadata.sdk.platform || | ||
| typeof metadata.sdk.platform !== 'string' | ||
| typeof metadata.sdk.platform !== 'string' || | ||
| metadata.sdk.platform.length > 256 // this seems long still |
There was a problem hiding this comment.
I think we can lower this. Our expected values are something like JavaScript, React Native, iOS, Android right?
There was a problem hiding this comment.
i'll make Version and Platform both 64
| if (jsonString.length > 1024 * 1024) { | ||
| throw new Error('Decompressed payload too large (max 1MB).'); | ||
| } | ||
|
|
There was a problem hiding this comment.
I believe @chakra-guy has a fix to make this use streaming decompression instead
|
The two blocks connection requests with 'metamask' as origin/dapp name tests (lines 665 and 695 of connection-registry.test.ts) now pass for the wrong reason. With the new stricter EDIT => addressed here a65d13e |
…king logic The URL test used `metamask` which now fails isConnectionRequests new URL() validation before reaching the INTERNAL_ORIGINS guard. The name test had no assertions and passed vacuously. Mock INTERNAL_ORIGINS with a valid URL so the URL test reaches the origin check, and add assertions to the name test.
…l to http(s) (#26833) ## **Description** The `new URL(metadata.dapp.url)` validation added in the parent branch ensures `dapp.url` is always a parseable URL. However, the production `INTERNAL_ORIGINS` array contains plain strings (`'metamask'`, `'MetaMask Mobile'`, `process.env.MM_FOX_CODE`), so the existing `INTERNAL_ORIGINS.includes(dapp.url)` check became dead code — no validated URL can equal those strings. The test masked this by mocking `INTERNAL_ORIGINS` with `'https://internal.metamask.io'`, a value absent from the production array. This PR: 1. **Restricts `dapp.url` to `http:`/`https:` schemes** in `isConnectionRequest()` — this is the primary security boundary. It prevents a malicious dApp from sending `metamask://...` or other non-web schemes as its URL, which could match internal origin strings. 2. **Retains the original `INTERNAL_ORIGINS.includes()` check as documented defense-in-depth** — currently redundant but will activate if the upstream URL validation is ever relaxed. 3. **Adds cross-referencing security comments** in both locations so future developers understand the invariant linking them. 4. **Removes the `INTERNAL_ORIGINS` mock** from the test file so tests exercise the real production values instead of a synthetic URL that hid the dead code. ## **Changelog** CHANGELOG entry: null ## **Related issues** Refs: parent branch `jl/make-mmc-parsing-more-robust` ## **Manual testing steps** ```gherkin Feature: SDKConnectV2 internal origin blocking Scenario: dApp with non-http scheme is rejected at validation Given a dApp connection request with url "metamask://evil.com" When isConnectionRequest validates the request Then it returns false Scenario: dApp with internal origin name is blocked Given a dApp connection request with dapp.name "metamask" When ConnectionRegistry handles the deeplink Then it throws "External transactions cannot use internal origins" ``` ## **Screenshots/Recordings** N/A — no UI changes. ### **Before** N/A ### **After** N/A ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.
… into jl/make-mmc-parsing-more-robust
… organization This commit refactors the test file by moving the mock connection request, connection info, and valid deeplink definitions into the `beforeEach` block. This change enhances the organization of the test setup, ensuring that the mock data is reset before each test case, improving test reliability and readability.
… into jl/make-mmc-parsing-more-robust
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
🔍 Smart E2E Test Selection
click to see 🤖 AI reasoning detailsE2E Test Selection:
These changes affect the SDKConnectV2 module which is the entry point for:
The selected tags cover dApp connection flows:
Risk is HIGH because these are security-critical changes to the connection validation boundary that could potentially break legitimate dApp connections if validation is too strict, or leave security holes if validation is insufficient. Performance Test Selection: |
The committed fixture schema is out of date. To update, comment: |
Description
Adds more robust parsing to the MM Connect deeplink handler
Changelog
CHANGELOG entry: null
MM Connect not released to public yet
Related issues
Fixes: https://consensyssoftware.atlassian.net/browse/WAPI-1125?atlOrigin=eyJpIjoiNzg3ZGFiZWExYTE2NDdmZmFiMDM5YWNmN2MzNWRmY2MiLCJwIjoiaiJ9
Manual testing steps
Screenshots/Recordings
Before
After
Pre-merge author checklist
Pre-merge reviewer checklist
Note
Medium Risk
Tightens validation for incoming MM Connect deeplink payloads and adds decompression size limits, which could reject previously accepted connection requests and affect connection onboarding flows.
Overview
Hardens MM Connect deeplink parsing and validation.
isConnectionRequest()now enforces stricter constraints on connection request fields (UUID + handshake channel format, base64 public key decoding/size, allowedmode, futureexpiresAt, length limits, and http(s)-only dapp URLs with optional icon URL validation).Adds additional safety checks in deeplink handling.
ConnectionRegistry.parseConnectionRequest()now rejects decompressed payloads over 1MB, andhandleConnectDeeplink()documents and tests the existing internal-origin block as defense-in-depth.Expands automated coverage. Adds a comprehensive
connection-request.test.tssuite and updatesconnection-registry.test.tsto use per-test fixtures plus new cases for oversized decompressed payloads and internal-origin blocking.Written by Cursor Bugbot for commit c63abfe. This will update automatically on new commits. Configure here.