Skip to content

Commit

Permalink
Merge branch 'main' into fix/circular-deps-networks-engine
Browse files Browse the repository at this point in the history
  • Loading branch information
tommasini authored Nov 29, 2024
2 parents 1905fdc + a6e7175 commit 7b4acc9
Show file tree
Hide file tree
Showing 20 changed files with 431 additions and 29 deletions.
2 changes: 2 additions & 0 deletions .e2e.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ export MM_TEST_ACCOUNT_SRP='word1 word... word12'
export MM_TEST_ACCOUNT_ADDRESS='0x...'
export MM_TEST_ACCOUNT_PRIVATE_KEY=''
export IS_TEST="true"
# Temporary mechanism to enable security alerts API prior to release.
export MM_SECURITY_ALERTS_API_ENABLED="true"
Original file line number Diff line number Diff line change
Expand Up @@ -1386,7 +1386,7 @@ class Confirm extends PureComponent {
</Text>
<Text
style={styles.textAmount}
testID={TransactionConfirmViewSelectorsIDs.COMFIRM_TXN_AMOUNT}
testID={TransactionConfirmViewSelectorsIDs.CONFIRM_TXN_AMOUNT}
>
{transactionValue}
</Text>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { BLOCKAID_SUPPORTED_NETWORK_NAMES } from '../../../../../util/networks';
import BlockaidVersionInfo from '../../../../../lib/ppom/blockaid-version';
import { WALLET_CONNECT_ORIGIN } from '../../../../../util/walletconnect';
import AppConstants from '../../../../../core/AppConstants';
import { ConfirmationTopSheetSelectorsIDs } from '../../../../../../e2e/selectors/ConfirmationView.selectors';

const getReportUrl = (encodedData: string) =>
`${FALSE_POSITIVE_REPORT_BASE_URL}?data=${encodeURIComponent(
Expand Down Expand Up @@ -123,6 +124,7 @@ const BlockaidBanner = (bannerProps: BlockaidBannerProps) => {
severity={BannerAlertSeverity.Warning}
title={title}
description={description}
testID={ConfirmationTopSheetSelectorsIDs.SECURITY_ALERT_RESPONSE_FAILED_BANNER}
/>
</View>
);
Expand Down Expand Up @@ -177,6 +179,7 @@ const BlockaidBanner = (bannerProps: BlockaidBannerProps) => {
title={title}
description={description}
{...bannerProps}
testID={ConfirmationTopSheetSelectorsIDs.SECURITY_ALERT_BANNER}
>
{renderDetails()}
</BannerAlert>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ exports[`BlockaidBanner should render correctly 1`] = `
"paddingLeft": 8,
}
}
testID="banneralert"
testID="security-alert-banner"
>
<View
style={
Expand Down Expand Up @@ -190,7 +190,7 @@ exports[`BlockaidBanner should render correctly with list attack details 1`] = `
"paddingLeft": 8,
}
}
testID="banneralert"
testID="security-alert-banner"
>
<View
style={
Expand Down Expand Up @@ -343,7 +343,7 @@ exports[`BlockaidBanner should render correctly with reason "raw_signature_farmi
"paddingLeft": 8,
}
}
testID="banneralert"
testID="security-alert-banner"
>
<View
style={
Expand Down Expand Up @@ -505,7 +505,7 @@ exports[`BlockaidBanner should render normal banner alert if resultType is faile
"paddingLeft": 8,
}
}
testID="banneralert"
testID="security-alert-response-failed-banner"
>
<View
style={
Expand Down Expand Up @@ -598,7 +598,7 @@ exports[`BlockaidBanner should render something does not look right with contact
"paddingLeft": 8,
}
}
testID="banneralert"
testID="security-alert-banner"
>
<View
style={
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`TransactionBlockaidBanner should not render if transactionId passed is undefined 1`] = `null`;

exports[`TransactionBlockaidBanner should not render if securityAlertResponses.id is undefined 1`] = `null`;

exports[`TransactionBlockaidBanner should not render if transactionId passed is undefined 1`] = `null`;

