Skip to content

fix: mv3 sw lavamoat background wrapping - #44187

Merged
HowardBraham merged 21 commits into
mainfrom
fix/mv3-sw-lavamoat-background-wrapping
Jul 10, 2026
Merged

fix: mv3 sw lavamoat background wrapping#44187
HowardBraham merged 21 commits into
mainfrom
fix/mv3-sw-lavamoat-background-wrapping

Conversation

@itsyoboieltr

@itsyoboieltr itsyoboieltr commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Description

  1. Split the entry sets. nullUnsafeEntries (scripts/inpage.js, bootstrap) run fully outside LavaMoat with no runtime added. service-worker.ts stays in the unsafe layer (so its module + static bootstrap imports are excluded from wrapping) but its chunk now runs in safe mode so it carries the LavaMoat runtime.
  2. SES in the SW bundle. The MV3 SW is self-contained and loads no shared runtime chunk, so service-worker.js is added to inlineLockdown — SES is inlined into its own bundle so the wrapped background has hardened intrinsics + Compartment/harden available.
  3. Layer boundary at the dynamic import. lavamoatBackgroundLayerRule re-layers background.js (and, by inheritance, its whole graph) out of unsafe into a background layer so it gets wrapped, and lavamoatUnsafeLayerRule now excludes background.js so the boundary module itself is wrapped rather than excluded.
  4. Scuttling on the SW global. Defense-in-depth parity with the UI runtime and scripts/contentscript.js chunks: scuttleGlobalThis is enabled for the SW with an exception list for the globals the unwrapped bootstrap and webpack's importScripts chunk loader touch live after scuttling (importScripts, console, performance, timers, and the Sentry cluster). Dependency containment does not depend on scuttling — it comes from the Compartments — so wrapped deps still can't reach the real global regardless.
  5. Policies regenerated. lavamoat/webpack/mv3/{main,flask,beta,experimental}/policy.json were regenerated to include the now-wrapped background dependency graph (~1198 → ~1594 resources). MV2 policies are untouched.

Changelog

CHANGELOG entry: null

Related issues

Fixes:

Manual testing steps

  1. Build the MV3 Chrome bundle with LavaMoat: yarn webpack:lavamoat:build.

  2. Load dist/chrome unpacked in Chrome, open the service worker inspector

  3. Confirm the service worker installs and reaches MetaMask initialization complete with no inaccessible under scuttling mode / missing-global / policy errors.

  4. Containment check: temporarily patch a background dependency (e.g.node_modules/loglevel/lib/loglevel.js) with:

    console.log('probe', {
      clients: typeof globalThis.clients,
      registration: typeof globalThis.registration,
      serviceWorker: typeof globalThis.serviceWorker,
      keyCount: Object.keys(globalThis).length,
    });

Rebuild, reload, and confirm in the SW console that clients/registration/serviceWorker are "undefined" and keyCount is small (the dep sees its policy-scoped Compartment global).

Screenshots/Recordings

N/A — no UI changes. Behavioral verification is the service-worker console output described in Manual testing steps.

Before

A background dependency could see globals outside of its intended scope.

After

A wrapped background dependency sees only its Compartment global.

Pre-merge author checklist

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.

Note

High Risk
Changes core extension security boundaries for the MV3 service worker and background; mis-layering could break the SW or leave background code unwrapped.

Overview
Fixes MV3 so background.js and its dependency graph run inside LavaMoat instead of inheriting the unwrapped service worker “unsafe” layer.

LavamoatPlugin now treats only inpage/bootstrap as null_unsafe, keeps service-worker.ts in the unsafe webpack layer but runs that chunk in safe mode (with inlined SES on service-worker.js, scuttleGlobalThis, and importScripts in the exception list). lavamoatBackgroundLayerRule plus an exclude on background.js in the unsafe exclude-loader pull the background entry and its graph into a dedicated background layer so they get wrapped.

Adds LavamoatPlugin.test.ts regression tests for the layer rules and per-chunk runtime config. LavaMoat policies are updated (notably a large MV3 policy.json regen for the newly wrapped background graph; small policy-override.json tweaks including zod global write).

Reviewed by Cursor Bugbot for commit eb17c5c. Bugbot is set up for automated code reviews on this repo. Configure here.

@itsyoboieltr itsyoboieltr self-assigned this Jul 6, 2026
@itsyoboieltr itsyoboieltr added the team-extension-platform Extension Platform team label Jul 6, 2026
@itsyoboieltr
itsyoboieltr requested review from a team as code owners July 6, 2026 00:20
@itsyoboieltr itsyoboieltr added the no-changelog no-changelog Indicates no external facing user changes, therefore no changelog documentation needed label Jul 6, 2026
@github-project-automation github-project-automation Bot moved this to Needs dev review in PR review queue Jul 6, 2026
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

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.

@metamask-ci

metamask-ci Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

✨ Files requiring CODEOWNER review ✨

👨‍🔧 @MetaMask/extension-platform (2 files, +315 -83)
  • 📁 development/
    • 📁 webpack/
      • 📁 test/
        • 📄 plugins.LavamoatPlugin.test.ts +186 -0
      • 📁 utils/
        • 📁 plugins/
          • 📁 LavamoatPlugin/
            • 📄 index.ts +129 -83

📜 @MetaMask/policy-reviewers (12 files, +8044 -160)
  • 📁 lavamoat/
    • 📁 webpack/
      • 📁 mv2/
        • 📁 beta/
          • 📄 policy-override.json +12 -7
        • 📁 experimental/
          • 📄 policy-override.json +12 -7
        • 📁 flask/
          • 📄 policy-override.json +12 -7
        • 📁 main/
          • 📄 policy-override.json +12 -7
      • 📁 mv3/
        • 📁 beta/
          • 📄 policy-override.json +12 -0
          • 📄 policy.json +1987 -33
        • 📁 experimental/
          • 📄 policy-override.json +12 -0
          • 📄 policy.json +1987 -33
        • 📁 flask/
          • 📄 policy-override.json +12 -0
          • 📄 policy.json +1987 -33
        • 📁 main/
          • 📄 policy-override.json +12 -0
          • 📄 policy.json +1987 -33

Tip

Follow the policy review process outlined in the LavaMoat Policy Review Process doc before expecting an approval from Policy Reviewers.


👨‍🔧 @itsyoboieltr (2 files, +315 -83)
  • 📁 development/
    • 📁 webpack/
      • 📁 test/
        • 📄 plugins.LavamoatPlugin.test.ts +186 -0
      • 📁 utils/
        • 📁 plugins/
          • 📁 LavamoatPlugin/
            • 📄 index.ts +129 -83

@itsyoboieltr itsyoboieltr moved this from Needs dev review to Review in progress in PR review queue Jul 6, 2026
@itsyoboieltr itsyoboieltr moved this from Review in progress to Needs dev review in PR review queue Jul 6, 2026
@github-actions github-actions Bot added the size-S label Jul 6, 2026
Comment thread development/webpack/utils/plugins/LavamoatPlugin/index.ts
Comment thread package.json Outdated
@metamask-ci

metamask-ci Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor
Builds ready [b84b701]
Deprecated Browserify fallback builds
⚡ Performance Benchmarks (Total: 🟢 20 pass · 🟡 5 warn · 🔴 0 fail)

Baseline (latest main): 1716549 | Date: 6/29/2026 | Pipeline: 28761384891 | Baseline logs

Interaction Benchmarks · Samples: 5
Benchmarkchrome-webpackfirefox-webpack
loadNewAccount
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
🔴 load_new_account
confirmTx
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
bridgeUserActions
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]

