diff --git a/.depcheckrc.yml b/.depcheckrc.yml index 50c309973..2652ffb2b 100644 --- a/.depcheckrc.yml +++ b/.depcheckrc.yml @@ -17,6 +17,7 @@ ignores: # used in testing + ci - '@metamask/auto-changelog' # invoked as `auto-changelog` - '@metamask/forwarder' + - '@metamask/phishing-warning' # statically hosted as part of some e2e tests - '@metamask/test-dapp' - '@metamask/design-tokens' # Only imported in index.css - '@tsconfig/node14' # required dynamically by TS, used in tsconfig.json diff --git a/.metamaskrc.dist b/.metamaskrc.dist index 29dbcb2f7..d3bff3b46 100644 --- a/.metamaskrc.dist +++ b/.metamaskrc.dist @@ -6,3 +6,6 @@ ONBOARDING_V2= SWAPS_USE_DEV_APIS= COLLECTIBLES_V1= TOKEN_DETECTION_V2= + +; Set this to test changes to the phishing warning page. +PHISHING_WARNING_PAGE_URL= diff --git a/CHANGELOG.md b/CHANGELOG.md index d271b47a3..c30bf6edb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,39 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [10.14.7] +### Changed +- Make JavaScript bundles more reproducible between environments. + - The bundles no longer include absolute paths to each module included. + +## [10.14.6] +### Changed +- Move phishing warning page to external site. + - The page shown when a site is blocked has been extracted from the extension and moved to an external site. This site is eagerly cached with a service worker upon extension startup, so it should continue to work even while offline. +- Make build .zip files reproducible (#14623) + - The ordering of files within each .zip file was non-deterministic before this change. We fixed this to comply with Firefox store policies. + +## [10.14.5] +### Fixed +- This release was deployed to fix a configuration issue. + +## [10.14.4] +### Fixed +- This release was deployed to fix a configuration issue. + +## [10.14.3] +### Fixed +- This release was deployed to fix a configuration issue. + +## [10.14.2] +### Fixed +- Make build deterministic (#14610) + - The ordering of modules within each bundle was non-deterministic before this change. We fixed this to comply with Firefox store policies. + +## [10.14.1] +### Changed +- This version was used to rollback from v10.14.0 to v10.13.0. + ## [10.14.0] ### Added - **[FLASK]** Add snap version to details page ([#14110](https://github.com/MetaMask/metamask-extension/pull/14110)) @@ -2908,7 +2941,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Uncategorized - Added the ability to restore accounts from seed words. -[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v10.14.0...HEAD +[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v10.14.7...HEAD +[10.14.7]: https://github.com/MetaMask/metamask-extension/compare/v10.14.6...v10.14.7 +[10.14.6]: https://github.com/MetaMask/metamask-extension/compare/v10.14.5...v10.14.6 +[10.14.5]: https://github.com/MetaMask/metamask-extension/compare/v10.14.4...v10.14.5 +[10.14.4]: https://github.com/MetaMask/metamask-extension/compare/v10.14.3...v10.14.4 +[10.14.3]: https://github.com/MetaMask/metamask-extension/compare/v10.14.2...v10.14.3 +[10.14.2]: https://github.com/MetaMask/metamask-extension/compare/v10.14.1...v10.14.2 +[10.14.1]: https://github.com/MetaMask/metamask-extension/compare/v10.14.0...v10.14.1 [10.14.0]: https://github.com/MetaMask/metamask-extension/compare/v10.13.0...v10.14.0 [10.13.0]: https://github.com/MetaMask/metamask-extension/compare/v10.12.4...v10.13.0 [10.12.4]: https://github.com/MetaMask/metamask-extension/compare/v10.12.3...v10.12.4 diff --git a/app/manifest/_base.json b/app/manifest/_base.json index c2b20b4db..87bed959d 100644 --- a/app/manifest/_base.json +++ b/app/manifest/_base.json @@ -72,6 +72,5 @@ "*://*.eth/", "notifications" ], - "short_name": "__MSG_appName__", - "web_accessible_resources": ["inpage.js", "phishing.html"] + "short_name": "__MSG_appName__" } diff --git a/app/phishing.html b/app/phishing.html deleted file mode 100644 index e854d79fa..000000000 --- a/app/phishing.html +++ /dev/null @@ -1,150 +0,0 @@ - - - - MetaMask Phishing Detection - - - - - - - - - - -
-
- MetaMask Logo -

