mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 09:57:02 +01:00
Disable eth_sign by default, allow users to toggle it back on (#17308)
* Added translation for eth sign toggle * Disabled the ability to call eth_sign by default. Added ability within advanced settings to renable support for eth_sign * Add test case for eth_sign being enabled and disabled * Modified copy * Moved rpc method preference to its own object within store * Complete work on moving rpc method preference into its own object within store * Fix with prettier * Fix lint * Fix a unit test * Fix test * Renamed function and object keys to be more intuitive * Fix e2e test * Enabled eth_sign through preferences fixture * Fix lint * Fix e2e test Wait for the notification popup to close, leaving 2 window handles the extension and the test dapp * Fix e2e test * Fix unit test Enable eth_sign method * Lint fix --------- Co-authored-by: PeterYinusa <peter.yinusa@consensys.net> Co-authored-by: Dan J Miller <danjm.com@gmail.com>
This commit is contained in:
parent
080d72abed
commit
b4ecc4c3f5
6
app/_locales/en/messages.json
generated
6
app/_locales/en/messages.json
generated
@ -3971,6 +3971,12 @@
|
|||||||
"message": "To: $1",
|
"message": "To: $1",
|
||||||
"description": "$1 is the address to include in the To label. It is typically shortened first using shortenAddress"
|
"description": "$1 is the address to include in the To label. It is typically shortened first using shortenAddress"
|
||||||
},
|
},
|
||||||
|
"toggleEthSignDescriptionField": {
|
||||||
|
"message": "Turn this on to let dapps request your signature using eth_sign requests. eth_sign is an open-ended signing method that lets you sign an arbitrary hash, making it a dangerous phishing risk. Only sign eth_sign requests if you can read what you are signing and trust the origin of the request."
|
||||||
|
},
|
||||||
|
"toggleEthSignField": {
|
||||||
|
"message": "Toggle eth_sign requests"
|
||||||
|
},
|
||||||
"toggleTestNetworks": {
|
"toggleTestNetworks": {
|
||||||
"message": "$1 test networks",
|
"message": "$1 test networks",
|
||||||
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open Settings > Advanced where users can enable the display of test networks in the network dropdown."
|
"description": "$1 is a clickable link with text defined by the 'showHide' key. The link will open Settings > Advanced where users can enable the display of test networks in the network dropdown."
|
||||||
|
@ -30,6 +30,9 @@ export default class PreferencesController {
|
|||||||
useNonceField: false,
|
useNonceField: false,
|
||||||
usePhishDetect: true,
|
usePhishDetect: true,
|
||||||
dismissSeedBackUpReminder: false,
|
dismissSeedBackUpReminder: false,
|
||||||
|
disabledRpcMethodPreferences: {
|
||||||
|
eth_sign: false,
|
||||||
|
},
|
||||||
useMultiAccountBalanceChecker: true,
|
useMultiAccountBalanceChecker: true,
|
||||||
|
|
||||||
// set to true means the dynamic list from the API is being used
|
// set to true means the dynamic list from the API is being used
|
||||||
@ -72,7 +75,7 @@ export default class PreferencesController {
|
|||||||
|
|
||||||
this.network = opts.network;
|
this.network = opts.network;
|
||||||
this.store = new ObservableStore(initState);
|
this.store = new ObservableStore(initState);
|
||||||
this.store.setMaxListeners(12);
|
this.store.setMaxListeners(13);
|
||||||
this.openPopup = opts.openPopup;
|
this.openPopup = opts.openPopup;
|
||||||
this.tokenListController = opts.tokenListController;
|
this.tokenListController = opts.tokenListController;
|
||||||
|
|
||||||
@ -544,6 +547,29 @@ export default class PreferencesController {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A setter for the user preference to enable/disable rpc methods
|
||||||
|
*
|
||||||
|
* @param {string} methodName - The RPC method name to change the setting of
|
||||||
|
* @param {bool} isEnabled - true to enable the rpc method
|
||||||
|
*/
|
||||||
|
async setDisabledRpcMethodPreference(methodName, isEnabled) {
|
||||||
|
const currentRpcMethodPreferences =
|
||||||
|
this.store.getState().disabledRpcMethodPreferences;
|
||||||
|
const updatedRpcMethodPreferences = {
|
||||||
|
...currentRpcMethodPreferences,
|
||||||
|
[methodName]: isEnabled,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.store.updateState({
|
||||||
|
disabledRpcMethodPreferences: updatedRpcMethodPreferences,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getRpcMethodPreferences() {
|
||||||
|
return this.store.getState().disabledRpcMethodPreferences;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// PRIVATE METHODS
|
// PRIVATE METHODS
|
||||||
//
|
//
|
||||||
|
@ -1824,6 +1824,14 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
preferencesController.setDismissSeedBackUpReminder.bind(
|
preferencesController.setDismissSeedBackUpReminder.bind(
|
||||||
preferencesController,
|
preferencesController,
|
||||||
),
|
),
|
||||||
|
setDisabledRpcMethodPreference:
|
||||||
|
preferencesController.setDisabledRpcMethodPreference.bind(
|
||||||
|
preferencesController,
|
||||||
|
),
|
||||||
|
getRpcMethodPreferences:
|
||||||
|
preferencesController.getRpcMethodPreferences.bind(
|
||||||
|
preferencesController,
|
||||||
|
),
|
||||||
setAdvancedGasFee: preferencesController.setAdvancedGasFee.bind(
|
setAdvancedGasFee: preferencesController.setAdvancedGasFee.bind(
|
||||||
preferencesController,
|
preferencesController,
|
||||||
),
|
),
|
||||||
@ -3046,8 +3054,19 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
* @param {object} [req] - The original request, containing the origin.
|
* @param {object} [req] - The original request, containing the origin.
|
||||||
*/
|
*/
|
||||||
async newUnsignedMessage(msgParams, req) {
|
async newUnsignedMessage(msgParams, req) {
|
||||||
|
const { disabledRpcMethodPreferences } =
|
||||||
|
this.preferencesController.store.getState();
|
||||||
|
const { eth_sign } = disabledRpcMethodPreferences; // eslint-disable-line camelcase
|
||||||
const data = normalizeMsgData(msgParams.data);
|
const data = normalizeMsgData(msgParams.data);
|
||||||
let promise;
|
let promise;
|
||||||
|
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
if (!eth_sign) {
|
||||||
|
throw ethErrors.rpc.methodNotFound(
|
||||||
|
'eth_sign has been disabled. You must enable it in the advanced settings',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// 64 hex + "0x" at the beginning
|
// 64 hex + "0x" at the beginning
|
||||||
// This is needed because Ethereum's EcSign works only on 32 byte numbers
|
// This is needed because Ethereum's EcSign works only on 32 byte numbers
|
||||||
// For 67 length see: https://github.com/MetaMask/metamask-extension/pull/12679/files#r749479607
|
// For 67 length see: https://github.com/MetaMask/metamask-extension/pull/12679/files#r749479607
|
||||||
|
@ -872,6 +872,10 @@ describe('MetaMaskController', function () {
|
|||||||
data,
|
data,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
metamaskController.preferencesController.setDisabledRpcMethodPreference(
|
||||||
|
'eth_sign',
|
||||||
|
true,
|
||||||
|
);
|
||||||
const promise = metamaskController.newUnsignedMessage(msgParams);
|
const promise = metamaskController.newUnsignedMessage(msgParams);
|
||||||
// handle the promise so it doesn't throw an unhandledRejection
|
// handle the promise so it doesn't throw an unhandledRejection
|
||||||
promise.then(noop).catch(noop);
|
promise.then(noop).catch(noop);
|
||||||
|
@ -45,6 +45,9 @@
|
|||||||
"metamask": {
|
"metamask": {
|
||||||
"ipfsGateway": "",
|
"ipfsGateway": "",
|
||||||
"dismissSeedBackUpReminder": false,
|
"dismissSeedBackUpReminder": false,
|
||||||
|
"disabledRpcMethodPreferences": {
|
||||||
|
"eth_sign": false
|
||||||
|
},
|
||||||
"usePhishDetect": true,
|
"usePhishDetect": true,
|
||||||
"participateInMetaMetrics": false,
|
"participateInMetaMetrics": false,
|
||||||
"gasEstimateType": "fee-market",
|
"gasEstimateType": "fee-market",
|
||||||
|
@ -2,8 +2,6 @@ const { strict: assert } = require('assert');
|
|||||||
const { convertToHexValue, withFixtures } = require('../helpers');
|
const { convertToHexValue, withFixtures } = require('../helpers');
|
||||||
const FixtureBuilder = require('../fixture-builder');
|
const FixtureBuilder = require('../fixture-builder');
|
||||||
|
|
||||||
describe('Eth sign', function () {
|
|
||||||
it('can initiate and confirm a eth sign', async function () {
|
|
||||||
const ganacheOptions = {
|
const ganacheOptions = {
|
||||||
accounts: [
|
accounts: [
|
||||||
{
|
{
|
||||||
@ -13,10 +11,9 @@ describe('Eth sign', function () {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
const expectedPersonalMessage =
|
|
||||||
'0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0';
|
describe('Eth sign', function () {
|
||||||
const expectedEthSignResult =
|
it('will detect if eth_sign is disabled', async function () {
|
||||||
'"0x816ab6c5d5356548cc4e004ef35a37fdfab916742a2bbeda756cd064c3d3789a6557d41d49549be1de249e1937a8d048996dfcc70d0552111605dc7cc471e8531b"';
|
|
||||||
await withFixtures(
|
await withFixtures(
|
||||||
{
|
{
|
||||||
dapp: true,
|
dapp: true,
|
||||||
@ -34,6 +31,43 @@ describe('Eth sign', function () {
|
|||||||
await driver.openNewPage('http://127.0.0.1:8080/');
|
await driver.openNewPage('http://127.0.0.1:8080/');
|
||||||
await driver.clickElement('#ethSign');
|
await driver.clickElement('#ethSign');
|
||||||
|
|
||||||
|
const ethSignButton = await driver.findElement('#ethSign');
|
||||||
|
const exceptionString =
|
||||||
|
'ERROR: ETH_SIGN HAS BEEN DISABLED. YOU MUST ENABLE IT IN THE ADVANCED SETTINGS';
|
||||||
|
|
||||||
|
assert.equal(await ethSignButton.getText(), exceptionString);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can initiate and confirm a eth sign', async function () {
|
||||||
|
const expectedPersonalMessage =
|
||||||
|
'0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0';
|
||||||
|
const expectedEthSignResult =
|
||||||
|
'"0x816ab6c5d5356548cc4e004ef35a37fdfab916742a2bbeda756cd064c3d3789a6557d41d49549be1de249e1937a8d048996dfcc70d0552111605dc7cc471e8531b"';
|
||||||
|
await withFixtures(
|
||||||
|
{
|
||||||
|
dapp: true,
|
||||||
|
fixtures: new FixtureBuilder()
|
||||||
|
.withPreferencesController({
|
||||||
|
disabledRpcMethodPreferences: {
|
||||||
|
eth_sign: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.withPermissionControllerConnectedToTestDapp()
|
||||||
|
.build(),
|
||||||
|
ganacheOptions,
|
||||||
|
title: this.test.title,
|
||||||
|
},
|
||||||
|
async ({ driver }) => {
|
||||||
|
await driver.navigate();
|
||||||
|
await driver.fill('#password', 'correct horse battery staple');
|
||||||
|
await driver.press('#password', driver.Key.ENTER);
|
||||||
|
|
||||||
|
await driver.openNewPage('http://127.0.0.1:8080/');
|
||||||
|
await driver.clickElement('#ethSign');
|
||||||
|
|
||||||
|
// Wait for Signature request popup
|
||||||
await driver.waitUntilXWindowHandles(3);
|
await driver.waitUntilXWindowHandles(3);
|
||||||
let windowHandles = await driver.getAllWindowHandles();
|
let windowHandles = await driver.getAllWindowHandles();
|
||||||
await driver.switchToWindowWithTitle(
|
await driver.switchToWindowWithTitle(
|
||||||
|
@ -132,6 +132,13 @@ export const SETTINGS_CONSTANTS = [
|
|||||||
route: `${ADVANCED_ROUTE}#dimiss-secretrecovery`,
|
route: `${ADVANCED_ROUTE}#dimiss-secretrecovery`,
|
||||||
icon: 'fas fa-sliders-h',
|
icon: 'fas fa-sliders-h',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
tabMessage: (t) => t('advanced'),
|
||||||
|
sectionMessage: (t) => t('toggleEthSignField'),
|
||||||
|
descriptionMessage: (t) => t('toggleEthSignDescriptionField'),
|
||||||
|
route: `${ADVANCED_ROUTE}#toggle-ethsign`,
|
||||||
|
icon: 'fas fa-sliders-h',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
tabMessage: (t) => t('contacts'),
|
tabMessage: (t) => t('contacts'),
|
||||||
sectionMessage: (t) => t('contacts'),
|
sectionMessage: (t) => t('contacts'),
|
||||||
|
@ -159,7 +159,7 @@ describe('Settings Search Utils', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should get good advanced section number', () => {
|
it('should get good advanced section number', () => {
|
||||||
expect(getNumberOfSettingsInSection(t, t('advanced'))).toStrictEqual(13);
|
expect(getNumberOfSettingsInSection(t, t('advanced'))).toStrictEqual(14);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should get good contact section number', () => {
|
it('should get good contact section number', () => {
|
||||||
|
@ -56,6 +56,10 @@ export default class AdvancedTab extends PureComponent {
|
|||||||
userHasALedgerAccount: PropTypes.bool.isRequired,
|
userHasALedgerAccount: PropTypes.bool.isRequired,
|
||||||
backupUserData: PropTypes.func.isRequired,
|
backupUserData: PropTypes.func.isRequired,
|
||||||
restoreUserData: PropTypes.func.isRequired,
|
restoreUserData: PropTypes.func.isRequired,
|
||||||
|
setDisabledRpcMethodPreference: PropTypes.func.isRequired,
|
||||||
|
disabledRpcMethodPreferences: PropTypes.shape({
|
||||||
|
eth_sign: PropTypes.bool.isRequired,
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
@ -580,6 +584,39 @@ export default class AdvancedTab extends PureComponent {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderToggleEthSignControl() {
|
||||||
|
const { t } = this.context;
|
||||||
|
const { disabledRpcMethodPreferences, setDisabledRpcMethodPreference } =
|
||||||
|
this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={this.settingsRefs[10]}
|
||||||
|
className="settings-page__content-row"
|
||||||
|
data-testid="advanced-setting-toggle-ethsign"
|
||||||
|
>
|
||||||
|
<div className="settings-page__content-item">
|
||||||
|
<span>{t('toggleEthSignField')}</span>
|
||||||
|
<div className="settings-page__content-description">
|
||||||
|
{t('toggleEthSignDescriptionField')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="settings-page__content-item">
|
||||||
|
<div className="settings-page__content-item-col">
|
||||||
|
<ToggleButton
|
||||||
|
value={disabledRpcMethodPreferences?.eth_sign || false}
|
||||||
|
onToggle={(value) =>
|
||||||
|
setDisabledRpcMethodPreference('eth_sign', !value)
|
||||||
|
}
|
||||||
|
offLabel={t('off')}
|
||||||
|
onLabel={t('on')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
handleLockChange(time) {
|
handleLockChange(time) {
|
||||||
const { t } = this.context;
|
const { t } = this.context;
|
||||||
const autoLockTimeLimit = Math.max(Number(time), 0);
|
const autoLockTimeLimit = Math.max(Number(time), 0);
|
||||||
@ -711,6 +748,7 @@ export default class AdvancedTab extends PureComponent {
|
|||||||
{this.renderRestoreUserData()}
|
{this.renderRestoreUserData()}
|
||||||
{notUsingFirefox ? this.renderLedgerLiveControl() : null}
|
{notUsingFirefox ? this.renderLedgerLiveControl() : null}
|
||||||
{this.renderDismissSeedBackupReminderControl()}
|
{this.renderDismissSeedBackupReminderControl()}
|
||||||
|
{this.renderToggleEthSignControl()}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
setUseNonceField,
|
setUseNonceField,
|
||||||
setLedgerTransportPreference,
|
setLedgerTransportPreference,
|
||||||
setDismissSeedBackUpReminder,
|
setDismissSeedBackUpReminder,
|
||||||
|
setDisabledRpcMethodPreference,
|
||||||
backupUserData,
|
backupUserData,
|
||||||
restoreUserData,
|
restoreUserData,
|
||||||
} from '../../../store/actions';
|
} from '../../../store/actions';
|
||||||
@ -25,6 +26,7 @@ export const mapStateToProps = (state) => {
|
|||||||
} = state;
|
} = state;
|
||||||
const {
|
const {
|
||||||
featureFlags: { sendHexData, advancedInlineGas } = {},
|
featureFlags: { sendHexData, advancedInlineGas } = {},
|
||||||
|
disabledRpcMethodPreferences,
|
||||||
useNonceField,
|
useNonceField,
|
||||||
ledgerTransportType,
|
ledgerTransportType,
|
||||||
dismissSeedBackUpReminder,
|
dismissSeedBackUpReminder,
|
||||||
@ -48,6 +50,7 @@ export const mapStateToProps = (state) => {
|
|||||||
ledgerTransportType,
|
ledgerTransportType,
|
||||||
dismissSeedBackUpReminder,
|
dismissSeedBackUpReminder,
|
||||||
userHasALedgerAccount,
|
userHasALedgerAccount,
|
||||||
|
disabledRpcMethodPreferences,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -78,6 +81,9 @@ export const mapDispatchToProps = (dispatch) => {
|
|||||||
setDismissSeedBackUpReminder: (value) => {
|
setDismissSeedBackUpReminder: (value) => {
|
||||||
return dispatch(setDismissSeedBackUpReminder(value));
|
return dispatch(setDismissSeedBackUpReminder(value));
|
||||||
},
|
},
|
||||||
|
setDisabledRpcMethodPreference: (methodName, isEnabled) => {
|
||||||
|
return dispatch(setDisabledRpcMethodPreference(methodName, isEnabled));
|
||||||
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3830,6 +3830,33 @@ export function setDismissSeedBackUpReminder(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setDisabledRpcMethodPreference(
|
||||||
|
methodName: string,
|
||||||
|
value: number,
|
||||||
|
): ThunkAction<void, MetaMaskReduxState, unknown, AnyAction> {
|
||||||
|
return async (dispatch) => {
|
||||||
|
dispatch(showLoadingIndication());
|
||||||
|
await submitRequestToBackground('setDisabledRpcMethodPreference', [
|
||||||
|
methodName,
|
||||||
|
value,
|
||||||
|
]);
|
||||||
|
dispatch(hideLoadingIndication());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRpcMethodPreferences(): ThunkAction<
|
||||||
|
void,
|
||||||
|
MetaMaskReduxState,
|
||||||
|
unknown,
|
||||||
|
AnyAction
|
||||||
|
> {
|
||||||
|
return async (dispatch) => {
|
||||||
|
dispatch(showLoadingIndication());
|
||||||
|
await submitRequestToBackground('getRpcMethodPreferences', []);
|
||||||
|
dispatch(hideLoadingIndication());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function setConnectedStatusPopoverHasBeenShown(): ThunkAction<
|
export function setConnectedStatusPopoverHasBeenShown(): ThunkAction<
|
||||||
void,
|
void,
|
||||||
MetaMaskReduxState,
|
MetaMaskReduxState,
|
||||||
|
Loading…
Reference in New Issue
Block a user