📈 Results compared to the previous 5 runs on main

  • loadNewAccount/load_new_account: -26%
  • loadNewAccount/total: -26%
  • loadNewAccount/inp: -26%
  • loadNewAccount/fcp: -15%
  • loadNewAccount/lcp: +52%
  • confirmTx/longTaskTotalDuration: -21%
  • confirmTx/longTaskMaxDuration: -26%
  • confirmTx/tbt: -39%
  • confirmTx/inp: -18%
  • confirmTx/fcp: -24%
  • confirmTx/lcp: -10%
  • bridgeUserActions/bridge_load_page: -45%
  • bridgeUserActions/bridge_load_asset_picker: -40%
  • bridgeUserActions/longTaskCount: -17%
  • bridgeUserActions/longTaskTotalDuration: -42%
  • bridgeUserActions/longTaskMaxDuration: -35%
  • bridgeUserActions/tbt: -86%
  • bridgeUserActions/total: -12%
  • bridgeUserActions/inp: -31%
  • bridgeUserActions/fcp: -11%
  • bridgeUserActions/lcp: +45%
  • bridgeUserActions/cls: +100%
  • loadNewAccount/load_new_account: +586%
  • loadNewAccount/total: +586%
  • loadNewAccount/inp: -26%
  • loadNewAccount/fcp: -52%
  • loadNewAccount/lcp: +1081%
  • confirmTx/confirm_tx: +11%
  • confirmTx/longTaskCount: -100%
  • confirmTx/longTaskTotalDuration: -100%
  • confirmTx/longTaskMaxDuration: -100%
  • confirmTx/tbt: -100%
  • confirmTx/total: +11%
  • confirmTx/lcp: +1097%
  • bridgeUserActions/bridge_load_page: +78%
  • bridgeUserActions/bridge_load_asset_picker: +78%
  • bridgeUserActions/longTaskCount: -100%
  • bridgeUserActions/longTaskTotalDuration: -100%
  • bridgeUserActions/longTaskMaxDuration: -100%
  • bridgeUserActions/tbt: -100%
  • bridgeUserActions/total: +20%
  • bridgeUserActions/inp: -22%
  • bridgeUserActions/fcp: -47%
  • bridgeUserActions/lcp: +1172%
Startup Benchmarks · Samples: 100
Benchmarkchrome-webpackfirefox-webpack
startupStandardHome
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
startupPowerUserHome
[Sentry log · main/release]
🟡 [CI log]🟡 [CI log]

📈 Results compared to the previous 5 runs on main

  • startupStandardHome/setupStore: -15%
  • startupPowerUserHome/uiStartup: +29%
  • startupPowerUserHome/backgroundConnect: +285%
  • startupPowerUserHome/firstReactRender: -19%
  • startupPowerUserHome/setupStore: -16%
  • startupPowerUserHome/longTaskCount: -25%
  • startupPowerUserHome/longTaskTotalDuration: -19%
  • startupPowerUserHome/tbt: -17%
  • startupPowerUserHome/inp: -10%
  • startupPowerUserHome/lcp: +253%
  • startupStandardHome/domInteractive: -25%
  • startupStandardHome/fcp: -24%
  • startupPowerUserHome/fcp: +11%

🌐 Core Web Vitals — 🟢 good · 🟡 needs improvement · 🔴 poor (web.dev thresholds)

  • 🟡 startupPowerUserHome/INP: p75 488ms
  • 🟡 startupPowerUserHome/INP: p75 208ms
  • 🟡 startupPowerUserHome/LCP: p75 3.7s
User Journey Benchmarks · Samples: 5 · mock API
Benchmarkchrome-webpackfirefox-webpack
onboardingImportWallet
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
onboardingNewWallet
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
assetDetails
[Sentry log · main/release]
🟢 [CI log]🟡 [CI log]
solanaAssetDetails
[Sentry log · main/release]
🟢 [CI log]🟡 [CI log]
importSrpHome
[Sentry log · main/release]
🟢 [CI log]🟡 [CI log]
sendTransactions
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
swap
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]

📈 Results compared to the previous 5 runs on main

  • onboardingImportWallet/metricsToWalletReadyScreen: -12%
  • onboardingImportWallet/doneButtonToHomeScreen: -86%
  • onboardingImportWallet/openAccountMenuToAccountListLoaded: -63%
  • onboardingImportWallet/longTaskCount: -80%
  • onboardingImportWallet/longTaskTotalDuration: -89%
  • onboardingImportWallet/longTaskMaxDuration: -91%
  • onboardingImportWallet/tbt: -99%
  • onboardingImportWallet/total: -84%
  • onboardingNewWallet/doneButtonToAssetList: -32%
  • onboardingNewWallet/longTaskTotalDuration: -32%
  • onboardingNewWallet/longTaskMaxDuration: -22%
  • onboardingNewWallet/tbt: -56%
  • onboardingNewWallet/total: -27%
  • solanaAssetDetails/assetClickToPriceChart: -72%
  • solanaAssetDetails/total: -72%
  • solanaAssetDetails/inp: -31%
  • solanaAssetDetails/cls: +1620%
  • importSrpHome/loginToHomeScreen: -16%
  • importSrpHome/openAccountMenuAfterLogin: -15%
  • importSrpHome/homeAfterImportWithNewWallet: -16%
  • importSrpHome/longTaskCount: -19%
  • importSrpHome/longTaskTotalDuration: -31%
  • importSrpHome/longTaskMaxDuration: -22%
  • importSrpHome/tbt: -40%
  • importSrpHome/inp: -27%
  • importSrpHome/lcp: -63%
  • importSrpHome/cls: -30%
  • sendTransactions/openSendPageFromHome: -25%
  • sendTransactions/selectTokenToSendFormLoaded: -41%
  • sendTransactions/reviewTransactionToConfirmationPage: -22%
  • sendTransactions/longTaskCount: -20%
  • sendTransactions/longTaskTotalDuration: -31%
  • sendTransactions/longTaskMaxDuration: -17%
  • sendTransactions/tbt: -92%
  • sendTransactions/total: -22%
  • sendTransactions/fcp: -13%
  • sendTransactions/lcp: -16%
  • sendTransactions/cls: -40%

🌐 Core Web Vitals — 🟢 good · 🟡 needs improvement · 🔴 poor (web.dev thresholds)

  • 🟡 assetDetails/FCP: p75 1.9s
  • 🟡 solanaAssetDetails/FCP: p75 1.9s
  • 🟡 importSrpHome/FCP: p75 1.9s
Dapp Page Load Benchmarks · Samples: 100
Benchmarkchrome-webpack
dappPageLoad
[Sentry log · main/release]
🟢 [CI log]
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 3.13 MiB (29.07%)
  • ui: 79.9 KiB (0.46%)
  • common: 0 Bytes (0%)
  • other: 0 Bytes (0%)
  • contentScripts: 76.59 KiB (4.19%)
  • zip: 710.88 KiB (2.66%)

@seaona seaona left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add an e2e test to verify this? Maybe something similar as we had for lockdown-more?
Related conversation
https://consensys.slack.com/archives/CTQAGKY5V/p1781256822162049

@metamask-ci

metamask-ci Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor
Builds ready [4c9d190]
Deprecated Browserify fallback builds
⚡ Performance Benchmarks (Total: 🟢 14 pass · 🟡 9 warn · 🔴 1 fail)

Baseline (latest main): 1716549 | Date: 6/29/2026 | Pipeline: 29085285618 | Baseline logs

Metricschrome-webpackfirefox-webpack
onboardingNewWallet
[Sentry log · main/release]
🟢 [CI log]🔴 [CI log]

Regressions (🔴 1 failure)

Interaction Benchmarks · Samples: 5
Benchmarkchrome-webpackfirefox-webpack
loadNewAccount
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
confirmTx
[Sentry log · main/release]
🟢 [CI log]🟡 [CI log]
bridgeUserActions
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]

