From 7de59250597c02113a19da27777cf3ea23b502f6 Mon Sep 17 00:00:00 2001 From: AugmentedMode <31675118+AugmentedMode@users.noreply.github.com> Date: Fri, 20 Dec 2024 12:15:55 -0500 Subject: [PATCH] fix: Add main frame URL property to req object whenever req is triggered from an iframe (#29337) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** See the attached issue in metamask planning for more details. [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/29337?quickstart=1) ## **Related issues** Fixes: ## **Manual testing steps** 1. Go to `https://develop.d3bkcslj57l47p.amplifyapp.com/` 2. Click on Proceed anyways (This phishing warning page here is expected) 3. Open the network tab to monitor network requests 4. Connect your wallet and click on a signature or transaction 5. Verify that mainFrameOrigin is included in the payload of the network request to the security alerts API Screenshot 2024-12-20 at 10 46 05 AM ## **Screenshots/Recordings** Below are screenshots demonstrating the behavior of a test HTML page I created: 1. In the first screenshot, before the iframe is loaded, the console shows only the origin of the main frame. 2. In the second screenshot, after clicking the button to load an iframe pointing to example.com, the solution correctly identifies both the mainFrameOrigin (main frame) and the origin (iframe). Screenshot 2024-12-18 at 10 24 48 PM Screenshot 2024-12-18 at 10 24 54 PM ### **Before** ### **After** ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/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. --- .../lib/createMainFrameOriginMiddleware.ts | 24 +++++++++++++++++++ app/scripts/metamask-controller.js | 22 ++++++++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 app/scripts/lib/createMainFrameOriginMiddleware.ts diff --git a/app/scripts/lib/createMainFrameOriginMiddleware.ts b/app/scripts/lib/createMainFrameOriginMiddleware.ts new file mode 100644 index 000000000000..bcbc2cb7d6fd --- /dev/null +++ b/app/scripts/lib/createMainFrameOriginMiddleware.ts @@ -0,0 +1,24 @@ +// Request and responses are currently untyped. +/* eslint-disable @typescript-eslint/no-explicit-any */ + +/** + * Returns a middleware that appends the mainFrameOrigin to request + * + * @param {{ mainFrameOrigin: string }} opts - The middleware options + * @returns {Function} + */ + +export default function createMainFrameOriginMiddleware({ + mainFrameOrigin, +}: { + mainFrameOrigin: string; +}) { + return function mainFrameOriginMiddleware( + req: any, + _res: any, + next: () => void, + ) { + req.mainFrameOrigin = mainFrameOrigin; + next(); + }; +} diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index d60d937e1c3c..c80538e0e7cc 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -296,6 +296,7 @@ import { createUnsupportedMethodMiddleware, } from './lib/rpc-method-middleware'; import createOriginMiddleware from './lib/createOriginMiddleware'; +import createMainFrameOriginMiddleware from './lib/createMainFrameOriginMiddleware'; import createTabIdMiddleware from './lib/createTabIdMiddleware'; import { NetworkOrderController } from './controllers/network-order'; import { AccountOrderController } from './controllers/account-order'; @@ -5669,11 +5670,18 @@ export default class MetamaskController extends EventEmitter { tabId = sender.tab.id; } + let mainFrameOrigin = origin; + if (sender.tab && sender.tab.url) { + // If sender origin is an iframe, then get the top-level frame's origin + mainFrameOrigin = new URL(sender.tab.url).origin; + } + const engine = this.setupProviderEngineEip1193({ origin, sender, subjectType, tabId, + mainFrameOrigin, }); const dupeReqFilterStream = createDupeReqFilterStream(); @@ -5794,13 +5802,25 @@ export default class MetamaskController extends EventEmitter { * @param {MessageSender | SnapSender} options.sender - The sender object. * @param {string} options.subjectType - The type of the sender subject. * @param {tabId} [options.tabId] - The tab ID of the sender - if the sender is within a tab + * @param {mainFrameOrigin} [options.mainFrameOrigin] - The origin of the main frame if the sender is an iframe */ - setupProviderEngineEip1193({ origin, subjectType, sender, tabId }) { + setupProviderEngineEip1193({ + origin, + subjectType, + sender, + tabId, + mainFrameOrigin, + }) { const engine = new JsonRpcEngine(); // Append origin to each request engine.push(createOriginMiddleware({ origin })); + // Append mainFrameOrigin to each request if present + if (mainFrameOrigin) { + engine.push(createMainFrameOriginMiddleware({ mainFrameOrigin })); + } + // Append selectedNetworkClientId to each request engine.push(createSelectedNetworkMiddleware(this.controllerMessenger));