mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 09:57:02 +01:00
Adopt security provider request from core (#18520)
This commit is contained in:
parent
09d00e1e45
commit
ae0af1b283
@ -52,6 +52,7 @@ const messageMock = {
|
||||
const coreMessageMock = {
|
||||
...messageMock,
|
||||
messageParams: messageParamsMock,
|
||||
securityProviderResponse: securityProviderResponseMock,
|
||||
};
|
||||
|
||||
const stateMessageMock = {
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
}
|
117
app/scripts/lib/security-provider-helpers.test.ts
Normal file
117
app/scripts/lib/security-provider-helpers.test.ts
Normal 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');
|
||||
},
|
||||
);
|
||||
});
|
92
app/scripts/lib/security-provider-helpers.ts
Normal file
92
app/scripts/lib/security-provider-helpers.ts
Normal 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();
|
||||
}
|
Loading…
Reference in New Issue
Block a user