📈 Results compared to the previous 5 runs on main

  • loadNewAccount/load_new_account: -21%
  • loadNewAccount/total: -21%
  • loadNewAccount/inp: -26%
  • loadNewAccount/fcp: -10%
  • confirmTx/lcp: -11%
  • bridgeUserActions/bridge_load_page: -32%
  • bridgeUserActions/bridge_load_asset_picker: -15%
  • bridgeUserActions/longTaskCount: -17%
  • bridgeUserActions/longTaskTotalDuration: -25%
  • bridgeUserActions/longTaskMaxDuration: -15%
  • bridgeUserActions/tbt: -38%
  • bridgeUserActions/inp: -14%
  • loadNewAccount/load_new_account: +70%
  • loadNewAccount/total: +70%
  • loadNewAccount/inp: -18%
  • loadNewAccount/lcp: +1047%
  • confirmTx/confirm_tx: +11%
  • confirmTx/longTaskCount: -100%
  • confirmTx/longTaskTotalDuration: -100%
  • confirmTx/longTaskMaxDuration: -100%
  • confirmTx/tbt: -100%
  • confirmTx/total: +11%
  • confirmTx/lcp: +1121%
  • bridgeUserActions/bridge_load_page: +269%
  • bridgeUserActions/bridge_load_asset_picker: +151%
  • bridgeUserActions/longTaskCount: -100%
  • bridgeUserActions/longTaskTotalDuration: -100%
  • bridgeUserActions/longTaskMaxDuration: -100%
  • bridgeUserActions/tbt: -100%
  • bridgeUserActions/total: +184%
  • bridgeUserActions/inp: -22%
  • bridgeUserActions/fcp: -45%
  • bridgeUserActions/lcp: +1247%

🌐 Core Web Vitals — 🟢 good · 🟡 needs improvement · 🔴 poor (web.dev thresholds)

  • 🟡 confirmTx/FCP: p75 1.9s
Startup Benchmarks · Samples: 100
Benchmarkchrome-webpackfirefox-webpack
startupStandardHome
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
startupPowerUserHome
[Sentry log · main/release]
🟡 [CI log]

📈 Results compared to the previous 5 runs on main

  • startupStandardHome/setupStore: -15%
  • startupStandardHome/numNetworkReqs: +10%
  • startupStandardHome/domInteractive: +16%
  • startupStandardHome/setupStore: +30%
  • startupStandardHome/fcp: +14%
  • startupPowerUserHome/uiStartup: -21%
  • startupPowerUserHome/load: -11%
  • startupPowerUserHome/domContentLoaded: -12%
  • startupPowerUserHome/domInteractive: -45%
  • startupPowerUserHome/backgroundConnect: -45%
  • startupPowerUserHome/firstReactRender: -11%
  • startupPowerUserHome/loadScripts: -11%
  • startupPowerUserHome/setupStore: -21%
  • startupPowerUserHome/inp: -12%
  • startupPowerUserHome/fcp: -41%
  • startupPowerUserHome/lcp: -23%

🌐 Core Web Vitals — 🟢 good · 🟡 needs improvement · 🔴 poor (web.dev thresholds)

  • 🟡 startupPowerUserHome/LCP: p75 2.8s
User Journey Benchmarks · Samples: 5 · mock API 🔴 1
Benchmarkchrome-webpackfirefox-webpack
onboardingImportWallet
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
onboardingNewWallet
[Sentry log · main/release]
🟢 [CI log]🔴 [CI log]
🔴 total
assetDetails
[Sentry log · main/release]
🟡 [CI log]🟡 [CI log]
solanaAssetDetails
[Sentry log · main/release]
🟢 [CI log]🟡 [CI log]
importSrpHome
[Sentry log · main/release]
🟡 [CI log]🟡 [CI log]
sendTransactions
[Sentry log · main/release]
🟢 [CI log]🟡 [CI log]
swap
[Sentry log · main/release]
🟢 [CI log]🟡 [CI log]

📈 Results compared to the previous 5 runs on main

  • onboardingImportWallet/pwFormToMetricsScreen: +10%
  • onboardingImportWallet/metricsToWalletReadyScreen: -12%
  • onboardingImportWallet/doneButtonToHomeScreen: -86%
  • onboardingImportWallet/openAccountMenuToAccountListLoaded: -50%
  • onboardingImportWallet/longTaskCount: -80%
  • onboardingImportWallet/longTaskTotalDuration: -88%
  • onboardingImportWallet/longTaskMaxDuration: -88%
  • onboardingImportWallet/tbt: -96%
  • onboardingImportWallet/total: -83%
  • onboardingNewWallet/skipBackupToMetricsScreen: +19%
  • onboardingNewWallet/agreeButtonToOnboardingSuccess: +15%
  • onboardingNewWallet/doneButtonToAssetList: -16%
  • onboardingNewWallet/longTaskCount: -50%
  • onboardingNewWallet/longTaskTotalDuration: -71%
  • onboardingNewWallet/longTaskMaxDuration: -49%
  • onboardingNewWallet/tbt: -98%
  • onboardingNewWallet/total: -13%
  • solanaAssetDetails/assetClickToPriceChart: -53%
  • solanaAssetDetails/total: -53%
  • solanaAssetDetails/inp: -31%
  • importSrpHome/loginToHomeScreen: -10%
  • importSrpHome/homeAfterImportWithNewWallet: -21%
  • importSrpHome/longTaskCount: -35%
  • importSrpHome/longTaskTotalDuration: -43%
  • importSrpHome/tbt: -36%
  • importSrpHome/total: -17%
  • importSrpHome/inp: -12%
  • importSrpHome/lcp: -60%
  • importSrpHome/cls: -47%
  • sendTransactions/openSendPageFromHome: -31%
  • sendTransactions/selectTokenToSendFormLoaded: -49%
  • sendTransactions/reviewTransactionToConfirmationPage: +17%
  • sendTransactions/longTaskCount: -20%
  • sendTransactions/longTaskTotalDuration: -25%
  • sendTransactions/tbt: -47%
  • sendTransactions/total: +17%
  • sendTransactions/inp: -28%
  • sendTransactions/fcp: -15%
  • sendTransactions/lcp: -12%
  • sendTransactions/cls: -40%
  • swap/openSwapPageFromHome: -35%
  • swap/fetchAndDisplaySwapQuotes: +159%
  • swap/longTaskTotalDuration: -20%
  • swap/tbt: -84%
  • swap/total: +156%
  • swap/cls: -63%

🌐 Core Web Vitals — 🟢 good · 🟡 needs improvement · 🔴 poor (web.dev thresholds)

  • 🟡 assetDetails/FCP: p75 2.1s
  • 🟡 importSrpHome/INP: p75 232ms
  • 🟡 assetDetails/FCP: p75 1.9s
  • 🟡 solanaAssetDetails/FCP: p75 1.9s
  • 🟡 importSrpHome/FCP: p75 1.9s
  • 🟡 sendTransactions/FCP: p75 1.8s
  • 🔴 swap/INP: p75 728ms
  • 🟡 swap/FCP: p75 1.8s
Dapp Page Load Benchmarks · Samples: 100
Benchmarkchrome-webpack
dappPageLoad
[Sentry log · main/release]
🟢 [CI log]
Bundle sizes
  • background: 14.17 MiB
  • ui: 17.04 MiB
  • common: 0 Bytes
  • other: 998.07 KiB
  • contentScripts: 1.87 MiB
  • zip: 27.02 MiB
No matching bundle-size baseline was found in the history data, so diff values are omitted.

@naugtur

naugtur commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

I've built this branch and read some relevant files in dist.
I can confirm that the service-worker.js chunk is running outside LavaMoat but shipping the runtime and policies that apply to chunks it loads. With one exception that's a chunk where it seems the compilation put all of the css modules and i18n.
Reproduce for yourself if you want to fiddle with it:

