1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-22 01:47:00 +01:00

Adopt security provider request from core (#18520)

This commit is contained in:
Vinicius Stevam 2023-04-18 08:33:32 +01:00 committed by GitHub
parent 09d00e1e45
commit ae0af1b283
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 231 additions and 89 deletions

View File

@ -52,6 +52,7 @@ const messageMock = {
const coreMessageMock = {
...messageMock,
messageParams: messageParamsMock,
securityProviderResponse: securityProviderResponseMock,
};
const stateMessageMock = {

View File

@ -21,6 +21,7 @@ import {
AbstractMessageParams,
AbstractMessageParamsMetamask,
OriginalRequest,
SecurityProviderRequest,
} from '@metamask/message-manager/dist/AbstractMessageManager';
import {
BaseControllerV2,
@ -63,9 +64,10 @@ export type CoreMessage = AbstractMessage & {
messageParams: AbstractMessageParams;
};
export type StateMessage = Required<AbstractMessage> & {
export type StateMessage = Required<
Omit<AbstractMessage, 'securityProviderResponse'>
> & {
msgParams: Required<AbstractMessageParams>;
securityProviderResponse: any;
};
export type SignControllerState = {
@ -107,10 +109,7 @@ export type SignControllerOptions = {
preferencesController: PreferencesController;
getState: () => any;
metricsEvent: (payload: any, options?: any) => void;
securityProviderRequest: (
requestData: any,
methodName: string,
) => Promise<any>;
securityProviderRequest: SecurityProviderRequest;
};
/**
@ -143,11 +142,6 @@ export default class SignController extends BaseControllerV2<
private _metricsEvent: (payload: any, options?: any) => void;
private _securityProviderRequest: (
requestData: any,
methodName: string,
) => Promise<any>;
/**
* Construct a Sign controller.
*
@ -178,12 +172,23 @@ export default class SignController extends BaseControllerV2<
this._preferencesController = preferencesController;
this._getState = getState;
this._metricsEvent = metricsEvent;
this._securityProviderRequest = securityProviderRequest;
this.hub = new EventEmitter();
this._messageManager = new MessageManager();
this._personalMessageManager = new PersonalMessageManager();
this._typedMessageManager = new TypedMessageManager();
this._messageManager = new MessageManager(
undefined,
undefined,
securityProviderRequest,
);
this._personalMessageManager = new PersonalMessageManager(
undefined,
undefined,
securityProviderRequest,
);
this._typedMessageManager = new TypedMessageManager(
undefined,
undefined,
securityProviderRequest,
);
this._messageManagers = [
this._messageManager,
@ -589,15 +594,7 @@ export default class SignController extends BaseControllerV2<
origin: messageParams.origin as string,
},
};
const messageId = coreMessage.id;
const existingMessage = this._getMessage(messageId);
const securityProviderResponse = existingMessage
? existingMessage.securityProviderResponse
: await this._securityProviderRequest(stateMessage, stateMessage.type);
return { ...stateMessage, securityProviderResponse };
return stateMessage;
}
private _normalizeMsgData(data: string) {

View File

@ -1,65 +0,0 @@
import getFetchWithTimeout from '../../../shared/modules/fetch-with-timeout';
import { MESSAGE_TYPE } from '../../../shared/constants/app';
const fetchWithTimeout = getFetchWithTimeout();
export async function securityProviderCheck(
requestData,
methodName,
chainId,
currentLocale,
) {
let dataToValidate;
if (methodName === MESSAGE_TYPE.ETH_SIGN_TYPED_DATA) {
dataToValidate = {
host_name: requestData.msgParams.origin,
rpc_method_name: methodName,
chain_id: chainId,
data: requestData.msgParams.data,
currentLocale,
};
} else if (
methodName === MESSAGE_TYPE.ETH_SIGN ||
methodName === MESSAGE_TYPE.PERSONAL_SIGN
) {
dataToValidate = {
host_name: requestData.msgParams.origin,
rpc_method_name: methodName,
chain_id: chainId,
data: {
signer_address: requestData.msgParams.from,
msg_to_sign: requestData.msgParams.data,
},
currentLocale,
};
} else {
dataToValidate = {
host_name: requestData.origin,
rpc_method_name: methodName,
chain_id: chainId,
data: {
from_address: requestData?.txParams?.from,
to_address: requestData?.txParams?.to,
gas: requestData?.txParams?.gas,
gasPrice: requestData?.txParams?.gasPrice,
value: requestData?.txParams?.value,
data: requestData?.txParams?.data,
},
currentLocale,
};
}
const response = await fetchWithTimeout(
'https://proxy.metafi.codefi.network/opensea/security/v1/validate',
{
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(dataToValidate),
},
);
return await response.json();
}

View File

@ -0,0 +1,117 @@
import { MESSAGE_TYPE } from '../../../shared/constants/app';
import {
RequestData,
securityProviderCheck,
} from './security-provider-helpers';
describe('securityProviderCheck', () => {
let fetchSpy: jest.SpyInstance;
beforeEach(() => {
// Spy on the global fetch function
fetchSpy = jest.spyOn(global, 'fetch');
fetchSpy.mockImplementation(async () => {
return new Response(JSON.stringify('result_mocked'), { status: 200 });
});
});
const paramsMock = {
origin: 'https://example.com',
data: 'some_data',
from: '0x',
};
// Utility function to handle different data properties based on methodName
const getExpectedData = (methodName: string, requestData: RequestData) => {
switch (methodName) {
case MESSAGE_TYPE.ETH_SIGN:
case MESSAGE_TYPE.PERSONAL_SIGN:
return {
signer_address: requestData.msgParams?.from,
msg_to_sign: requestData.msgParams?.data,
};
case MESSAGE_TYPE.ETH_SIGN_TYPED_DATA:
return requestData.messageParams?.data;
default:
return {
from_address: requestData.txParams?.from,
to_address: requestData.txParams?.to,
gas: requestData.txParams?.gas,
gasPrice: requestData.txParams?.gasPrice,
value: requestData.txParams?.value,
data: requestData.txParams?.data,
};
}
};
test.each([
[MESSAGE_TYPE.ETH_SIGN_TYPED_DATA],
[MESSAGE_TYPE.ETH_SIGN],
[MESSAGE_TYPE.PERSONAL_SIGN],
['some_other_method'],
])(
'should call fetch with the correct parameters for %s',
async (methodName: string) => {
let requestData: RequestData;
switch (methodName) {
case MESSAGE_TYPE.ETH_SIGN_TYPED_DATA:
requestData = {
origin: 'https://example.com',
messageParams: paramsMock,
};
break;
case MESSAGE_TYPE.ETH_SIGN:
case MESSAGE_TYPE.PERSONAL_SIGN:
requestData = {
origin: 'https://example.com',
msgParams: paramsMock,
};
break;
default:
requestData = {
origin: 'https://example.com',
txParams: {
from: '0x',
to: '0x',
gas: 'some_gas',
gasPrice: 'some_gasPrice',
value: 'some_value',
data: 'some_data',
},
};
}
const result = await securityProviderCheck(
requestData,
methodName,
'1',
'en',
);
expect(fetchSpy).toHaveBeenCalledTimes(1);
expect(fetchSpy).toHaveBeenCalledWith(
'https://proxy.metafi.codefi.network/opensea/security/v1/validate',
expect.objectContaining({
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
host_name:
methodName === 'some_other_method'
? requestData.origin
: requestData.msgParams?.origin ||
requestData.messageParams?.origin,
rpc_method_name: methodName,
chain_id: '1',
data: getExpectedData(methodName, requestData),
currentLocale: 'en',
}),
}),
);
expect(result).toEqual('result_mocked');
},
);
});

View File

@ -0,0 +1,92 @@
import { Json } from '@metamask/utils';
import { MessageParams } from '@metamask/message-manager';
import getFetchWithTimeout from '../../../shared/modules/fetch-with-timeout';
import { MESSAGE_TYPE } from '../../../shared/constants/app';
const fetchWithTimeout = getFetchWithTimeout();
export type TransactionRequestData = {
txParams: Record<string, unknown>;
messageParams?: never;
msgParams?: never;
};
export type MessageRequestData =
| {
msgParams: MessageParams;
txParams?: never;
messageParams?: never;
}
| {
messageParams: MessageParams;
msgParams?: never;
txParams?: never;
}
| TransactionRequestData;
export type RequestData = {
origin: string;
} & MessageRequestData;
export async function securityProviderCheck(
requestData: RequestData,
methodName: string,
chainId: string,
currentLocale: string,
): Promise<Record<string, Json>> {
let dataToValidate;
// Core message managers use messageParams but frontend uses msgParams with lots of references
const params = requestData.msgParams || requestData.messageParams;
if (methodName === MESSAGE_TYPE.ETH_SIGN_TYPED_DATA) {
dataToValidate = {
host_name: params?.origin,
rpc_method_name: methodName,
chain_id: chainId,
data: params?.data,
currentLocale,
};
} else if (
methodName === MESSAGE_TYPE.ETH_SIGN ||
methodName === MESSAGE_TYPE.PERSONAL_SIGN
) {
dataToValidate = {
host_name: params?.origin,
rpc_method_name: methodName,
chain_id: chainId,
data: {
signer_address: params?.from,
msg_to_sign: params?.data,
},
currentLocale,
};
} else {
dataToValidate = {
host_name: requestData.origin,
rpc_method_name: methodName,
chain_id: chainId,
data: {
from_address: requestData.txParams?.from,
to_address: requestData.txParams?.to,
gas: requestData.txParams?.gas,
gasPrice: requestData.txParams?.gasPrice,
value: requestData.txParams?.value,
data: requestData.txParams?.data,
},
currentLocale,
};
}
const response: Response = await fetchWithTimeout(
'https://proxy.metafi.codefi.network/opensea/security/v1/validate',
{
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(dataToValidate),
},
);
return await response.json();
}