exports[`TransactionBlockaidBanner should render correctly 1`] = `
<View
securityAlertResponse={
Expand All @@ -26,7 +26,7 @@ exports[`TransactionBlockaidBanner should render correctly 1`] = `
"paddingLeft": 8,
}
}
testID="banneralert"
testID="security-alert-banner"
>
<View
style={
Expand Down
57 changes: 54 additions & 3 deletions app/lib/ppom/ppom-util.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ import {
RpcEndpointType,
} from '@metamask/network-controller';
import { NETWORKS_CHAIN_ID } from '../../constants/network';
import {
Reason,
ResultType,
SecurityAlertSource,
} from '../../components/Views/confirmations/components/BlockaidBanner/BlockaidBanner.types';
import Logger from '../../util/Logger';

const CHAIN_ID_MOCK = '0x1';

Expand Down Expand Up @@ -176,8 +182,8 @@ describe('PPOM Utils', () => {
MockEngine.context.PreferencesController.state.securityAlertsEnabled =
false;
await PPOMUtil.validateRequest(mockRequest, CHAIN_ID_MOCK);
expect(MockEngine.context.PPOMController?.usePPOM).toBeCalledTimes(0);
expect(spyTransactionAction).toBeCalledTimes(0);
expect(MockEngine.context.PPOMController?.usePPOM).toHaveBeenCalledTimes(0);
expect(spyTransactionAction).toHaveBeenCalledTimes(0);
});

it('should not validate if request is send to users own account ', async () => {
Expand Down Expand Up @@ -307,6 +313,22 @@ describe('PPOM Utils', () => {
});
});

it('logs error if normalization fails', async () => {
const error = new Error('Test Error');
normalizeTransactionParamsMock.mockImplementation(() => {
throw error;
});

const spyLogger = jest.spyOn(Logger, 'log');

await PPOMUtil.validateRequest(mockRequest, CHAIN_ID_MOCK);

expect(spyLogger).toHaveBeenCalledTimes(1);
expect(spyLogger).toHaveBeenCalledWith(
`Error validating JSON RPC using PPOM: ${error}`,
);
});

it('normalizes transaction request origin before validation', async () => {
const validateMock = jest.fn();

Expand Down Expand Up @@ -385,5 +407,34 @@ describe('PPOM Utils', () => {
await PPOMUtil.validateRequest(mockRequest, CHAIN_ID_MOCK);
expect(spy).toHaveBeenCalledTimes(2);
});

it('sets security alerts response to failed when security alerts API and controller PPOM throws', async () => {
const spy = jest.spyOn(
TransactionActions,
'setTransactionSecurityAlertResponse',
);

const validateMock = new Error('Test Error');

const ppomMock = {
validateJsonRpc: validateMock,
};

MockEngine.context.PPOMController?.usePPOM.mockImplementation(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(callback: any) => callback(ppomMock),
);

await PPOMUtil.validateRequest(mockRequest, CHAIN_ID_MOCK);
expect(spy).toHaveBeenCalledTimes(2);
expect(spy).toHaveBeenCalledWith(CHAIN_ID_MOCK, {
chainId: CHAIN_ID_MOCK,
req: mockRequest,
result_type: ResultType.Failed,
reason: Reason.failed,
description: 'Validating the confirmation failed by throwing error.',
source: SecurityAlertSource.Local,
});
});
});
});
});
23 changes: 14 additions & 9 deletions app/lib/ppom/ppom-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
} from './security-alerts-api';
import { PPOMController } from '@metamask/ppom-validator';
import { Hex } from '@metamask/utils';
import { BLOCKAID_SUPPORTED_CHAIN_IDS } from '../../util/networks';
import { SECURITY_PROVIDER_SUPPORTED_CHAIN_IDS_FALLBACK_LIST } from '../../util/networks';

