From a46baada611164403d7c96dfbf39fc1596e3fbc4 Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Sun, 1 Dec 2019 21:27:09 -0800 Subject: [PATCH] basic wallet_enable and related methods --- app/_locales/en/messages.json | 14 +- app/scripts/controllers/permissions/enums.js | 62 +++++ app/scripts/controllers/permissions/index.js | 94 +++---- .../permissions/methodMiddleware.js | 239 ++++++++++++++++-- .../permissions/permissions-safe-methods.json | 49 ---- app/scripts/controllers/plugins.js | 76 +++--- app/scripts/controllers/preferences.js | 7 +- app/scripts/metamask-controller.js | 2 +- .../permissions-activity.component.js | 4 +- .../permissions-tab.component.js | 19 +- .../plugins-tab/plugins-tab.component.js | 13 +- yarn.lock | 2 +- 12 files changed, 406 insertions(+), 175 deletions(-) create mode 100644 app/scripts/controllers/permissions/enums.js delete mode 100644 app/scripts/controllers/permissions/permissions-safe-methods.json diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 227009008..c574df5c7 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -32,6 +32,9 @@ "clearPlugins": { "message": "Clear Snaps" }, + "clearPluginsDescription": { + "message": "Remove all Snaps and their associated permissions." + }, "confirmClearPlugins": { "message": "Are you sure you want to remove all Snaps and their associated permissions?" }, @@ -56,6 +59,9 @@ "clearPermissions": { "message": "Clear Permissions" }, + "clearPermissionsDescription": { + "message": "Clear permissions so that all dapps/websites must request access again." + }, "confirmClearPermissions": { "message": "Are you sure you want to remove all dapp/website permissions?" }, @@ -74,6 +80,9 @@ "clearPermissionsActivity": { "message": "Clear Permissions Activity" }, + "clearPermissionsActivityDescription": { + "message": "Clear the permissions activity log." + }, "confirmClearPermissionsActivity": { "message": "Are you sure you want to clear the permissions activity log?" }, @@ -89,6 +98,9 @@ "clearPermissionsHistory": { "message": "Clear Permissions History" }, + "clearPermissionsHistoryDescription": { + "message": "Clear the permissions history." + }, "confirmClearPermissionsHistory": { "message": "Are you sure you want to clear the permissions history?" }, @@ -1011,7 +1023,7 @@ "message": "Permissions" }, "permissionsSettingsDescription": { - "message": "Manage dapp/website permissions." + "message": "Manage dapp/website permissions" }, "personalAddressDetected": { "message": "Personal address detected. Input the token contract address." diff --git a/app/scripts/controllers/permissions/enums.js b/app/scripts/controllers/permissions/enums.js new file mode 100644 index 000000000..4e4fe35f6 --- /dev/null +++ b/app/scripts/controllers/permissions/enums.js @@ -0,0 +1,62 @@ +module.exports = { + WALLET_PREFIX: 'wallet_', + PLUGIN_PREFIX: 'wallet_plugin_', + HISTORY_STORE_KEY: 'permissionsHistory', + LOG_STORE_KEY: 'permissionsLog', + METADATA_STORE_KEY: 'domainMetadata', + CAVEAT_NAMES: { + exposedAccounts: 'exposedAccounts', + }, + NOTIFICATION_NAMES: { + accountsChanged: 'wallet_accountsChanged', + }, + SAFE_METHODS: [ + 'web3_sha3', + 'net_listening', + 'net_peerCount', + 'net_version', + 'eth_blockNumber', + 'eth_call', + 'eth_chainId', + 'eth_coinbase', + 'eth_estimateGas', + 'eth_gasPrice', + 'eth_getBalance', + 'eth_getBlockByHash', + 'eth_getBlockByNumber', + 'eth_getBlockTransactionCountByHash', + 'eth_getBlockTransactionCountByNumber', + 'eth_getCode', + 'eth_getFilterChanges', + 'eth_getFilterLogs', + 'eth_getLogs', + 'eth_getStorageAt', + 'eth_getTransactionByBlockHashAndIndex', + 'eth_getTransactionByBlockNumberAndIndex', + 'eth_getTransactionByHash', + 'eth_getTransactionCount', + 'eth_getTransactionReceipt', + 'eth_getUncleByBlockHashAndIndex', + 'eth_getUncleByBlockNumberAndIndex', + 'eth_getUncleCountByBlockHash', + 'eth_getUncleCountByBlockNumber', + 'eth_getWork', + 'eth_hashrate', + 'eth_mining', + 'eth_newBlockFilter', + 'eth_newFilter', + 'eth_newPendingTransactionFilter', + 'eth_protocolVersion', + 'eth_sendRawTransaction', + 'eth_sendTransaction', + 'eth_sign', + 'personal_sign', + 'eth_signTypedData', + 'eth_signTypedData_v1', + 'eth_signTypedData_v3', + 'eth_submitHashrate', + 'eth_submitWork', + 'eth_syncing', + 'eth_uninstallFilter', + ], +} diff --git a/app/scripts/controllers/permissions/index.js b/app/scripts/controllers/permissions/index.js index 5f93cd711..4c9b1ed74 100644 --- a/app/scripts/controllers/permissions/index.js +++ b/app/scripts/controllers/permissions/index.js @@ -12,20 +12,19 @@ const { const createMethodMiddleware = require('./methodMiddleware') const createLoggerMiddleware = require('./loggerMiddleware') -// Methods that do not require any permissions to use: -const SAFE_METHODS = require('./permissions-safe-methods.json') - -const METADATA_STORE_KEY = 'domainMetadata' -const LOG_STORE_KEY = 'permissionsLog' -const HISTORY_STORE_KEY = 'permissionsHistory' -const WALLET_METHOD_PREFIX = 'wallet_' -const CAVEAT_NAMES = { - exposedAccounts: 'exposedAccounts', -} -const ACCOUNTS_CHANGED_NOTIFICATION = 'wallet_accountsChanged' +const { + SAFE_METHODS, // methods that do not require any permissions to use + WALLET_PREFIX, + PLUGIN_PREFIX, + METADATA_STORE_KEY, + LOG_STORE_KEY, + HISTORY_STORE_KEY, + CAVEAT_NAMES, + NOTIFICATION_NAMES, +} = require('./enums') function prefix (method) { - return WALLET_METHOD_PREFIX + method + return WALLET_PREFIX + method } class PermissionsController { @@ -34,8 +33,7 @@ class PermissionsController { openPopup, closePopup, keyringController, assetsController, setupProvider, pluginRestrictedMethods, getApi, notifyDomain, notifyAllDomains, - } = {}, - restoredState = {} + } = {}, restoredState = {} ) { this.store = new ObservableStore({ [METADATA_STORE_KEY]: restoredState[METADATA_STORE_KEY] || {}, @@ -71,13 +69,11 @@ class PermissionsController { store: this.store, storeKey: METADATA_STORE_KEY, getAccounts: this.getAccounts.bind(this, origin), - requestAccountsPermission: this._requestPermissions.bind( - this, origin, { eth_accounts: {} } - ), - handleInstallPlugins: this.handleInstallPlugins.bind(this), + requestPermissions: this._requestPermissions.bind(this, origin), + installPlugins: this.installPlugins.bind(this), })) engine.push(createLoggerMiddleware({ - walletPrefix: WALLET_METHOD_PREFIX, + walletPrefix: WALLET_PREFIX, restrictedMethods: ( Object.keys(this.externalRestrictedMethods) .concat(Object.keys(this.pluginRestrictedMethods)) @@ -113,9 +109,9 @@ class PermissionsController { /** * @param {string} origin - The external domain id. - * @param {Array} requestedPlugins - The names of the requested plugin permissions. + * @param {Object} requestedPlugins - The names of the requested plugin permissions. */ - async handleInstallPlugins (origin, requestedPlugins) { + async installPlugins (origin, requestedPlugins) { const existingPerms = this.permissions.getPermissionsForDomain(origin).reduce( (acc, p) => { @@ -124,24 +120,34 @@ class PermissionsController { }, {} ) - requestedPlugins.forEach(p => { - if (!existingPerms[p]) { - throw ethErrors.provider.unauthorized(`Not authorized to install plugin '${p}'.`) + const result = {} + + // use a for-loop so that we can return an object and await the resolution + // of each call to processRequestedPlugin + for (const pluginName of Object.keys(requestedPlugins)) { + + const permissionName = PLUGIN_PREFIX + pluginName + result[pluginName] = { + permission: permissionName, } - }) - const installedPlugins = await this.pluginsController.processRequestedPlugins(requestedPlugins) + // only allow the installation of permitted plugins + if (!existingPerms[permissionName]) { - if (installedPlugins.length === 0) { - // TODO:plugins reserve error in Ethereum error space? - throw ethErrors.provider.custom({ - code: 4301, - message: 'Failed to install all plugins.', - data: requestedPlugins, - }) - } + result[pluginName].error = ethErrors.provider.unauthorized( + `Not authorized to install plugin '${pluginName}'. Please request the permission for the plugin before attempting to install it.` + ) + } else { - return installedPlugins + // attempt to install and run the plugin, storing any errors that + // occur during the process + result[pluginName] = { + ...result[pluginName], + ...(await this.pluginsController.processRequestedPlugin(pluginName)), + } + } + } + return result } /** @@ -298,7 +304,7 @@ class PermissionsController { ) this.notifyDomain(origin, { - method: ACCOUNTS_CHANGED_NOTIFICATION, + method: NOTIFICATION_NAMES.accountsChanged, result: accounts, }) } @@ -373,7 +379,7 @@ class PermissionsController { if (methodName === 'eth_accounts') { this.notifyDomain( origin, - { method: ACCOUNTS_CHANGED_NOTIFICATION, result: [] } + { method: NOTIFICATION_NAMES.accountsChanged, result: [] } ) } @@ -392,7 +398,7 @@ class PermissionsController { domainsToDelete.forEach(d => { delete domains[d] this.notifyDomain(d, { - method: ACCOUNTS_CHANGED_NOTIFICATION, + method: NOTIFICATION_NAMES.accountsChanged, result: [], }) }) @@ -428,7 +434,7 @@ class PermissionsController { clearPermissions () { this.permissions.clearDomains() this.notifyAllDomains({ - method: ACCOUNTS_CHANGED_NOTIFICATION, + method: NOTIFICATION_NAMES.accountsChanged, result: [], }) } @@ -459,20 +465,20 @@ class PermissionsController { * @param {Object} opts - The CapabilitiesController options. * @param {Object} opts.metamaskEventMethods - Plugin-related internal event methods. * @param {Object} opts.pluginRestrictedMethods - Restricted methods for plugins, if any. - * @param {Object} opts.restoredState - The restored state, if any. + * @param {Object} opts.restoredPermissions - The restored permissions state, if any. */ initializePermissions ({ pluginsController, metamaskEventMethods = {}, pluginRestrictedMethods = {}, - restoredState = {}, + restoredPermissions = {}, } = {}) { this.pluginsController = pluginsController this.metamaskEventMethods = metamaskEventMethods this.pluginRestrictedMethods = pluginRestrictedMethods - const initState = Object.keys(restoredState) + const initState = Object.keys(restoredPermissions) .filter(k => { return ![ 'permissionsDescriptions', @@ -480,7 +486,7 @@ class PermissionsController { ].includes(k) }) .reduce((acc, k) => { - acc[k] = restoredState[k] + acc[k] = restoredPermissions[k] return acc }, {}) @@ -518,7 +524,7 @@ class PermissionsController { safeMethods: SAFE_METHODS, // optional prefix for internal methods - methodPrefix: WALLET_METHOD_PREFIX, + methodPrefix: WALLET_PREFIX, restrictedMethods: { ...this.externalRestrictedMethods, ...namespacedPluginRestrictedMethods, diff --git a/app/scripts/controllers/permissions/methodMiddleware.js b/app/scripts/controllers/permissions/methodMiddleware.js index daa2558b4..5f08c9e62 100644 --- a/app/scripts/controllers/permissions/methodMiddleware.js +++ b/app/scripts/controllers/permissions/methodMiddleware.js @@ -1,32 +1,33 @@ const createAsyncMiddleware = require('json-rpc-engine/src/createAsyncMiddleware') const { ethErrors } = require('eth-json-rpc-errors') +const { PLUGIN_PREFIX } = require('./enums') /** - * Create middleware for preprocessing permissions requests. + * Middleware for preprocessing permission requests and outright handling + * certain methods. + * + * Note: returning from an rpc-engine async middleware is like calling end() + * in a non-async one; it returns the response to the client. Previously added + * return handlers will still process it. */ module.exports = function createRequestMiddleware ({ - store, storeKey, getAccounts, requestAccountsPermission, handleInstallPlugins, + store, storeKey, getAccounts, requestPermissions, installPlugins, }) { return createAsyncMiddleware(async (req, res, next) => { - if (typeof req.method !== 'string') { + if ( + typeof req.method !== 'string' + ) { res.error = ethErrors.rpc.invalidRequest({ data: req }) return } - const prefix = 'wallet_' - const pluginPrefix = prefix + 'plugin_' - switch (req.method) { - // intercepting eth_accounts requests for backwards compatibility, - // i.e. return an empty array instead of an error - case 'eth_accounts': - - res.result = await getAccounts() - return + // account-related methods + // completely handled here case 'eth_requestAccounts': // first, just try to get accounts @@ -38,7 +39,7 @@ module.exports = function createRequestMiddleware ({ // if no accounts, request the accounts permission try { - await requestAccountsPermission() + await requestPermissions({ eth_accounts: {} }) } catch (err) { res.error = err return @@ -54,36 +55,133 @@ module.exports = function createRequestMiddleware ({ 'Accounts unexpectedly unavailable. Please report this bug.' ) } - return + // not handled here, just preprocessed to support some syntactic sugar, + // specifically 'wallet_plugin' + case 'wallet_requestPermissions': + + if (!Array.isArray(req.params)) { + res.error = ethErrors.rpc.invalidParams({ data: req }) + return + } + + try { + req.params[0] = await preprocessRequestPermissions(req.params[0]) + break + } catch (err) { + res.error = err + return + } + + // plugin-related methods + + // completely handled here case 'wallet_installPlugins': + if (!Array.isArray(req.params)) { + res.error = ethErrors.rpc.invalidParams({ data: req }) + return + } + + try { + res.result = await handleInstallPlugins(req.origin, req.params[0]) + } catch (err) { + res.error = err + } + return + + // basically syntactic sugar for calling a plugin RPC method + // we preprocess and forward the request for completion elsewhere + case 'wallet_invokePlugin': + if ( - !Array.isArray(req.params) || typeof req.params[0] !== 'object' + !Array.isArray(req.params) || + typeof req.params[0] !== 'string' || + typeof req.params[1] !== 'object' || + Array.isArray(req.params[1]) ) { res.error = ethErrors.rpc.invalidParams({ data: req }) return } - const requestedPlugins = Object.keys(req.params[0]).filter( - p => p.startsWith(pluginPrefix) - ) + req.method = PLUGIN_PREFIX + req.params[0] + req.params = [ req.params[1] ] + break - if (requestedPlugins.length === 0) { - res.error = ethErrors.rpc.invalidParams({ - message: 'Must request at least one plugin.', data: req, - }) + case 'wallet_getPlugins': + + // TODO + return + + // a convenience method combining: + // - wallet_requestPermissions + // - wallet_installPlugins + // - eth_accounts (effectively, by parsing returned permissions) + case 'wallet_enable': + + if (!Array.isArray(req.params)) { + res.error = ethErrors.rpc.invalidParams({ data: req }) + return + } + + const result = { + accounts: [], + permissions: [], + plugins: {}, } + // request the permissions + try { - res.result = await handleInstallPlugins(req.origin, requestedPlugins) + // we expect the params to be the same as wallet_requestPermissions + result.permissions = await requestPermissions( + await preprocessRequestPermissions(req.params[0]) + ) } catch (err) { + // if this fails, reject the entire request res.error = err + return } + + // install plugins, if any + + // get the names of the approved plugins + const pluginPrefixRegex = new RegExp(`^${PLUGIN_PREFIX}`) + const requestedPlugins = result.permissions + .filter(p => p.parentCapability.startsWith(PLUGIN_PREFIX)) + .map(p => p.parentCapability.replace(pluginPrefixRegex, '')) + .reduce((acc, pluginName) => { + acc[pluginName] = {} + return acc + }, {}) + + console.log('requestedPlugins', requestedPlugins) + + try { + if (Object.keys(requestedPlugins).length > 0) { + // this throws if requestedPlugins is empty + result.plugins = await handleInstallPlugins( + req.origin, requestedPlugins + ) + } + } catch (err) { + if (!result.errors) { + result.errors = [] + } + result.errors.push(err) + } + + // get whatever accounts we have + result.accounts = await getAccounts() + + // return the result + res.result = result return - // custom method for getting metadata from the requesting domain + // miscellaneous methods + + // we received metadata about the requesting domain from its provider case 'wallet_sendDomainMetadata': if ( @@ -96,17 +194,19 @@ module.exports = function createRequestMiddleware ({ res.result = true return + // passthrough all other methods default: break } + // try to infer some metadata for unknown domains, including plugins if ( req.origin !== 'metamask' && ( getOwnState() && !getOwnState()[req.origin] ) ) { - // plugin metadata is handled here for now? - // TODO:plugin handle this better, rename siteMetadata to domainMetadata everywhere + // plugin metadata is handled here for now + // TODO:plugin handle this better, rename domainMetadata everywhere let name = 'Unknown Domain' try { name = new URL(req.origin).hostname @@ -114,9 +214,96 @@ module.exports = function createRequestMiddleware ({ addDomainMetadata(req.origin, { name }) } + // if we make it here, continue down the middleware stack return next() }) + // helper functions + + // preprocess requested permissions to support 'wallet_plugin' syntactic sugar + async function preprocessRequestPermissions (requestedPermissions) { + + if ( + typeof requestedPermissions !== 'object' || + Array.isArray(requestedPermissions) + ) { + throw ethErrors.rpc.invalidRequest({ data: { requestedPermissions } }) + } + + // passthrough if 'wallet_plugin' is not requested + if (!requestedPermissions['wallet_plugin']) { + return requestedPermissions + } + + // rewrite permissions request parameter by destructuring plugins into + // proper permissions prefixed with 'wallet_plugin_' + return Object.keys(requestedPermissions).reduce((acc, permName) => { + + if (permName === 'wallet_plugin') { + + if ( + typeof (requestedPermissions[permName]) !== 'object' || + Array.isArray(requestedPermissions[permName]) + ) { + throw ethErrors.rpc.invalidParams({ + message: `Invalid params to 'wallet_requestPermissions'`, + data: { requestedPermissions }, + }) + } + + const requestedPlugins = requestedPermissions[permName] + + // destructure 'wallet_plugin' object + Object.keys(requestedPlugins).forEach(pluginName => { + + const pluginKey = PLUGIN_PREFIX + pluginName + + // disallow requesting a plugin X under 'wallet_plugins' and + // directly as 'wallet_plugin_X' + if (requestedPermissions[pluginKey]) { + throw ethErrors.rpc.invalidParams({ + message: `Plugin '${pluginName}' requested both as direct permission and under 'wallet_plugin'. We recommend using 'wallet_plugin' only.`, + data: { requestedPermissions }, + }) + } + + acc[pluginKey] = requestedPlugins[pluginName] + }) + } else { + + // otherwise, leave things as we found them + acc[permName] = requestedPermissions[permName] + } + + return acc + }, {}) + } + + // typechecks the requested plugins and passes them to the permissions + // controller for installation + async function handleInstallPlugins (origin, requestedPlugins) { + + // we expect requestedPlugins to be an object of the form: + // { pluginName1: {}, pluginName2: {}, ... } + // the object values are placeholders + if ( + typeof requestedPlugins !== 'object' || + Array.isArray(requestedPlugins) + ) { + throw ethErrors.rpc.invalidParams({ + message: `Invalid params to 'wallet_installPlugins'`, + data: { requestedPlugins }, + }) + } else if (Object.keys(requestedPlugins).length === 0) { + throw ethErrors.rpc.invalidParams({ + message: `Must request at least one plugin when calling 'wallet_installPlugins'`, + data: { requestedPlugins }, + }) + } + + return installPlugins(origin, requestedPlugins) + } + function addDomainMetadata (origin, metadata) { // extensionId added higher up the stack, preserve it if it exists diff --git a/app/scripts/controllers/permissions/permissions-safe-methods.json b/app/scripts/controllers/permissions/permissions-safe-methods.json deleted file mode 100644 index 17b46b531..000000000 --- a/app/scripts/controllers/permissions/permissions-safe-methods.json +++ /dev/null @@ -1,49 +0,0 @@ -[ - "web3_sha3", - "net_listening", - "net_peerCount", - "net_version", - "eth_blockNumber", - "eth_call", - "eth_chainId", - "eth_coinbase", - "eth_estimateGas", - "eth_gasPrice", - "eth_getBalance", - "eth_getBlockByHash", - "eth_getBlockByNumber", - "eth_getBlockTransactionCountByHash", - "eth_getBlockTransactionCountByNumber", - "eth_getCode", - "eth_getFilterChanges", - "eth_getFilterLogs", - "eth_getLogs", - "eth_getStorageAt", - "eth_getTransactionByBlockHashAndIndex", - "eth_getTransactionByBlockNumberAndIndex", - "eth_getTransactionByHash", - "eth_getTransactionCount", - "eth_getTransactionReceipt", - "eth_getUncleByBlockHashAndIndex", - "eth_getUncleByBlockNumberAndIndex", - "eth_getUncleCountByBlockHash", - "eth_getUncleCountByBlockNumber", - "eth_getWork", - "eth_hashrate", - "eth_mining", - "eth_newBlockFilter", - "eth_newFilter", - "eth_newPendingTransactionFilter", - "eth_protocolVersion", - "eth_sendRawTransaction", - "eth_sendTransaction", - "eth_sign", - "personal_sign", - "eth_signTypedData", - "eth_signTypedData_v1", - "eth_signTypedData_v3", - "eth_submitHashrate", - "eth_submitWork", - "eth_syncing", - "eth_uninstallFilter" -] \ No newline at end of file diff --git a/app/scripts/controllers/plugins.js b/app/scripts/controllers/plugins.js index 4bd90752a..03397f476 100644 --- a/app/scripts/controllers/plugins.js +++ b/app/scripts/controllers/plugins.js @@ -142,37 +142,32 @@ class PluginsController extends EventEmitter { /** * Adds, authorizes, and runs plugins with a plugin provider. */ - async processRequestedPlugins (requestedPlugins) { + async processRequestedPlugin (pluginName) { - const pluginNames = requestedPlugins.map( - perm => perm.replace(/^wallet_plugin_/, '') - ) + const result = {} - const added = await Promise.all(pluginNames.map(async (pluginName) => { - try { - await this.add(pluginName) - const plugin = await this.authorize(pluginName) - const { sourceCode, approvedPermissions } = plugin - const ethereumProvider = this.setupProvider( - { hostname: pluginName }, async () => { - return {name: pluginName } - }, true - ) - await this.run( - pluginName, approvedPermissions, sourceCode, ethereumProvider - ) - return pluginName - } catch (err) { - // TODO: figure out what to do with this error - console.error(`Error when adding plugin:`, err) - } - })) - .catch((err) => { - // TODO: will this ever happen? - console.error(`Unexpected error when adding plugins:`, err) - }) + try { + + await this.add(pluginName) + const plugin = await this.authorize(pluginName) + const { sourceCode, approvedPermissions } = plugin + + const ethereumProvider = this.setupProvider( + { hostname: pluginName }, async () => { + return {name: pluginName } + }, true + ) + + await this.run( + pluginName, approvedPermissions, sourceCode, ethereumProvider + ) + } catch (err) { - return added ? added.filter(plugin => !!plugin) : [] + console.warn(`Error when adding plugin:`, err) + result.error = err + } + + return result } /** @@ -380,15 +375,28 @@ class PluginsController extends EventEmitter { } _startPlugin (pluginName, approvedPermissions, sourceCode, ethereumProvider) { - console.log(`starting plugin ${pluginName}`) + + console.log(`starting plugin '${pluginName}'`) + const apisToProvide = this._generateApisToProvide(approvedPermissions, pluginName) Object.assign(ethereumProvider, apisToProvide) + try { + const sessedPlugin = this.rootRealm.evaluate(sourceCode, { + wallet: ethereumProvider, console, // Adding console for now for logging purposes. BigInt, setTimeout, + crypto, + SubtleCrypto, + fetch, + XMLHttpRequest, + WebSocket, + Buffer, + Date, + window: { crypto, SubtleCrypto, @@ -397,19 +405,15 @@ class PluginsController extends EventEmitter { XMLHttpRequest, WebSocket, }, - crypto, - SubtleCrypto, - fetch, - XMLHttpRequest, - WebSocket, - Buffer, - Date, }) sessedPlugin() } catch (err) { + + console.log(`error encountered trying to run plugin '${pluginName}', removing it`) this.removePlugin(pluginName) throw err } + this._setPluginToActive(pluginName) return true } diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 409ce6876..756339b27 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -3,6 +3,7 @@ const { addInternalMethodPrefix } = require('./permissions') const normalizeAddress = require('eth-sig-util').normalize const { isValidAddress, sha3, bufferToHex } = require('ethereumjs-util') const extend = require('xtend') +const { ethErrors } = require('eth-json-rpc-errors') class PreferencesController { @@ -203,7 +204,11 @@ class PreferencesController { } break default: - end(new Error(`Asset of type ${type} not supported`)) + end(ethErrors.rpc.invalidParams({ + message: `Asset of type '${type}' not supported.`, + data: req, + })) + break } } else { next() diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 93939fa8d..5812fcc14 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -235,7 +235,7 @@ module.exports = class MetamaskController extends EventEmitter { getApi: this.getPluginsApi.bind(this), notifyDomain: this.notifyConnections.bind(this), notifyAllDomains: this.notifyAllConnections.bind(this), - }, initState.PermissionsController, initState.PermissionsMetadata) + }, initState.PermissionsMetadata) this.txController = new TransactionController({ initState: initState.TransactionController || initState.TransactionManager, diff --git a/ui/app/pages/settings/permissions-tab/permissions-activity/permissions-activity.component.js b/ui/app/pages/settings/permissions-tab/permissions-activity/permissions-activity.component.js index 8dd3dd1b4..332cb238f 100644 --- a/ui/app/pages/settings/permissions-tab/permissions-activity/permissions-activity.component.js +++ b/ui/app/pages/settings/permissions-tab/permissions-activity/permissions-activity.component.js @@ -76,8 +76,8 @@ export default class PermissionsActivity extends Component { } renderRpcResponse (e) { - if (!e.response) { - return 'This request did not produce a response.' + if (!e.response || !e.response.result) { + return 'This request did not produce a result.' } if (!e.success) { return this.renderRpcError(e.response.error) diff --git a/ui/app/pages/settings/permissions-tab/permissions-tab.component.js b/ui/app/pages/settings/permissions-tab/permissions-tab.component.js index dcd25dd4e..ad03c878e 100644 --- a/ui/app/pages/settings/permissions-tab/permissions-tab.component.js +++ b/ui/app/pages/settings/permissions-tab/permissions-tab.component.js @@ -24,14 +24,13 @@ export default class PermissionsTab extends Component { t: PropTypes.func, } - renderClearButton (messageKey, clickHandler, isDisabled) { - const { t } = this.context + renderClearButton (mainMessage, descriptionMessage, clickHandler, isDisabled) { return (
- { t(messageKey) } + { mainMessage } - { t(`${messageKey}Description`) } + { descriptionMessage }
@@ -46,7 +45,7 @@ export default class PermissionsTab extends Component { clickHandler() }} > - { t(messageKey) } + { mainMessage }
@@ -55,6 +54,7 @@ export default class PermissionsTab extends Component { } render () { + const { t } = this.context const { warning } = this.props const hasPermissions = Object.keys(this.props.permissions).length > 0 const hasPermissionsActivity = this.props.permissionsLog.length > 0 @@ -71,7 +71,8 @@ export default class PermissionsTab extends Component { /> { this.renderClearButton( - 'clearPermissions', + t('clearPermissions'), + t('clearPermissionsDescription'), this.props.showClearPermissionsModal, !hasPermissions ) @@ -81,7 +82,8 @@ export default class PermissionsTab extends Component { /> { this.renderClearButton( - 'clearPermissionsHistory', + t('clearPermissionsHistory'), + t('clearPermissionsHistoryDescription'), this.props.showClearPermissionsHistoryModal, !hasPermissionsHistory ) @@ -91,7 +93,8 @@ export default class PermissionsTab extends Component { /> { this.renderClearButton( - 'clearPermissionsActivity', + t('clearPermissionsActivity'), + t('clearPermissionsActivityDescription'), this.props.showClearPermissionsActivityModal, !hasPermissionsActivity ) diff --git a/ui/app/pages/settings/plugins-tab/plugins-tab.component.js b/ui/app/pages/settings/plugins-tab/plugins-tab.component.js index 01a917be0..9b146ebcb 100644 --- a/ui/app/pages/settings/plugins-tab/plugins-tab.component.js +++ b/ui/app/pages/settings/plugins-tab/plugins-tab.component.js @@ -16,14 +16,13 @@ export default class PluginsTab extends Component { t: PropTypes.func, } - renderClearButton (messageKey, clickHandler, isDisabled) { - const { t } = this.context + renderClearButton (mainMessage, descriptionMessage, clickHandler, isDisabled) { return (
- { t(messageKey) } + { mainMessage } - { t(`${messageKey}Description`) } + { descriptionMessage }
@@ -38,7 +37,7 @@ export default class PluginsTab extends Component { clickHandler() }} > - { t(messageKey) } + { mainMessage }
@@ -47,6 +46,7 @@ export default class PluginsTab extends Component { } render () { + const { t } = this.context const { warning } = this.props const hasPlugins = Object.keys(this.props.plugins).length > 0 @@ -59,7 +59,8 @@ export default class PluginsTab extends Component { /> { this.renderClearButton( - 'clearPlugins', + t('clearPlugins'), + t('clearPluginsDescription'), this.props.showClearPluginsModal, !hasPlugins ) diff --git a/yarn.lock b/yarn.lock index 49657f2ba..1cf62ba5d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18267,7 +18267,7 @@ mersenne-twister@^1.0.1: metamask-inpage-provider@MetaMask/metamask-inpage-provider#snaps: version "5.0.0" - resolved "https://codeload.github.com/MetaMask/metamask-inpage-provider/tar.gz/38abec68d77364fd561a74ab7c60b54982d406d8" + resolved "https://codeload.github.com/MetaMask/metamask-inpage-provider/tar.gz/abe916e743f645a8da5ed2d5aa938f5cadcd4dc1" dependencies: capnode "^4.0.2" eth-json-rpc-errors "^2.0.0"