1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 01:39:44 +01:00

Fix #14846 - Inject provider for MV3 via app-init (#15448)

This commit is contained in:
David Walsh 2022-08-15 11:26:13 -05:00 committed by GitHub
parent b29aa44a64
commit 9cf358a82a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 128 additions and 111 deletions

View File

@ -47,6 +47,7 @@
], ],
"default_locale": "en", "default_locale": "en",
"description": "__MSG_appDescription__", "description": "__MSG_appDescription__",
"host_permissions": ["file://*/*", "http://*/*", "https://*/*"],
"icons": { "icons": {
"16": "images/icon-16.png", "16": "images/icon-16.png",
"19": "images/icon-19.png", "19": "images/icon-19.png",
@ -61,6 +62,7 @@
"name": "__MSG_appName__", "name": "__MSG_appName__",
"permissions": [ "permissions": [
"storage", "storage",
"scripting",
"unlimitedStorage", "unlimitedStorage",
"clipboardWrite", "clipboardWrite",
"http://localhost:8545/", "http://localhost:8545/",

View File

@ -126,3 +126,18 @@ chrome.runtime.onMessage.addListener(() => {
importAllScripts(); importAllScripts();
return false; return false;
}); });
/*
* This content script is injected programmatically because
* MAIN world injection does not work properly via manifest
* https://bugs.chromium.org/p/chromium/issues/detail?id=634381
*/
chrome.scripting.registerContentScripts([
{
id: 'inpage',
matches: ['file://*/*', 'http://*/*', 'https://*/*'],
js: ['inpage.js'],
runAt: 'document_start',
world: 'MAIN',
},
]);

View File

@ -6,6 +6,7 @@ import PortStream from 'extension-port-stream';
import { obj as createThoughStream } from 'through2'; import { obj as createThoughStream } from 'through2';
import { isManifestV3 } from '../../shared/modules/mv3.utils'; import { isManifestV3 } from '../../shared/modules/mv3.utils';
import shouldInjectProvider from '../../shared/modules/provider-injection';
// These require calls need to use require to be statically recognized by browserify // These require calls need to use require to be statically recognized by browserify
const fs = require('fs'); const fs = require('fs');
@ -43,7 +44,9 @@ if (
) { ) {
setupPhishingStream(); setupPhishingStream();
} else if (shouldInjectProvider()) { } else if (shouldInjectProvider()) {
if (!isManifestV3()) {
injectScript(inpageBundle); injectScript(inpageBundle);
}
setupStreams(); setupStreams();
} }
@ -57,12 +60,7 @@ function injectScript(content) {
const container = document.head || document.documentElement; const container = document.head || document.documentElement;
const scriptTag = document.createElement('script'); const scriptTag = document.createElement('script');
scriptTag.setAttribute('async', 'false'); scriptTag.setAttribute('async', 'false');
// Inline scripts do not work in MV3 due to more strict security policy
if (isManifestV3()) {
scriptTag.setAttribute('src', browser.runtime.getURL('inpage.js'));
} else {
scriptTag.textContent = content; scriptTag.textContent = content;
}
container.insertBefore(scriptTag, container.children[0]); container.insertBefore(scriptTag, container.children[0]);
container.removeChild(scriptTag); container.removeChild(scriptTag);
} catch (error) { } catch (error) {
@ -264,100 +262,6 @@ function notifyInpageOfStreamFailure() {
); );
} }
/**
* Determines if the provider should be injected
*
* @returns {boolean} {@code true} Whether the provider should be injected
*/
function shouldInjectProvider() {
return (
doctypeCheck() &&
suffixCheck() &&
documentElementCheck() &&
!blockedDomainCheck()
);
}
/**
* Checks the doctype of the current document if it exists
*
* @returns {boolean} {@code true} if the doctype is html or if none exists
*/
function doctypeCheck() {
const { doctype } = window.document;
if (doctype) {
return doctype.name === 'html';
}
return true;
}
/**
* Returns whether or not the extension (suffix) of the current document is prohibited
*
* This checks {@code window.location.pathname} against a set of file extensions
* that we should not inject the provider into. This check is indifferent of
* query parameters in the location.
*
* @returns {boolean} whether or not the extension of the current document is prohibited
*/
function suffixCheck() {
const prohibitedTypes = [/\.xml$/u, /\.pdf$/u];
const currentUrl = window.location.pathname;
for (let i = 0; i < prohibitedTypes.length; i++) {
if (prohibitedTypes[i].test(currentUrl)) {
return false;
}
}
return true;
}
/**
* Checks the documentElement of the current document
*
* @returns {boolean} {@code true} if the documentElement is an html node or if none exists
*/
function documentElementCheck() {
const documentElement = document.documentElement.nodeName;
if (documentElement) {
return documentElement.toLowerCase() === 'html';
}
return true;
}
/**
* Checks if the current domain is blocked
*
* @returns {boolean} {@code true} if the current domain is blocked
*/
function blockedDomainCheck() {
const blockedDomains = [
'adyen.com',
'ani.gamer.com.tw',
'blueskybooking.com',
'cdn.shopify.com/s/javascripts/tricorder/xtld-read-only-frame.html',
'docs.google.com',
'dropbox.com',
'gravityforms.com',
'harbourair.com',
'sharefile.com',
'uscourts.gov',
'webbyawards.com',
];
const currentUrl = window.location.href;
let currentRegex;
for (let i = 0; i < blockedDomains.length; i++) {
const blockedDomain = blockedDomains[i].replace('.', '\\.');
currentRegex = new RegExp(
`(?:https?:\\/\\/)(?:(?!${blockedDomain}).)*$`,
'u',
);
if (!currentRegex.test(currentUrl)) {
return true;
}
}
return false;
}
/** /**
* Redirects the current page to a phishing information page * Redirects the current page to a phishing information page
* *

View File

@ -34,6 +34,7 @@ cleanContextForImports();
import log from 'loglevel'; import log from 'loglevel';
import { WindowPostMessageStream } from '@metamask/post-message-stream'; import { WindowPostMessageStream } from '@metamask/post-message-stream';
import { initializeProvider } from '@metamask/providers/dist/initializeInpageProvider'; import { initializeProvider } from '@metamask/providers/dist/initializeInpageProvider';
import shouldInjectProvider from '../../shared/modules/provider-injection';
restoreContextAfterImports(); restoreContextAfterImports();
@ -43,6 +44,7 @@ log.setDefaultLevel(process.env.METAMASK_DEBUG ? 'debug' : 'warn');
// setup plugin communication // setup plugin communication
// //
if (shouldInjectProvider()) {
// setup background connection // setup background connection
const metamaskStream = new WindowPostMessageStream({ const metamaskStream = new WindowPostMessageStream({
name: 'metamask-inpage', name: 'metamask-inpage',
@ -54,3 +56,4 @@ initializeProvider({
logger: log, logger: log,
shouldShimWeb3: true, shouldShimWeb3: true,
}); });
}

View File

@ -0,0 +1,93 @@
/**
* Determines if the provider should be injected
*
* @returns {boolean} {@code true} Whether the provider should be injected
*/
export default function shouldInjectProvider() {
return (
doctypeCheck() &&
suffixCheck() &&
documentElementCheck() &&
!blockedDomainCheck()
);
}
/**
* Checks the doctype of the current document if it exists
*
* @returns {boolean} {@code true} if the doctype is html or if none exists
*/
function doctypeCheck() {
const { doctype } = window.document;
if (doctype) {
return doctype.name === 'html';
}
return true;
}
/**
* Returns whether or not the extension (suffix) of the current document is prohibited
*
* This checks {@code window.location.pathname} against a set of file extensions
* that we should not inject the provider into. This check is indifferent of
* query parameters in the location.
*
* @returns {boolean} whether or not the extension of the current document is prohibited
*/
function suffixCheck() {
const prohibitedTypes = [/\.xml$/u, /\.pdf$/u];
const currentUrl = window.location.pathname;
for (let i = 0; i < prohibitedTypes.length; i++) {
if (prohibitedTypes[i].test(currentUrl)) {
return false;
}
}
return true;
}
/**
* Checks the documentElement of the current document
*
* @returns {boolean} {@code true} if the documentElement is an html node or if none exists
*/
function documentElementCheck() {
const documentElement = document.documentElement.nodeName;
if (documentElement) {
return documentElement.toLowerCase() === 'html';
}
return true;
}
/**
* Checks if the current domain is blocked
*
* @returns {boolean} {@code true} if the current domain is blocked
*/
function blockedDomainCheck() {
const blockedDomains = [
'uscourts.gov',
'dropbox.com',
'webbyawards.com',
'cdn.shopify.com/s/javascripts/tricorder/xtld-read-only-frame.html',
'adyen.com',
'gravityforms.com',
'docs.google.com',
'harbourair.com',
'ani.gamer.com.tw',
'blueskybooking.com',
'sharefile.com',
];
const currentUrl = window.location.href;
let currentRegex;
for (let i = 0; i < blockedDomains.length; i++) {
const blockedDomain = blockedDomains[i].replace('.', '\\.');
currentRegex = new RegExp(
`(?:https?:\\/\\/)(?:(?!${blockedDomain}).)*$`,
'u',
);
if (!currentRegex.test(currentUrl)) {
return true;
}
}
return false;
}