$ grep -rL 'this.ST' dist/chrome/*.*.js
dist/chrome/7568.554899985993b3956e84.js
dist/chrome/bootstrap.3866ece2406bf05d7678.js
dist/chrome/runtime.f18d6d38a1d963aca582.js
dist/chrome/usb-permissions.edfad0af0c8d47903d06.js

the layer works as advertised and makes unwrapped copies of modules for service-worker.

One consequence of that is all of bootstrap goes into that layer too and there's a surprising depth of dependencies involved there. All of sentry makes sense, but some of the items I found there are not what we'd want duplicated

Here's a few references I found (ignore webpack specific underscores)

_metamask_base_controller__
_metamask_permission_controller__
_metamask_rpc_errors__
_metamask_snaps_rpc_methods__
_metamask_snaps_sdk__
_metamask_superstruct__
_metamask_utils__
👉 full list, somewhat messy includes all selectors from the service-worker chunk, so these are package names but also file names ``` _address_mjs__ _api_js__ _applyScopeDataToEvent_js__ _assert_mjs__ _asyncContext_index_js__ _backgroundtab_js__ _baggage_js__ _BaseController_mjs__ _breadcrumbs_js__ _browser_js__ _button_mjs__ _carrier_js__ _classes_mjs__ _client_js__ _cls_js__ _component_mjs__ _components_index_mjs__ _constants_js__ _conversions_js__ _copyable_mjs__ _currentScopes_js__ _datetime_js__ _debug_build_js__ _defaultScopes_js__ _digits_js__ _divider_mjs__ _dsn_js__ _duration_js__ _dynamicSamplingContext_js__ _english_js__ _envelope_js__ _env_js__ _error_constants_mjs__ _error_js__ _error_mjs__ _errors_js__ _errors_mjs__ _eventbuilder_js__ _eventProcessors_js__ _exports_js__ _extractEthjsErrorMessage__ _feedbackAsync_js__ _feedbackSync_js__ _fetch_js__ _formats_js__ _formatter_js__ _form_mjs__ _generateUniqueID_js__ _getActivationStart_js__ _getNavigationEntry_js__ _handlers_index_mjs__ _handlers_js__ _heading_mjs__ _helpers_js__ _image_mjs__ _impl_conversions_js__ _impl_diff_js__ _impl_digits_js__ _impl_formats_js__ _impl_formatter_js__ _impl_invalid_js__ _impl_locale_js__ _impl_regexParser_js__ _impl_tokenParser_js__ _impl_util_js__ _impl_zoneUtil_js__ _info_js__ _input_mjs__ _install_type__ _instance_js__ _instrument_js__ _integration_js__ _integrations_breadcrumbs_js__ _integrations_browserapierrors_js__ _integrations_contextlines_js__ _integrations_globalhandlers_js__ _integrations_httpclient_js__ _integrations_httpcontext_js__ _integrations_linkederrors_js__ _integrations_reportingobserver_js__ _internals_index_mjs__ _interval_js__ _invalid_js__ _is_js__ _jsx_index_mjs__ _lib_bindReporter_js__ _lib_getActivationStart_js__ _lib_getNavigationEntry_js__ _lib_getVisibilityWatcher_js__ _lib_initMetric_js__ _lib_observe_js__ _lib_onHidden_js__ _lib_polyfills_interactionCountPolyfill_js__ _lib_runOnce_js__ _lib_setupSentry__ _lib_whenActivated_js__ _logger_js__ _logSpans_js__ _measurement_js__ _memo_js__ _metadata_js__ _metamask_base_controller__ _metamask_permission_controller__ _metamask_rpc_errors__ _metamask_snaps_rpc_methods__ _metamask_snaps_sdk__ _metamask_superstruct__ _metamask_utils__ _metrics_js__ _metrics_metric_summary_js__ _misc_js__ _misc_mjs__ _noble_hashes_sha3__ _node_js__ _nodes_mjs__ _normalize_js__ _object_js__ _observe_js__ _onFCP_js__ _permissions__ _polyfill__ _profiling_integration_js__ _regex_js__ _request_js__ _rng_js__ _row_mjs__ _safe_stringify__ _sampling_js__ _scope_js__ _scripts_lib_extension_lazy_listener_extension_lazy_listener__ _scripts_load_bootstrap__ _sdk_js__ _semanticAttributes_js__ _sentry_browser__ _sentry_core__ _sentry_get_state__ _sentry_internal_browser_utils__ _sentry_internal_feedback__ _sentry_internal_replay__ _sentry_internal_replay_canvas__ _sentry_make_transport__ _sentry_metametrics__ _sentryNonRecordingSpan_js__ _sentrySpan_js__ _sentry_trace_propagation__ _sentry_utils__ _session_js__ _settings_js__ _shared_constants_app__ _shared_constants_ui_initialization__ _shared_lib_manifestFlags__ _shared_lib_mv3_utils__ _shared_lib_sentry__ _shared_lib_sentry_release__ _snaps_permissions__ _spanOnScope_js__ _spanstatus_js__ _spanUtils_js__ _spinner_mjs__ _stack_parsers_js__ _stackStrategy_js__ _stacktrace_js__ _startProfileForSpan_js__ _stringify_js__ _string_js__ _struct_mjs__ _structs_mjs__ _superstruct_mjs__ _supports_js__ _syncpromise_js__ _text_mjs__ _time_js__ _trace_js__ _tracing_browserTracingIntegration_js__ _tracing_dynamicSamplingContext_js__ _tracing_request_js__ _tracing_sentryNonRecordingSpan_js__ _tracing_spanstatus_js__ _tracing_trace_js__ _transports_fetch_js__ _transports_offline_js__ _types_index_mjs__ _types_js__ _types_mjs__ _u64_js__ _ui_index_mjs__ _userfeedback_js__ _utilities_mjs__ _util_js__ _utils_handleCallbackErrors_js__ _utils_hasTracingEnabled_js__ _utils_js__ _utils_lazyLoadIntegration_js__ _utils_mjs__ _utils_parseSampleRate_js__ _utils_prepareEvent_js__ _utils_spanOnScope_js__ _utils_spanUtils_js__ _validate_js__ _version_js__ _web_vitals_getCLS_js__ _web_vitals_getFID_js__ _web_vitals_getINP_js__ _web_vitals_getLCP_js__ _web_vitals_lib_getActivationStart_js__ _web_vitals_lib_getNavigationEntry_js__ _web_vitals_lib_getVisibilityWatcher_js__ _web_vitals_lib_observe_js__ _web_vitals_lib_onHidden_js__ _web_vitals_onTTFB_js__ _worldwide_js__ _zone_js__ _zones_fixedOffsetZone_js__ _zones_IANAZone_js__ _zones_invalidZone_js__ _zones_systemZone_js__

one possible culprit is that @metamask/utils is not designed to be treeshakeable and it's used for a few types and one logger utility in sentry setup.

I have no idea what pulls snaps stuff in.

potential solutions

1 - rule + exclude loader

Instead of creating a layer, we could use the excludeLoader in rules and target the modules we want to work outside LavaMoat protections, but let them use the same protected dependencies.

  • pros: less duplication, no need to manually prune what sentry setup pulls in, no risk of breaking behavior that relies on packages being singletons (I don't know if we rely on that for sentry stuff)
  • cons: major sentry updates might change their dependency structure and require adjustments to the exclude rules, if exclude rules are too naive, they could match something they shouldn't

2 - manual treeshaking

Tweak the bootstrap to drop some heavy dependencies

  • pros: can keep the cleaner exclude definition with a layer
  • cons: potentially more work and easiwe for dev to regress on.

Note: it's likely we pulled the same packages in the browserify build. Doesn't mean it's good :)

Gudahtt
Gudahtt previously approved these changes Jul 10, 2026

@Gudahtt Gudahtt left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

I've reviewed @naugtur's findings, we will follow-up in a later PR

davidmurdoch
davidmurdoch previously approved these changes Jul 10, 2026

@davidmurdoch davidmurdoch left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good for now. i think we can improve things later.

@github-project-automation github-project-automation Bot moved this from Needs dev review to Review finalised - Ready to be merged in PR review queue Jul 10, 2026
@Gudahtt
Gudahtt added this pull request to the merge queue Jul 10, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jul 10, 2026
@metamask-ci

metamask-ci Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor
Builds ready [9031555]
Deprecated Browserify fallback builds
⚡ Performance Benchmarks (Total: 🟢 10 pass · 🟡 8 warn · 🔴 1 fail)

Baseline (latest main): 1716549 | Date: 6/29/2026 | Pipeline: 29114677640 | Baseline logs

Metricschrome-webpackfirefox-webpack
onboardingNewWallet
[Sentry log · main/release]
🟢 [CI log]🔴 [CI log]

Regressions (🔴 1 failure)

Interaction Benchmarks · Samples: 5
Benchmarkchrome-webpackfirefox-webpack
loadNewAccount
[Sentry log · main/release]
🟢 [CI log]🟡 [CI log]
confirmTx
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
bridgeUserActions
[Sentry log · main/release]
🟢 [CI log]🟡 [CI log]
🔴 bridge_load_page

📈 Results compared to the previous 5 runs on main

  • loadNewAccount/load_new_account: +165%
  • loadNewAccount/total: +165%
  • loadNewAccount/inp: -18%
  • loadNewAccount/lcp: +1103%
  • confirmTx/confirm_tx: +13%
  • confirmTx/longTaskCount: -100%
  • confirmTx/longTaskTotalDuration: -100%
  • confirmTx/longTaskMaxDuration: -100%
  • confirmTx/tbt: -100%
  • confirmTx/total: +13%
  • confirmTx/fcp: -44%
  • confirmTx/lcp: +1227%
  • bridgeUserActions/bridge_load_page: +1424%
  • bridgeUserActions/bridge_load_asset_picker: +98%
  • bridgeUserActions/longTaskCount: -100%
  • bridgeUserActions/longTaskTotalDuration: -100%
  • bridgeUserActions/longTaskMaxDuration: -100%
  • bridgeUserActions/tbt: -100%
  • bridgeUserActions/total: +195%
  • bridgeUserActions/inp: -14%
  • bridgeUserActions/fcp: +12%
  • bridgeUserActions/lcp: +1263%

🌐 Core Web Vitals — 🟢 good · 🟡 needs improvement · 🔴 poor (web.dev thresholds)

  • 🟡 loadNewAccount/FCP: p75 1.9s
  • 🟡 bridgeUserActions/FCP: p75 1.9s
Startup Benchmarks · Samples: 100
Benchmarkfirefox-webpack
startupStandardHome
[Sentry log · main/release]
🟢 [CI log]
startupPowerUserHome
[Sentry log · main/release]
🟡 [CI log]

📈 Results compared to the previous 5 runs on main

  • startupStandardHome/domInteractive: -23%
  • startupStandardHome/firstReactRender: +317%
  • startupStandardHome/setupStore: +38%
  • startupStandardHome/fcp: -22%
  • startupPowerUserHome/domInteractive: -43%
  • startupPowerUserHome/backgroundConnect: -13%
  • startupPowerUserHome/firstReactRender: +632%
  • startupPowerUserHome/inp: -12%
  • startupPowerUserHome/fcp: -42%

🌐 Core Web Vitals — 🟢 good · 🟡 needs improvement · 🔴 poor (web.dev thresholds)

  • 🟡 startupPowerUserHome/LCP: p75 3.4s
User Journey Benchmarks · Samples: 5 · mock API 🔴 1

⚠️ Missing data: chrome/webpack/userJourneyAssets, chrome/webpack/userJourneyTransactions

Benchmarkchrome-webpackfirefox-webpack
onboardingImportWallet
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
onboardingNewWallet
[Sentry log · main/release]
🟢 [CI log]🔴 [CI log]
🔴 total
importSrpHome
[Sentry log · main/release]
🟢 [CI log]🟡 [CI log]
assetDetails
[Sentry log · main/release]
🟡 [CI log]
solanaAssetDetails
[Sentry log · main/release]
🟡 [CI log]
sendTransactions
[Sentry log · main/release]
🟡 [CI log]
swap
[Sentry log · main/release]
🟡 [CI log]

🌐 Core Web Vitals — 🟢 good · 🟡 needs improvement · 🔴 poor (web.dev thresholds)

  • 🟡 assetDetails/FCP: p75 2.0s
  • 🟡 solanaAssetDetails/FCP: p75 2.0s
  • 🟡 importSrpHome/FCP: p75 1.9s
  • 🟡 sendTransactions/FCP: p75 1.9s
  • 🟡 swap/FCP: p75 2.0s
Dapp Page Load Benchmarks · Samples: 100
Benchmarkchrome-webpack
dappPageLoad
[Sentry log · main/release]
🟢 [CI log]

📈 Results compared to the previous 5 runs on main

  • dappPageLoad/pageLoadTime: -25%
  • dappPageLoad/domContentLoaded: -20%
  • dappPageLoad/firstPaint: -14%
  • dappPageLoad/firstContentfulPaint: -14%
Bundle sizes
  • background: 14.21 MiB
  • ui: 17.04 MiB
  • common: 0 Bytes
  • other: 998.07 KiB
  • contentScripts: 1.87 MiB
  • zip: 27.02 MiB
No matching bundle-size baseline was found in the history data, so diff values are omitted.

…ackground-wrapping

* origin/main:
  feat(ramps): add ramps controller hooks and selectors (#43963)
  fix: Fix Firefox detatched-window memory leak by avoiding `DocumentPictureInPicture` instantiation in Snow hook cp-13.39.2 (#44352)
  feat: support Robinhood chain on swaps cp-13.39.1 (#44347)
@Gudahtt
Gudahtt dismissed stale reviews from davidmurdoch and themself via eb17c5c July 10, 2026 20:33
@Gudahtt

Gudahtt commented Jul 10, 2026

Copy link
Copy Markdown
Member

Just pushed a merge commit to resolve some policy conflicts.

The conflicts were just with the entries for our own packages, here is the diff:
diff --cc lavamoat/webpack/mv3/beta/policy.json
index b2f09502832,c28dd263a70..00000000000
--- a/lavamoat/webpack/mv3/beta/policy.json
+++ b/lavamoat/webpack/mv3/beta/policy.json
@@@ -2239,22 -1380,12 +2239,32 @@@
          "@metamask/providers>readable-stream": true
        }
      },
-     "@metamask/transaction-pay-controller>@metamask/ramps-controller": {
+     "@metamask/ramps-controller": {
        "globals": {
--        "URL": true
++        "AbortController": true,
++        "URL": true,
++        "clearInterval": true,
++        "setInterval": true
++      },
++      "meta": {
++        "webpack-optimization": [
++          "Dependency '@metamask/controller-utils' reexports from 'cockatiel' and webpack collapsed that to a direct import."
++        ]
        },
        "packages": {
--        "@metamask/controller-utils": true
++        "@metamask/base-controller": true,
++        "@metamask/controller-utils": true,
++        "cockatiel": true
 +      }
 +    },
 +    "@metamask/rate-limit-controller": {
 +      "globals": {
 +        "setTimeout": true
 +      },
 +      "packages": {
 +        "@metamask/base-controller": true,
 +        "@metamask/rpc-errors": true,
 +        "@metamask/utils": true
        }
      },
      "@metamask/react-data-query": {
@@@ -2591,46 -1521,8 +2601,46 @@@
        }
      },
      "@metamask/transaction-pay-controller": {
 +      "globals": {
 +        "AbortController": true,
 +        "URLSearchParams": true,
 +        "clearTimeout": true,
 +        "fetch": true,
 +        "setTimeout": true
 +      },
 +      "packages": {
 +        "@ethersproject/abi": true,
 +        "@metamask/base-controller": true,
 +        "@metamask/bridge-controller": true,
 +        "@metamask/controller-utils": true,
 +        "@metamask/keyring-controller": true,
 +        "@metamask/metamask-eth-abis": true,
 +        "@metamask/network-controller": true,
-         "@metamask/transaction-pay-controller>@metamask/ramps-controller": true,
++        "@metamask/ramps-controller": true,
 +        "@metamask/transaction-controller": true,
 +        "@metamask/utils": true,
 +        "@metamask/transaction-pay-controller>bignumber.js": true,
 +        "lodash": true
 +      }
 +    },
 +    "@metamask/user-operation-controller": {
 +      "globals": {
 +        "fetch": true
 +      },
        "packages": {
 -        "@metamask/transaction-controller": true
 +        "@metamask/base-controller": true,
 +        "@metamask/controller-utils": true,
 +        "@metamask/controller-utils>@metamask/eth-query": true,
 +        "@metamask/gas-fee-controller": true,
 +        "@metamask/polling-controller": true,
 +        "@metamask/rpc-errors": true,
 +        "@metamask/superstruct": true,
 +        "@metamask/transaction-controller": true,
 +        "@metamask/utils": true,
 +        "ethereumjs-util>bn.js": true,
 +        "webpack>events": true,
 +        "lodash": true,
 +        "uuid": true
        }
      },
      "@metamask/utils": {
diff --cc lavamoat/webpack/mv3/experimental/policy.json
index b2f09502832,c28dd263a70..00000000000
--- a/lavamoat/webpack/mv3/experimental/policy.json
+++ b/lavamoat/webpack/mv3/experimental/policy.json
@@@ -2239,22 -1380,12 +2239,32 @@@
          "@metamask/providers>readable-stream": true
        }
      },
-     "@metamask/transaction-pay-controller>@metamask/ramps-controller": {
+     "@metamask/ramps-controller": {
        "globals": {
--        "URL": true
++        "AbortController": true,
++        "URL": true,
++        "clearInterval": true,
++        "setInterval": true
++      },
++      "meta": {
++        "webpack-optimization": [
++          "Dependency '@metamask/controller-utils' reexports from 'cockatiel' and webpack collapsed that to a direct import."
++        ]
        },
        "packages": {
--        "@metamask/controller-utils": true
++        "@metamask/base-controller": true,
++        "@metamask/controller-utils": true,
++        "cockatiel": true
 +      }
 +    },
 +    "@metamask/rate-limit-controller": {
 +      "globals": {
 +        "setTimeout": true
 +      },
 +      "packages": {
 +        "@metamask/base-controller": true,
 +        "@metamask/rpc-errors": true,
 +        "@metamask/utils": true
        }
      },
      "@metamask/react-data-query": {
@@@ -2591,46 -1521,8 +2601,46 @@@
        }
      },
      "@metamask/transaction-pay-controller": {
 +      "globals": {
 +        "AbortController": true,
 +        "URLSearchParams": true,
 +        "clearTimeout": true,
 +        "fetch": true,
 +        "setTimeout": true
 +      },
 +      "packages": {
 +        "@ethersproject/abi": true,
 +        "@metamask/base-controller": true,
 +        "@metamask/bridge-controller": true,
 +        "@metamask/controller-utils": true,
 +        "@metamask/keyring-controller": true,
 +        "@metamask/metamask-eth-abis": true,
 +        "@metamask/network-controller": true,
-         "@metamask/transaction-pay-controller>@metamask/ramps-controller": true,
++        "@metamask/ramps-controller": true,
 +        "@metamask/transaction-controller": true,
 +        "@metamask/utils": true,
 +        "@metamask/transaction-pay-controller>bignumber.js": true,
 +        "lodash": true
 +      }
 +    },
 +    "@metamask/user-operation-controller": {
 +      "globals": {
 +        "fetch": true
 +      },
        "packages": {
 -        "@metamask/transaction-controller": true
 +        "@metamask/base-controller": true,
 +        "@metamask/controller-utils": true,
 +        "@metamask/controller-utils>@metamask/eth-query": true,
 +        "@metamask/gas-fee-controller": true,
 +        "@metamask/polling-controller": true,
 +        "@metamask/rpc-errors": true,
 +        "@metamask/superstruct": true,
 +        "@metamask/transaction-controller": true,
 +        "@metamask/utils": true,
 +        "ethereumjs-util>bn.js": true,
 +        "webpack>events": true,
 +        "lodash": true,
 +        "uuid": true
        }
      },
      "@metamask/utils": {
diff --cc lavamoat/webpack/mv3/flask/policy.json
index b2f09502832,c28dd263a70..00000000000
--- a/lavamoat/webpack/mv3/flask/policy.json
+++ b/lavamoat/webpack/mv3/flask/policy.json
@@@ -2239,22 -1380,12 +2239,32 @@@
          "@metamask/providers>readable-stream": true
        }
      },
-     "@metamask/transaction-pay-controller>@metamask/ramps-controller": {
+     "@metamask/ramps-controller": {
        "globals": {
--        "URL": true
++        "AbortController": true,
++        "URL": true,
++        "clearInterval": true,
++        "setInterval": true
++      },
++      "meta": {
++        "webpack-optimization": [
++          "Dependency '@metamask/controller-utils' reexports from 'cockatiel' and webpack collapsed that to a direct import."
++        ]
        },
        "packages": {
--        "@metamask/controller-utils": true
++        "@metamask/base-controller": true,
++        "@metamask/controller-utils": true,
++        "cockatiel": true
 +      }
 +    },
 +    "@metamask/rate-limit-controller": {
 +      "globals": {
 +        "setTimeout": true
 +      },
 +      "packages": {
 +        "@metamask/base-controller": true,
 +        "@metamask/rpc-errors": true,
 +        "@metamask/utils": true
        }
      },
      "@metamask/react-data-query": {
@@@ -2591,46 -1521,8 +2601,46 @@@
        }
      },
      "@metamask/transaction-pay-controller": {
 +      "globals": {
 +        "AbortController": true,
 +        "URLSearchParams": true,
 +        "clearTimeout": true,
 +        "fetch": true,
 +        "setTimeout": true
 +      },
 +      "packages": {
 +        "@ethersproject/abi": true,
 +        "@metamask/base-controller": true,
 +        "@metamask/bridge-controller": true,
 +        "@metamask/controller-utils": true,
 +        "@metamask/keyring-controller": true,
 +        "@metamask/metamask-eth-abis": true,
 +        "@metamask/network-controller": true,
-         "@metamask/transaction-pay-controller>@metamask/ramps-controller": true,
++        "@metamask/ramps-controller": true,
 +        "@metamask/transaction-controller": true,
 +        "@metamask/utils": true,
 +        "@metamask/transaction-pay-controller>bignumber.js": true,
 +        "lodash": true
 +      }
 +    },
 +    "@metamask/user-operation-controller": {
 +      "globals": {
 +        "fetch": true
 +      },
        "packages": {
 -        "@metamask/transaction-controller": true
 +        "@metamask/base-controller": true,
 +        "@metamask/controller-utils": true,
 +        "@metamask/controller-utils>@metamask/eth-query": true,
 +        "@metamask/gas-fee-controller": true,
 +        "@metamask/polling-controller": true,
 +        "@metamask/rpc-errors": true,
 +        "@metamask/superstruct": true,
 +        "@metamask/transaction-controller": true,
 +        "@metamask/utils": true,
 +        "ethereumjs-util>bn.js": true,
 +        "webpack>events": true,
 +        "lodash": true,
 +        "uuid": true
        }
      },
      "@metamask/utils": {
diff --cc lavamoat/webpack/mv3/main/policy.json
index b2f09502832,c28dd263a70..00000000000
--- a/lavamoat/webpack/mv3/main/policy.json
+++ b/lavamoat/webpack/mv3/main/policy.json
@@@ -2239,22 -1380,12 +2239,32 @@@
          "@metamask/providers>readable-stream": true
        }
      },
-     "@metamask/transaction-pay-controller>@metamask/ramps-controller": {
+     "@metamask/ramps-controller": {
        "globals": {
--        "URL": true
++        "AbortController": true,
++        "URL": true,
++        "clearInterval": true,
++        "setInterval": true
++      },
++      "meta": {
++        "webpack-optimization": [
++          "Dependency '@metamask/controller-utils' reexports from 'cockatiel' and webpack collapsed that to a direct import."
++        ]
        },
        "packages": {
--        "@metamask/controller-utils": true
++        "@metamask/base-controller": true,
++        "@metamask/controller-utils": true,
++        "cockatiel": true
 +      }
 +    },
 +    "@metamask/rate-limit-controller": {
 +      "globals": {
 +        "setTimeout": true
 +      },
 +      "packages": {
 +        "@metamask/base-controller": true,
 +        "@metamask/rpc-errors": true,
 +        "@metamask/utils": true
        }
      },
      "@metamask/react-data-query": {
@@@ -2591,46 -1521,8 +2601,46 @@@
        }
      },
      "@metamask/transaction-pay-controller": {
 +      "globals": {
 +        "AbortController": true,
 +        "URLSearchParams": true,
 +        "clearTimeout": true,
 +        "fetch": true,
 +        "setTimeout": true
 +      },
 +      "packages": {
 +        "@ethersproject/abi": true,
 +        "@metamask/base-controller": true,
 +        "@metamask/bridge-controller": true,
 +        "@metamask/controller-utils": true,
 +        "@metamask/keyring-controller": true,
 +        "@metamask/metamask-eth-abis": true,
 +        "@metamask/network-controller": true,
-         "@metamask/transaction-pay-controller>@metamask/ramps-controller": true,
++        "@metamask/ramps-controller": true,
 +        "@metamask/transaction-controller": true,
 +        "@metamask/utils": true,
 +        "@metamask/transaction-pay-controller>bignumber.js": true,
 +        "lodash": true
 +      }
 +    },
 +    "@metamask/user-operation-controller": {
 +      "globals": {
 +        "fetch": true
 +      },
        "packages": {
 -        "@metamask/transaction-controller": true
 +        "@metamask/base-controller": true,
 +        "@metamask/controller-utils": true,
 +        "@metamask/controller-utils>@metamask/eth-query": true,
 +        "@metamask/gas-fee-controller": true,
 +        "@metamask/polling-controller": true,
 +        "@metamask/rpc-errors": true,
 +        "@metamask/superstruct": true,
 +        "@metamask/transaction-controller": true,
 +        "@metamask/utils": true,
 +        "ethereumjs-util>bn.js": true,
 +        "webpack>events": true,
 +        "lodash": true,
 +        "uuid": true
        }
      },
      "@metamask/utils": {

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
69.4% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@metamask-ci

metamask-ci Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor
Builds ready [eb17c5c]
Deprecated Browserify fallback builds
⚡ Performance Benchmarks (Total: 🟢 14 pass · 🟡 8 warn · 🔴 2 fail)

Baseline (latest main): 1716549 | Date: 6/29/2026 | Pipeline: 29121792607 | Baseline logs

Metricschrome-webpackfirefox-webpack
loadNewAccount
[Sentry log · main/release]
🟢 [CI log]🔴 load_new_account(p95) [CI log]
onboardingNewWallet
[Sentry log · main/release]
🟢 [CI log]🔴 [CI log]

Regressions (🔴 2 failures)

Interaction Benchmarks · Samples: 5 🔴 1
Benchmarkchrome-webpackfirefox-webpack
loadNewAccount
[Sentry log · main/release]
🟢 [CI log]🔴 [CI log]
🔴 load_new_account
confirmTx
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
bridgeUserActions
[Sentry log · main/release]
🟡 [CI log]🟢 [CI log]

📈 Results compared to the previous 5 runs on main

  • loadNewAccount/load_new_account: -22%
  • loadNewAccount/total: -22%
  • confirmTx/tbt: +21%
  • confirmTx/inp: +22%
  • bridgeUserActions/bridge_load_page: -14%
  • bridgeUserActions/bridge_load_asset_picker: +32%
  • bridgeUserActions/bridge_search_token: +12%
  • bridgeUserActions/longTaskCount: +67%
  • bridgeUserActions/longTaskTotalDuration: +59%
  • bridgeUserActions/tbt: +48%
  • loadNewAccount/load_new_account: +686%
  • loadNewAccount/total: +686%
  • loadNewAccount/inp: +113%
  • loadNewAccount/fcp: -54%
  • loadNewAccount/lcp: +977%
  • confirmTx/confirm_tx: +10%
  • confirmTx/longTaskCount: -100%
  • confirmTx/longTaskTotalDuration: -100%
  • confirmTx/longTaskMaxDuration: -100%
  • confirmTx/tbt: -100%
  • confirmTx/total: +10%
  • confirmTx/inp: -18%
  • confirmTx/lcp: +1092%
  • bridgeUserActions/bridge_load_page: +85%
  • bridgeUserActions/bridge_load_asset_picker: +63%
  • bridgeUserActions/longTaskCount: -100%
  • bridgeUserActions/longTaskTotalDuration: -100%
  • bridgeUserActions/longTaskMaxDuration: -100%
  • bridgeUserActions/tbt: -100%
  • bridgeUserActions/total: +17%
  • bridgeUserActions/inp: -14%
  • bridgeUserActions/fcp: -50%
  • bridgeUserActions/lcp: +1144%

🌐 Core Web Vitals — 🟢 good · 🟡 needs improvement · 🔴 poor (web.dev thresholds)

  • 🟡 bridgeUserActions/FCP: p75 1.8s
  • 🟡 loadNewAccount/INP: p75 208ms
Startup Benchmarks · Samples: 100
Benchmarkchrome-webpackfirefox-webpack
startupStandardHome
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
startupPowerUserHome
[Sentry log · main/release]
🟡 [CI log]

📈 Results compared to the previous 5 runs on main

  • startupStandardHome/uiStartup: +20%
  • startupStandardHome/backgroundConnect: +12%
  • startupStandardHome/firstReactRender: +203%
  • startupStandardHome/numNetworkReqs: +50%
  • startupStandardHome/firstReactRender: +344%
  • startupStandardHome/setupStore: +45%
  • startupPowerUserHome/domInteractive: -31%
  • startupPowerUserHome/backgroundConnect: -22%
  • startupPowerUserHome/firstReactRender: +617%
  • startupPowerUserHome/inp: +13%
  • startupPowerUserHome/fcp: -29%

🌐 Core Web Vitals — 🟢 good · 🟡 needs improvement · 🔴 poor (web.dev thresholds)

  • 🟡 startupPowerUserHome/INP: p75 248ms
  • 🟡 startupPowerUserHome/LCP: p75 3.7s
User Journey Benchmarks · Samples: 5 · mock API 🔴 1
Benchmarkchrome-webpackfirefox-webpack
onboardingImportWallet
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
onboardingNewWallet
[Sentry log · main/release]
🟢 [CI log]🔴 [CI log]
🔴 total
assetDetails
[Sentry log · main/release]
🟡 [CI log]🟡 [CI log]
solanaAssetDetails
[Sentry log · main/release]
🟢 [CI log]🟡 [CI log]
importSrpHome
[Sentry log · main/release]
🟡 [CI log]🟡 [CI log]
sendTransactions
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
swap
[Sentry log · main/release]
🟢 [CI log]🟡 [CI log]

📈 Results compared to the previous 5 runs on main

  • onboardingImportWallet/srpButtonToSrpForm: +25%
  • onboardingImportWallet/metricsToWalletReadyScreen: -11%
  • onboardingImportWallet/doneButtonToHomeScreen: -86%
  • onboardingImportWallet/openAccountMenuToAccountListLoaded: -50%
  • onboardingImportWallet/longTaskCount: -100%
  • onboardingImportWallet/longTaskTotalDuration: -100%
  • onboardingImportWallet/longTaskMaxDuration: -100%
  • onboardingImportWallet/tbt: -100%
  • onboardingImportWallet/total: -83%
  • onboardingNewWallet/agreeButtonToOnboardingSuccess: +10%
  • onboardingNewWallet/doneButtonToAssetList: -12%
  • onboardingNewWallet/longTaskCount: -50%
  • onboardingNewWallet/longTaskTotalDuration: -64%
  • onboardingNewWallet/longTaskMaxDuration: -37%
  • onboardingNewWallet/tbt: -79%
  • onboardingNewWallet/total: -10%
  • solanaAssetDetails/assetClickToPriceChart: -42%
  • solanaAssetDetails/total: -42%
  • solanaAssetDetails/inp: -24%
  • solanaAssetDetails/lcp: +16%
  • importSrpHome/loginToHomeScreen: -19%
  • importSrpHome/openAccountMenuAfterLogin: -37%
  • importSrpHome/homeAfterImportWithNewWallet: -26%
  • importSrpHome/longTaskCount: -44%
  • importSrpHome/longTaskTotalDuration: -40%
  • importSrpHome/longTaskMaxDuration: +21%
  • importSrpHome/tbt: -36%
  • importSrpHome/total: -24%
  • importSrpHome/lcp: -61%
  • importSrpHome/cls: -47%
  • sendTransactions/openSendPageFromHome: -25%
  • sendTransactions/selectTokenToSendFormLoaded: -60%
  • sendTransactions/reviewTransactionToConfirmationPage: -44%
  • sendTransactions/longTaskCount: -20%
  • sendTransactions/longTaskTotalDuration: -23%
  • sendTransactions/tbt: -40%
  • sendTransactions/total: -42%
  • sendTransactions/inp: -19%
  • sendTransactions/fcp: -14%
  • sendTransactions/cls: -40%
  • swap/openSwapPageFromHome: -30%
  • swap/fetchAndDisplaySwapQuotes: +121%
  • swap/longTaskTotalDuration: -13%
  • swap/tbt: -51%
  • swap/total: +119%
  • swap/cls: -63%

🌐 Core Web Vitals — 🟢 good · 🟡 needs improvement · 🔴 poor (web.dev thresholds)

  • 🟡 assetDetails/FCP: p75 2.1s
  • 🟡 importSrpHome/INP: p75 288ms
  • 🟡 assetDetails/FCP: p75 1.9s
  • 🟡 solanaAssetDetails/FCP: p75 1.9s
  • 🟡 importSrpHome/FCP: p75 1.9s
  • 🟡 swap/FCP: p75 1.8s
Dapp Page Load Benchmarks · Samples: 100
Benchmarkchrome-webpack
dappPageLoad
[Sentry log · main/release]
🟢 [CI log]
Bundle sizes
  • background: 14.22 MiB
  • ui: 17.06 MiB
  • common: 0 Bytes
  • other: 998.07 KiB
  • contentScripts: 1.87 MiB
  • zip: 27.03 MiB
No matching bundle-size baseline was found in the history data, so diff values are omitted.

@HowardBraham
HowardBraham added this pull request to the merge queue Jul 10, 2026
@HowardBraham HowardBraham added the retry-ci Tells GitHub Actions to retry failed jobs, label removed automatically before the retry label Jul 10, 2026
Merged via the queue into main with commit 84145c4 Jul 10, 2026
390 of 393 checks passed
@HowardBraham
HowardBraham deleted the fix/mv3-sw-lavamoat-background-wrapping branch July 10, 2026 22:25
@gauthierpetetin

Copy link
Copy Markdown
Contributor

Missing release label release-13.40.0 on PR. Adding release label release-13.40.0 on PR and removing other release labels(release-13.41.0), as PR was added to branch 13.40.0 when release was cut.

@MajorLift

MajorLift commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

🧪 Validation Run

Verdict: ✅ the wrapping is real and adds no capability mv2 did not already grant — two hand-written entries are the exception — Claim: wrapping the mv3 background under LavaMoat brings it into containment. head 84145c408f1 · 2026-08-02 · dependency-containment check

Note

Trial run of the MetaMask evidence skills,
using the LavaMoat policy-diligence skill
feedback welcome, on the finding or on whether this format is useful to a reviewer.
Not a review verdict; nothing here blocks the PR.

mv3/main goes from 494 to 651 contained packages. Every capability arriving with them is compared against mv2/main, which already contains 639.

converges on the mv2 baseline; two hand-written entries are this change's own (exit 0)

Claim under test: whether the mv3 containment surface grows into capabilities mv2 already grants, or into new ones

$ sh -c git show 3d173df60c56c852104ce473e51044010a582561:lavamoat/webpack/mv3/main/policy.json > evidence-artifacts/mv3-base-policy.json && git show 3d173df60c56c852104ce473e51044010a582561:lavamoat/webpack/mv3/main/policy-override.json > evidence-artifacts/mv3-base-override.json && python3 .evidence-skills/domains/security/skills/lavamoat-policy-diligence/scripts/policy-converge.py --base evidence-artifacts/mv3-base-policy.json --head lavamoat/webpack/mv3/main/policy.json --reference lavamoat/webpack/mv2/main/policy.json --override-base evidence-artifacts/mv3-base-override.json --override-head lavamoat/webpack/mv3/main/policy-override.json --override-path lavamoat/webpack/mv3/main/policy-override.json
CONTAINMENT CONVERGENCE
==========================================================================
contained packages   base   494  ->  head   651
reference policy     lavamoat/webpack/mv2/main/policy.json  (639 packages)

grants new to head between base and head:  1116
  of those, already granted in reference:  1116
  new to head and ABSENT from reference:   0

None. Every capability the head policy gains is one the reference already
grants, so the surface converges on the existing baseline rather than
extending it. This says nothing about whether the reference is correctly
scoped — an over-broad grant inherited from it is still over-broad.

--------------------------------------------------------------------------
hand-written override entries added by this change: 2
Convergence does not cover these. An override is written by a person, so
each is a decision this diff made, and each is cited where it can be read.

    @metamask/assets-controllers>@tanstack/query-core
      {"document": false, "document.visibilityState": false, "addEventListener": false}
      cite: lavamoat/webpack/mv3/main/policy-override.json:234
    eth-lattice-keyring>gridplus-sdk>zod
      {"__zod_globalRegistry": "write"}
      cite: lavamoat/webpack/mv3/main/policy-override.json:288

Produced by capture.sh, not transcribed. head 84145c408f119f2a1f7f69e3af3bda47ec57b9a0 · 0 tracked changes · node v22.23.1 · Python 3.12.3 · yarn.lock ebd1180ecde116bc. Run: https://github.com/MajorLift/metamask-skills/actions/runs/30749471377 — logs and artifacts attached there.

Follows from the audit above

  • All 1116 new grants are ones mv2/main already carries, so the surface converges on the existing baseline rather than extending it — which is what a wrapping change should look like, and is not what +2020 lines of policy looks like on its face.
  • Convergence does not cover the two override entries, because an override is written by a person. Both are cited to the line:
    • policy-override.json:234@metamask/assets-controllers>@tanstack/query-core loses document, document.visibilityState, addEventListener. Tightens.
    • policy-override.json:288eth-lattice-keyring>gridplus-sdk>zod gains "__zod_globalRegistry": "write". Widens.

Raise with a human: write access to a shared global registry from a keyring-adjacent dependency is a new decision, and whether it is right turns on intent and threat model rather than on anything in the policy files. Separately, convergence establishes that these capabilities are not new to the codebase, not that each is correctly scoped — an over-broad grant inherited from mv2 is still over-broad. Only mv3/main was compared; the other three mv3 variants show an identical line delta, which is not an identical capability delta.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

no-changelog no-changelog Indicates no external facing user changes, therefore no changelog documentation needed release-13.40.0 Issue or pull request that will be included in release 13.40.0 risk:medium size-L team-extension-platform Extension Platform team

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

9 participants