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",
|
||||
"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": {
|
||||
"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."
|
||||
|
@ -30,6 +30,9 @@ export default class PreferencesController {
|
||||
useNonceField: false,
|
||||
usePhishDetect: true,
|
||||
dismissSeedBackUpReminder: false,
|
||||
disabledRpcMethodPreferences: {
|
||||
eth_sign: false,
|
||||
},
|
||||
useMultiAccountBalanceChecker: true,
|
||||
|
||||
// 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.store = new ObservableStore(initState);
|
||||
this.store.setMaxListeners(12);
|
||||
this.store.setMaxListeners(13);
|
||||
this.openPopup = opts.openPopup;
|
||||
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
|
||||
//
|
||||
|
@ -1824,6 +1824,14 @@ export default class MetamaskController extends EventEmitter {
|
||||
preferencesController.setDismissSeedBackUpReminder.bind(
|
||||
preferencesController,
|
||||
),
|
||||
setDisabledRpcMethodPreference:
|
||||
preferencesController.setDisabledRpcMethodPreference.bind(
|
||||
preferencesController,
|
||||
),
|
||||
getRpcMethodPreferences:
|
||||
preferencesController.getRpcMethodPreferences.bind(
|
||||
preferencesController,
|
||||
),
|
||||
setAdvancedGasFee: preferencesController.setAdvancedGasFee.bind(
|
||||
preferencesController,
|
||||
),
|
||||
@ -3046,8 +3054,19 @@ export default class MetamaskController extends EventEmitter {
|
||||
* @param {object} [req] - The original request, containing the origin.
|
||||
*/
|
||||
async newUnsignedMessage(msgParams, req) {
|
||||
const { disabledRpcMethodPreferences } =
|
||||
this.preferencesController.store.getState();
|
||||
const { eth_sign } = disabledRpcMethodPreferences; // eslint-disable-line camelcase
|
||||
const data = normalizeMsgData(msgParams.data);
|
||||
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
|
||||
// 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
|
||||
|
@ -872,6 +872,10 @@ describe('MetaMaskController', function () {
|
||||
data,
|
||||
};
|
||||
|
||||
metamaskController.preferencesController.setDisabledRpcMethodPreference(
|
||||
'eth_sign',
|
||||
true,
|
||||
);
|
||||
const promise = metamaskController.newUnsignedMessage(msgParams);
|
||||
// handle the promise so it doesn't throw an unhandledRejection
|
||||
promise.then(noop).catch(noop);
|
||||
|
@ -45,6 +45,9 @@
|
||||
"metamask": {
|
||||
"ipfsGateway": "",
|
||||
"dismissSeedBackUpReminder": false,
|
||||
"disabledRpcMethodPreferences": {
|
||||
"eth_sign": false
|
||||
},
|
||||
"usePhishDetect": true,
|
||||
"participateInMetaMetrics": false,
|
||||
"gasEstimateType": "fee-market",
|
||||
|
@ -2,8 +2,6 @@ const { strict: assert } = require('assert');
|
||||
const { convertToHexValue, withFixtures } = require('../helpers');
|
||||
const FixtureBuilder = require('../fixture-builder');
|
||||
|
||||
describe('Eth sign', function () {
|
||||
it('can initiate and confirm a eth sign', async function () {
|
||||
const ganacheOptions = {
|
||||
accounts: [
|
||||
{
|
||||
@ -13,10 +11,9 @@ describe('Eth sign', function () {
|
||||
},
|
||||
],
|
||||
};
|
||||
const expectedPersonalMessage =
|
||||
'0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0';
|
||||
const expectedEthSignResult =
|
||||
'"0x816ab6c5d5356548cc4e004ef35a37fdfab916742a2bbeda756cd064c3d3789a6557d41d49549be1de249e1937a8d048996dfcc70d0552111605dc7cc471e8531b"';
|
||||
|
||||
describe('Eth sign', function () {
|
||||
it('will detect if eth_sign is disabled', async function () {
|
||||
await withFixtures(
|
||||
{
|
||||
dapp: true,
|
||||
@ -34,6 +31,43 @@ describe('Eth sign', function () {
|
||||
await driver.openNewPage('http://127.0.0.1:8080/');
|
||||
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);
|
||||
let windowHandles = await driver.getAllWindowHandles();
|
||||
await driver.switchToWindowWithTitle(
|
||||
|
@ -132,6 +132,13 @@ export const SETTINGS_CONSTANTS = [
|
||||
route: `${ADVANCED_ROUTE}#dimiss-secretrecovery`,
|
||||
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'),
|
||||
sectionMessage: (t) => t('contacts'),
|
||||
|
@ -159,7 +159,7 @@ describe('Settings Search Utils', () => {
|
||||
});
|
||||
|
||||
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', () => {
|
||||
|
@ -56,6 +56,10 @@ export default class AdvancedTab extends PureComponent {
|
||||
userHasALedgerAccount: PropTypes.bool.isRequired,
|
||||
backupUserData: PropTypes.func.isRequired,
|
||||
restoreUserData: PropTypes.func.isRequired,
|
||||
setDisabledRpcMethodPreference: PropTypes.func.isRequired,
|
||||
disabledRpcMethodPreferences: PropTypes.shape({
|
||||
eth_sign: PropTypes.bool.isRequired,
|
||||
}),
|
||||
};
|
||||
|
||||
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) {
|
||||
const { t } = this.context;
|
||||
const autoLockTimeLimit = Math.max(Number(time), 0);
|
||||
@ -711,6 +748,7 @@ export default class AdvancedTab extends PureComponent {
|
||||
{this.renderRestoreUserData()}
|
||||
{notUsingFirefox ? this.renderLedgerLiveControl() : null}
|
||||
{this.renderDismissSeedBackupReminderControl()}
|
||||
{this.renderToggleEthSignControl()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import {
|
||||
setUseNonceField,
|
||||
setLedgerTransportPreference,
|
||||
setDismissSeedBackUpReminder,
|
||||
setDisabledRpcMethodPreference,
|
||||
backupUserData,
|
||||
restoreUserData,
|
||||
} from '../../../store/actions';
|
||||
@ -25,6 +26,7 @@ export const mapStateToProps = (state) => {
|
||||
} = state;
|
||||
const {
|
||||
featureFlags: { sendHexData, advancedInlineGas } = {},
|
||||
disabledRpcMethodPreferences,
|
||||
useNonceField,
|
||||
ledgerTransportType,
|
||||
dismissSeedBackUpReminder,
|
||||
@ -48,6 +50,7 @@ export const mapStateToProps = (state) => {
|
||||
ledgerTransportType,
|
||||
dismissSeedBackUpReminder,
|
||||
userHasALedgerAccount,
|
||||
disabledRpcMethodPreferences,
|
||||
};
|
||||
};
|
||||
|
||||
@ -78,6 +81,9 @@ export const mapDispatchToProps = (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<
|
||||
void,
|
||||
MetaMaskReduxState,
|
||||
|
Loading…
Reference in New Issue
Block a user