export interface PPOMRequest {
method: string;
Expand Down Expand Up @@ -137,7 +137,7 @@ async function validateRequest(req: PPOMRequest, transactionId?: string) {
}

async function isChainSupported(chainId: Hex): Promise<boolean> {
let supportedChainIds = BLOCKAID_SUPPORTED_CHAIN_IDS;
let supportedChainIds = SECURITY_PROVIDER_SUPPORTED_CHAIN_IDS_FALLBACK_LIST;
try {
if (isSecurityAlertsAPIEnabled()) {
supportedChainIds = await getSecurityAlertsAPISupportedChainIds();
Expand All @@ -155,14 +155,19 @@ async function validateWithController(
ppomController: PPOMController,
request: PPOMRequest,
): Promise<SecurityAlertResponse> {
const response = (await ppomController.usePPOM((ppom) =>
ppom.validateJsonRpc(request as unknown as Record<string, unknown>),
)) as SecurityAlertResponse;
try{
const response = (await ppomController.usePPOM((ppom) =>
ppom.validateJsonRpc(request as unknown as Record<string, unknown>),
)) as SecurityAlertResponse;

return {
...response,
source: SecurityAlertSource.Local,
};
return {
...response,
source: SecurityAlertSource.Local,
};
} catch (e) {
Logger.log(`Error validating request with PPOM: ${e}`);
return {...SECURITY_ALERT_RESPONSE_FAILED, source: SecurityAlertSource.Local,};
}
}

async function validateWithAPI(
Expand Down
2 changes: 1 addition & 1 deletion app/util/networks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export const NetworkList = {

const NetworkListKeys = Object.keys(NetworkList);

export const BLOCKAID_SUPPORTED_CHAIN_IDS = [
export const SECURITY_PROVIDER_SUPPORTED_CHAIN_IDS_FALLBACK_LIST = [
NETWORKS_CHAIN_ID.MAINNET,
NETWORKS_CHAIN_ID.BSC,
NETWORKS_CHAIN_ID.BASE,
Expand Down
45 changes: 45 additions & 0 deletions e2e/api-mocking/mock-config/mock-events.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,27 @@ export const mockEvents = {
response: suggestedGasFeesApiGanache,
responseCode: 200,
},

securityAlertApiSupportedChains: {
urlEndpoint: 'https://security-alerts.api.cx.metamask.io/supportedChains',
response: [
'0xa4b1',
'0xa86a',
'0x2105',
'0x138d5',
'0x38',
'0xe708',
'0x1',
'0x1b6e6',
'0xcc',
'0xa',
'0x89',
'0x82750',
'0xaa36a7',
'0x144'
],
responseCode: 200,
},
},

/**
Expand All @@ -54,5 +75,29 @@ export const mockEvents = {
maxFee: '2.000855333',
},
},

securityAlertApiValidate: {
urlEndpoint: 'https://security-alerts.api.cx.metamask.io/validate/0xaa36a7',
response: {
block: 20733513,
result_type: 'Benign',
reason: '',
description: '',
features: [],
},
requestBody: {
jsonrpc: '2.0',
method: 'eth_sendTransaction',
origin: 'metamask',
params: [
{
from: '0x76cf1cdd1fcc252442b50d6e97207228aa4aefc3',
to: '0x50587e46c5b96a3f6f9792922ec647f13e6efae4',
value: '0x0'
}
]
},
responseCode: 201,
},
},
};
2 changes: 1 addition & 1 deletion e2e/api-mocking/mock-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export const startMockServer = async (events, port) => {
await mockServer
.forPost('/proxy')
.withQuery({ url: urlEndpoint })
.withJsonBody(requestBody || {})
.withJsonBodyIncluding(requestBody || {})
.thenReply(responseCode, JSON.stringify(response));
}
}
Expand Down
11 changes: 11 additions & 0 deletions e2e/fixtures/fixture-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import createStaticServer from '../create-static-server';
import { getFixturesServerPort, getLocalTestDappPort } from './utils';
import Utilities from '../utils/Utilities';
import { device } from 'detox';
import { startMockServer, stopMockServer } from '../api-mocking/mock-server';

export const DEFAULT_DAPP_SERVER_PORT = 8085;

Expand Down Expand Up @@ -99,9 +100,15 @@ export async function withFixtures(options, testSuite) {
dappOptions,
dappPath = undefined,
dappPaths,
testSpecificMock,
} = options;

const fixtureServer = new FixtureServer();

if (testSpecificMock) {
await startMockServer(testSpecificMock);
}

let ganacheServer;
if (!disableGanache) {
ganacheServer = new Ganache();
Expand Down Expand Up @@ -191,6 +198,10 @@ export async function withFixtures(options, testSuite) {
}
}
await stopFixtureServer(fixtureServer);

if (testSpecificMock) {
await stopMockServer();
}
}
}

Expand Down
15 changes: 15 additions & 0 deletions e2e/pages/Confirmation/ConfirmationView.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ConfirmationTopSheetSelectorsIDs } from '../../selectors/ConfirmationView.selectors';
import Matchers from '../../utils/Matchers';

class ConfirmationView {
get securityAlertBanner() {
return Matchers.getElementByID(ConfirmationTopSheetSelectorsIDs.SECURITY_ALERT_BANNER);
}

get securityAlertResponseFailedBanner() {
return Matchers.getElementByID(ConfirmationTopSheetSelectorsIDs.SECURITY_ALERT_RESPONSE_FAILED_BANNER);
}

}

export default new ConfirmationView();
15 changes: 14 additions & 1 deletion e2e/pages/Send/TransactionConfirmView.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
TransactionConfirmViewSelectorsText,
TransactionConfirmViewSelectorsIDs,
} from '../../selectors/TransactionConfirmView.selectors.js';
import { ConfirmationTopSheetSelectorsIDs } from '../../selectors/ConfirmationView.selectors.js';

class TransactionConfirmationView {
get confirmButton() {
Expand Down Expand Up @@ -40,7 +41,7 @@ class TransactionConfirmationView {

get transactionAmount() {
return Matchers.getElementByID(
TransactionConfirmViewSelectorsIDs.COMFIRM_TXN_AMOUNT,
TransactionConfirmViewSelectorsIDs.CONFIRM_TXN_AMOUNT,
);
}

Expand Down Expand Up @@ -77,6 +78,18 @@ class TransactionConfirmationView {
);
}

get securityAlertBanner() {
return Matchers.getElementByID(
ConfirmationTopSheetSelectorsIDs.SECURITY_ALERT_BANNER,
);
}

get securityAlertResponseFailedBanner() {
return Matchers.getElementByID(
ConfirmationTopSheetSelectorsIDs.SECURITY_ALERT_RESPONSE_FAILED_BANNER,
);
}

async tapConfirmButton() {
await Gestures.waitAndTap(this.confirmButton);
}
Expand Down
Loading

0 comments on commit 7b4acc9

Please sign in to comment.