- - MetaMask Phishing Detection -

-
-
-

- This domain is currently on the MetaMask domain warning list. This - means that based on information available to us, MetaMask believes - this domain could currently compromise your security and, as an added - safety feature, MetaMask has restricted access to the site. To - override this, please read the rest of this warning for instructions - on how to continue at your own risk. -

-

- There are many reasons sites can appear on our warning list, and our - warning list compiles from other widely used industry lists. Such - reasons can include known fraud or security risks, such as domains - that test positive on the - Ethereum Phishing Detector. Domains on these warning lists may include outright malicious - websites and legitimate websites that have been compromised by a - malicious actor. -

-

- To read more about this site - please search for the domain on CryptoScamDB. -

-

- Note that this warning list is compiled on a voluntary basis. This - list may be inaccurate or incomplete. Just because a domain does not - appear on this list is not an implicit guarantee of that domain's - safety. As always, your transactions are your own responsibility. If - you wish to interact with any domain on our warning list, you can do - so by continuing at your own risk. -

-

- If you think this domain is incorrectly flagged or if a blocked - legitimate website has resolved its security issues, - please file an issue. -

-
-
- - diff --git a/app/scripts/background.js b/app/scripts/background.js index ae85d781b..affdcd32d 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -67,6 +67,12 @@ if (inTest || process.env.METAMASK_DEBUG) { global.metamaskGetState = localStore.get.bind(localStore); } +const phishingPageUrl = new URL(process.env.PHISHING_WARNING_PAGE_URL); + +const ONE_SECOND_IN_MILLISECONDS = 1_000; +// Timeout for initializing phishing warning page. +const PHISHING_WARNING_PAGE_TIMEOUT = ONE_SECOND_IN_MILLISECONDS; + // initialization flow initialize().catch(log.error); @@ -134,9 +140,76 @@ async function initialize() { const initState = await loadStateFromPersistence(); const initLangCode = await getFirstPreferredLangCode(); await setupController(initState, initLangCode); + await loadPhishingWarningPage(); log.info('MetaMask initialization complete.'); } +/** + * An error thrown if the phishing warning page takes too long to load. + */ +class PhishingWarningPageTimeoutError extends Error { + constructor() { + super('Timeout failed'); + } +} + +/** + * Load the phishing warning page temporarily to ensure the service + * worker has been registered, so that the warning page works offline. + */ +async function loadPhishingWarningPage() { + let iframe; + try { + const extensionStartupPhishingPageUrl = new URL( + process.env.PHISHING_WARNING_PAGE_URL, + ); + // The `extensionStartup` hash signals to the phishing warning page that it should not bother + // setting up streams for user interaction. Otherwise this page load would cause a console + // error. + extensionStartupPhishingPageUrl.hash = '#extensionStartup'; + + iframe = window.document.createElement('iframe'); + iframe.setAttribute('src', extensionStartupPhishingPageUrl.href); + iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin'); + + // Create "deferred Promise" to allow passing resolve/reject to event handlers + let deferredResolve; + let deferredReject; + const loadComplete = new Promise((resolve, reject) => { + deferredResolve = resolve; + deferredReject = reject; + }); + + // The load event is emitted once loading has completed, even if the loading failed. + // If loading failed we can't do anything about it, so we don't need to check. + iframe.addEventListener('load', deferredResolve); + + // This step initiates the page loading. + window.document.body.appendChild(iframe); + + // This timeout ensures that this iframe gets cleaned up in a reasonable + // timeframe, and ensures that the "initialization complete" message + // doesn't get delayed too long. + setTimeout( + () => deferredReject(new PhishingWarningPageTimeoutError()), + PHISHING_WARNING_PAGE_TIMEOUT, + ); + await loadComplete; + } catch (error) { + if (error instanceof PhishingWarningPageTimeoutError) { + console.warn( + 'Phishing warning page timeout; page not guaraneteed to work offline.', + ); + } else { + console.error('Failed to initialize phishing warning page', error); + } + } finally { + if (iframe) { + iframe.remove(); + } + } +} + // // State and Persistence // @@ -362,6 +435,10 @@ function setupController(initState, initLangCode) { remotePort.sender.origin === `chrome-extension://${browser.runtime.id}`; } + const senderUrl = remotePort.sender?.url + ? new URL(remotePort.sender.url) + : null; + if (isMetaMaskInternalProcess) { const portStream = new PortStream(remotePort); // communication with popup @@ -406,6 +483,15 @@ function setupController(initState, initLangCode) { ); }); } + } else if ( + senderUrl && + senderUrl.origin === phishingPageUrl.origin && + senderUrl.pathname === phishingPageUrl.pathname + ) { + const portStream = new PortStream(remotePort); + controller.setupPhishingCommunication({ + connectionStream: portStream, + }); } else { if (remotePort.sender && remotePort.sender.tab && remotePort.sender.url) { const tabId = remotePort.sender.tab.id; diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js index 037f3de14..edbabad33 100644 --- a/app/scripts/contentscript.js +++ b/app/scripts/contentscript.js @@ -17,8 +17,13 @@ const inpageContent = fs.readFileSync( const inpageSuffix = `//# sourceURL=${browser.runtime.getURL('inpage.js')}\n`; const inpageBundle = inpageContent + inpageSuffix; +// contexts const CONTENT_SCRIPT = 'metamask-contentscript'; const INPAGE = 'metamask-inpage'; +const PHISHING_WARNING_PAGE = 'metamask-phishing-warning-page'; + +// stream channels +const PHISHING_SAFELIST = 'metamask-phishing-safelist'; const PROVIDER = 'metamask-provider'; // TODO:LegacyProvider: Delete @@ -27,7 +32,14 @@ const LEGACY_INPAGE = 'inpage'; const LEGACY_PROVIDER = 'provider'; const LEGACY_PUBLIC_CONFIG = 'publicConfig'; -if (shouldInjectProvider()) { +const phishingPageUrl = new URL(process.env.PHISHING_WARNING_PAGE_URL); + +if ( + window.location.origin === phishingPageUrl.origin && + window.location.pathname === phishingPageUrl.pathname +) { + setupPhishingStream(); +} else if (shouldInjectProvider()) { injectScript(inpageBundle); setupStreams(); } @@ -50,6 +62,47 @@ function injectScript(content) { } } +async function setupPhishingStream() { + // the transport-specific streams for communication between inpage and background + const pageStream = new WindowPostMessageStream({ + name: CONTENT_SCRIPT, + target: PHISHING_WARNING_PAGE, + }); + const extensionPort = browser.runtime.connect({ name: CONTENT_SCRIPT }); + const extensionStream = new PortStream(extensionPort); + + // create and connect channel muxers + // so we can handle the channels individually + const pageMux = new ObjectMultiplex(); + pageMux.setMaxListeners(25); + const extensionMux = new ObjectMultiplex(); + extensionMux.setMaxListeners(25); + + pump(pageMux, pageStream, pageMux, (err) => + logStreamDisconnectWarning('MetaMask Inpage Multiplex', err), + ); + pump(extensionMux, extensionStream, extensionMux, (err) => { + logStreamDisconnectWarning('MetaMask Background Multiplex', err); + window.postMessage( + { + target: PHISHING_WARNING_PAGE, // the post-message-stream "target" + data: { + // this object gets passed to obj-multiplex + name: PHISHING_SAFELIST, // the obj-multiplex channel name + data: { + jsonrpc: '2.0', + method: 'METAMASK_STREAM_FAILURE', + }, + }, + }, + window.location.origin, + ); + }); + + // forward communication across inpage-background for these channels only + forwardTrafficBetweenMuxes(PHISHING_SAFELIST, pageMux, extensionMux); +} + /** * Sets up two-way communication streams between the * browser extension and local per-page browser context. @@ -300,9 +353,9 @@ function blockedDomainCheck() { * Redirects the current page to a phishing information page */ function redirectToPhishingWarning() { - console.debug('MetaMask: Routing to Phishing Warning component.'); - const extensionURL = browser.runtime.getURL('phishing.html'); - window.location.href = `${extensionURL}#${querystring.stringify({ + console.debug('MetaMask: Routing to Phishing Warning page.'); + const baseUrl = process.env.PHISHING_WARNING_PAGE_URL; + window.location.href = `${baseUrl}#${querystring.stringify({ hostname: window.location.hostname, href: window.location.href, })}`; diff --git a/app/scripts/lib/util.js b/app/scripts/lib/util.js index 9800666af..b7fdf0521 100644 --- a/app/scripts/lib/util.js +++ b/app/scripts/lib/util.js @@ -27,7 +27,7 @@ const getEnvironmentTypeMemo = memoize((url) => { const parsedUrl = new URL(url); if (parsedUrl.pathname === '/popup.html') { return ENVIRONMENT_TYPE_POPUP; - } else if (['/home.html', '/phishing.html'].includes(parsedUrl.pathname)) { + } else if (['/home.html'].includes(parsedUrl.pathname)) { return ENVIRONMENT_TYPE_FULLSCREEN; } else if (parsedUrl.pathname === '/notification.html') { return ENVIRONMENT_TYPE_NOTIFICATION; diff --git a/app/scripts/lib/util.test.js b/app/scripts/lib/util.test.js index 62c33b5a5..0892a4e3d 100644 --- a/app/scripts/lib/util.test.js +++ b/app/scripts/lib/util.test.js @@ -34,13 +34,6 @@ describe('app utils', () => { expect(environmentType).toStrictEqual(ENVIRONMENT_TYPE_FULLSCREEN); }); - it('should return fullscreen type for phishing.html', () => { - const environmentType = getEnvironmentType( - 'http://extension-id/phishing.html', - ); - expect(environmentType).toStrictEqual(ENVIRONMENT_TYPE_FULLSCREEN); - }); - it('should return background type', () => { const environmentType = getEnvironmentType( 'http://extension-id/_generated_background_page.html', diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 159ace8ea..61b35e7f7 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -146,6 +146,9 @@ export const METAMASK_CONTROLLER_EVENTS = { APPROVAL_STATE_CHANGE: 'ApprovalController:stateChange', }; +// stream channels +const PHISHING_SAFELIST = 'metamask-phishing-safelist'; + export default class MetamaskController extends EventEmitter { /** * @param {Object} opts @@ -1412,7 +1415,6 @@ export default class MetamaskController extends EventEmitter { ), markPasswordForgotten: this.markPasswordForgotten.bind(this), unMarkPasswordForgotten: this.unMarkPasswordForgotten.bind(this), - safelistPhishingDomain: this.safelistPhishingDomain.bind(this), getRequestAccountTabIds: this.getRequestAccountTabIds, getOpenMetamaskTabsIds: this.getOpenMetamaskTabsIds, markNotificationPopupAsAutomaticallyClosed: () => @@ -3209,6 +3211,33 @@ export default class MetamaskController extends EventEmitter { ); } + /** + * Used to create a multiplexed stream for connecting to the phishing warning page. + * + * @param options - Options bag. + * @param {ReadableStream} options.connectionStream - The Duplex stream to connect to. + */ + setupPhishingCommunication({ connectionStream }) { + const { usePhishDetect } = this.preferencesController.store.getState(); + + if (!usePhishDetect) { + return; + } + + // setup multiplexing + const mux = setupMultiplex(connectionStream); + const phishingStream = mux.createStream(PHISHING_SAFELIST); + + // set up postStream transport + phishingStream.on( + 'data', + createMetaRPCHandler( + { safelistPhishingDomain: this.safelistPhishingDomain.bind(this) }, + phishingStream, + ), + ); + } + /** * Called when we detect a suspicious domain. Requests the browser redirects * to our anti-phishing page. diff --git a/app/scripts/phishing-detect.js b/app/scripts/phishing-detect.js deleted file mode 100644 index 68c30d501..000000000 --- a/app/scripts/phishing-detect.js +++ /dev/null @@ -1,40 +0,0 @@ -import querystring from 'querystring'; -import PortStream from 'extension-port-stream'; -import browser from 'webextension-polyfill'; -import createRandomId from '../../shared/modules/random-id'; -import { setupMultiplex } from './lib/stream-utils'; -import { getEnvironmentType } from './lib/util'; -import ExtensionPlatform from './platforms/extension'; - -document.addEventListener('DOMContentLoaded', start); - -function start() { - const hash = window.location.hash.substring(1); - const suspect = querystring.parse(hash); - - const newIssueLink = document.getElementById('new-issue-link'); - const newIssueUrl = `https://github.com/MetaMask/eth-phishing-detect/issues/new`; - const newIssueParams = `?title=[Legitimate%20Site%20Blocked]%20${encodeURIComponent( - suspect.hostname, - )}&body=${encodeURIComponent(suspect.href)}`; - newIssueLink.href = `${newIssueUrl}${newIssueParams}`; - - global.platform = new ExtensionPlatform(); - - const extensionPort = browser.runtime.connect({ - name: getEnvironmentType(), - }); - const connectionStream = new PortStream(extensionPort); - const mx = setupMultiplex(connectionStream); - const backgroundConnection = mx.createStream('controller'); - const continueLink = document.getElementById('unsafe-continue'); - continueLink.addEventListener('click', () => { - backgroundConnection.write({ - jsonrpc: '2.0', - method: 'safelistPhishingDomain', - params: [suspect.hostname], - id: createRandomId(), - }); - window.location.href = suspect.href; - }); -} diff --git a/development/build/etc.js b/development/build/etc.js index 7b76e0b9c..e6d06c84d 100644 --- a/development/build/etc.js +++ b/development/build/etc.js @@ -1,5 +1,6 @@ const { promises: fs } = require('fs'); const gulp = require('gulp'); +const sort = require('gulp-sort'); const gulpZip = require('gulp-zip'); const del = require('del'); const pify = require('pify'); @@ -45,7 +46,9 @@ function createZipTask(platform, buildType, version) { : `metamask-${buildType}-${platform}-${version}`; await pump( gulp.src(`dist/${platform}/**`), - gulpZip(`${path}.zip`), + // sort files and set `mtime` to epoch to ensure zip build is deterministic + sort(), + gulpZip(`${path}.zip`, { modifiedTime: new Date(0) }), gulp.dest('builds'), ); }; diff --git a/development/build/scripts.js b/development/build/scripts.js index f942c4c1b..39b626d1f 100644 --- a/development/build/scripts.js +++ b/development/build/scripts.js @@ -34,6 +34,7 @@ const metamaskrc = require('rc')('metamask', { INFURA_PROD_PROJECT_ID: process.env.INFURA_PROD_PROJECT_ID, ONBOARDING_V2: process.env.ONBOARDING_V2, COLLECTIBLES_V1: process.env.COLLECTIBLES_V1, + PHISHING_WARNING_PAGE_URL: process.env.PHISHING_WARNING_PAGE_URL, TOKEN_DETECTION_V2: process.env.TOKEN_DETECTION_V2, SEGMENT_HOST: process.env.SEGMENT_HOST, SEGMENT_WRITE_KEY: process.env.SEGMENT_WRITE_KEY, @@ -133,6 +134,48 @@ function getSegmentWriteKey({ buildType, environment }) { throw new Error(`Invalid build type: '${buildType}'`); } +/** + * Get the URL for the phishing warning page, if it has been set. + * + * @param {object} options - The phishing warning page options. + * @param {boolean} options.testing - Whether this is a test build or not. + * @returns {string} The URL for the phishing warning page, or `undefined` if no URL is set. + */ +function getPhishingWarningPageUrl({ testing }) { + let phishingWarningPageUrl = metamaskrc.PHISHING_WARNING_PAGE_URL; + + if (!phishingWarningPageUrl) { + phishingWarningPageUrl = testing + ? 'http://localhost:9999/' + : 'https://metamask.github.io/phishing-warning/v1.1.0/'; + } + + // We add a hash/fragment to the URL dynamically, so we need to ensure it + // has a valid pathname to append a hash to. + const normalizedUrl = phishingWarningPageUrl.endsWith('/') + ? phishingWarningPageUrl + : `${phishingWarningPageUrl}/`; + + let phishingWarningPageUrlObject; + try { + // eslint-disable-next-line no-new + phishingWarningPageUrlObject = new URL(normalizedUrl); + } catch (error) { + throw new Error( + `Invalid phishing warning page URL: '${normalizedUrl}'`, + error, + ); + } + if (phishingWarningPageUrlObject.hash) { + // The URL fragment must be set dynamically + throw new Error( + `URL fragment not allowed in phishing warning page URL: '${normalizedUrl}'`, + ); + } + + return normalizedUrl; +} + const noopWriteStream = through.obj((_file, _fileEncoding, callback) => callback(), ); @@ -216,11 +259,6 @@ function createScriptTasks({ createTaskForBundleSentry({ devMode, testing }), ); - const phishingDetectSubtask = createTask( - `${taskPrefix}:phishing-detect`, - createTaskForBundlePhishingDetect({ devMode, testing }), - ); - // task for initiating browser livereload const initiateLiveReload = async () => { if (devMode) { @@ -243,7 +281,6 @@ function createScriptTasks({ contentscriptSubtask, disableConsoleSubtask, installSentrySubtask, - phishingDetectSubtask, ].map((subtask) => runInChildProcess(subtask, { buildType, @@ -290,23 +327,6 @@ function createScriptTasks({ }); } - function createTaskForBundlePhishingDetect({ devMode, testing }) { - const label = 'phishing-detect'; - return createNormalBundle({ - buildType, - browserPlatforms, - destFilepath: `${label}.js`, - devMode, - entryFilepath: `./app/scripts/${label}.js`, - ignoredFiles, - label, - testing, - policyOnly, - shouldLintFenceFiles, - version, - }); - } - // the "contentscript" bundle contains the "inpage" bundle function createTaskForBundleContentscript({ devMode, testing }) { const inpage = 'inpage'; @@ -818,6 +838,7 @@ function getEnvironmentVariables({ buildType, devMode, testing, version }) { METAMASK_BUILD_TYPE: buildType, NODE_ENV: devMode ? ENVIRONMENT.DEVELOPMENT : ENVIRONMENT.PRODUCTION, IN_TEST: testing, + PHISHING_WARNING_PAGE_URL: getPhishingWarningPageUrl({ testing }), PUBNUB_SUB_KEY: process.env.PUBNUB_SUB_KEY || '', PUBNUB_PUB_KEY: process.env.PUBNUB_PUB_KEY || '', CONF: devMode ? metamaskrc : {}, diff --git a/development/build/static.js b/development/build/static.js index 7efde5a72..d1f024fbc 100644 --- a/development/build/static.js +++ b/development/build/static.js @@ -174,10 +174,6 @@ function getCopyTargets(shouldIncludeLockdown) { src: require.resolve('@lavamoat/lavapack/src/runtime.js'), dest: `runtime-lavamoat.js`, }, - { - src: `./app/phishing.html`, - dest: `phishing.html`, - }, ]; const languageTags = new Set(); diff --git a/development/sourcemap-validator.js b/development/sourcemap-validator.js index 01861e229..0531a03ce 100644 --- a/development/sourcemap-validator.js +++ b/development/sourcemap-validator.js @@ -24,7 +24,6 @@ async function start() { `common-0.js`, `background-0.js`, `ui-0.js`, - 'phishing-detect.js', // `contentscript.js`, skipped because the validator is erroneously sampling the inlined `inpage.js` script `inpage.js`, ]; diff --git a/lavamoat/build-system/policy.json b/lavamoat/build-system/policy.json index 03fe49344..a1a3e4eee 100644 --- a/lavamoat/build-system/policy.json +++ b/lavamoat/build-system/policy.json @@ -1087,21 +1087,11 @@ "ansi-wrap": true } }, - "ansi-cyan": { - "packages": { - "ansi-wrap": true - } - }, "ansi-gray": { "packages": { "ansi-wrap": true } }, - "ansi-red": { - "packages": { - "ansi-wrap": true - } - }, "ansi-styles": { "packages": { "color-convert": true @@ -1130,8 +1120,7 @@ }, "arr-diff": { "packages": { - "arr-flatten": true, - "array-slice": true + "arr-flatten": true } }, "arr-filter": { @@ -2273,8 +2262,7 @@ "extend-shallow": { "packages": { "assign-symbols": true, - "is-extendable": true, - "kind-of": true + "is-extendable": true } }, "extglob": { @@ -2540,10 +2528,14 @@ }, "get-stream": { "builtin": { + "buffer.constants.MAX_LENGTH": true, "stream.PassThrough": true }, "globals": { "Buffer.concat": true + }, + "packages": { + "pump": true } }, "glob": { @@ -2796,6 +2788,11 @@ "vinyl-sourcemaps-apply": true } }, + "gulp-sort": { + "packages": { + "through2": true + } + }, "gulp-sourcemaps": { "builtin": { "path.dirname": true, @@ -2874,6 +2871,7 @@ }, "gulp-zip": { "builtin": { + "buffer.constants.MAX_LENGTH": true, "path.join": true }, "packages": { @@ -3864,8 +3862,6 @@ }, "packages": { "ansi-colors": true, - "ansi-cyan": true, - "ansi-red": true, "arr-diff": true, "arr-union": true, "extend-shallow": true diff --git a/package.json b/package.json index 63f811e33..4d7846653 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "metamask-crx", - "version": "10.14.0", + "version": "10.14.7", "private": true, "repository": { "type": "git", @@ -252,6 +252,7 @@ "@metamask/eslint-config-nodejs": "^9.0.0", "@metamask/eslint-config-typescript": "^9.0.1", "@metamask/forwarder": "^1.1.0", + "@metamask/phishing-warning": "^1.1.0", "@metamask/test-dapp": "^5.0.0", "@sentry/cli": "^1.58.0", "@storybook/addon-a11y": "^6.3.12", @@ -277,7 +278,7 @@ "@typescript-eslint/parser": "^4.20.0", "addons-linter": "1.14.0", "babelify": "^10.0.0", - "bify-module-groups": "^1.0.0", + "bify-module-groups": "^2.0.0", "brfs": "^2.0.2", "browser-util-inspect": "^0.2.0", "browserify": "^16.5.1", @@ -318,10 +319,11 @@ "gulp-livereload": "4.0.0", "gulp-rename": "^2.0.0", "gulp-rtlcss": "^1.4.0", + "gulp-sort": "^2.0.0", "gulp-sourcemaps": "^3.0.0", "gulp-stylelint": "^13.0.0", "gulp-watch": "^5.0.1", - "gulp-zip": "^4.0.0", + "gulp-zip": "^5.1.0", "history": "^5.0.0", "improved-yarn-audit": "^3.0.0", "jest": "^26.6.3", diff --git a/patches/@lavamoat+lavapack+2.0.4.patch b/patches/@lavamoat+lavapack+2.0.4.patch new file mode 100644 index 000000000..24c412dad --- /dev/null +++ b/patches/@lavamoat+lavapack+2.0.4.patch @@ -0,0 +1,15 @@ +diff --git a/node_modules/@lavamoat/lavapack/src/pack.js b/node_modules/@lavamoat/lavapack/src/pack.js +index 8e5284f..f0e4a64 100644 +--- a/node_modules/@lavamoat/lavapack/src/pack.js ++++ b/node_modules/@lavamoat/lavapack/src/pack.js +@@ -204,7 +204,9 @@ function createPacker({ + // id, + package: packageName, + packageVersion, +- file, ++ // Omit this absolute filename from bundle so that builds are reproducible between environments ++ // TODO: update lavapack with an option to omit this, and/or make this filepath relative to the current working directory ++ // file, + // deps, + // source: sourceMeta.code + } diff --git a/test/e2e/helpers.js b/test/e2e/helpers.js index a6f7b3c7b..1b611b989 100644 --- a/test/e2e/helpers.js +++ b/test/e2e/helpers.js @@ -6,6 +6,7 @@ const enLocaleMessages = require('../../app/_locales/en/messages.json'); const { setupMocking } = require('./mock-e2e'); const Ganache = require('./ganache'); const FixtureServer = require('./fixture-server'); +const PhishingWarningPageServer = require('./phishing-warning-page-server'); const { buildWebDriver } = require('./webdriver'); const { ensureXServerIsRunning } = require('./x-server'); @@ -27,6 +28,7 @@ async function withFixtures(options, testSuite) { title, failOnConsoleError = true, dappPath = undefined, + dappPaths, testSpecificMock = function () { // do nothing. }, @@ -38,6 +40,7 @@ async function withFixtures(options, testSuite) { let secondaryGanacheServer; let numberOfDapps = dapp ? 1 : 0; const dappServer = []; + const phishingPageServer = new PhishingWarningPageServer(); let webDriver; let failed = false; @@ -55,14 +58,15 @@ async function withFixtures(options, testSuite) { } await fixtureServer.start(); await fixtureServer.loadState(path.join(__dirname, 'fixtures', fixtures)); + await phishingPageServer.start(); if (dapp) { if (dappOptions?.numberOfDapps) { numberOfDapps = dappOptions.numberOfDapps; } for (let i = 0; i < numberOfDapps; i++) { let dappDirectory; - if (dappPath) { - dappDirectory = path.resolve(__dirname, dappPath); + if (dappPath || (dappPaths && dappPaths[i])) { + dappDirectory = path.resolve(__dirname, dappPath || dappPaths[i]); } else { dappDirectory = path.resolve( __dirname, @@ -146,6 +150,9 @@ async function withFixtures(options, testSuite) { } } } + if (phishingPageServer.isRunning()) { + await phishingPageServer.quit(); + } await mockServer.stop(); } } diff --git a/test/e2e/mock-page-with-disallowed-iframe/index.html b/test/e2e/mock-page-with-disallowed-iframe/index.html new file mode 100644 index 000000000..ffadeeefa --- /dev/null +++ b/test/e2e/mock-page-with-disallowed-iframe/index.html @@ -0,0 +1,20 @@ + + + + Mock E2E Phishing Page + + + +
Hello
+