Skip to content

Commit

Permalink
Updating extension for ppom-validator updates (#20000)
Browse files Browse the repository at this point in the history
  • Loading branch information
jpuri authored Jul 25, 2023
1 parent 4927ee7 commit 98279b6
Show file tree
Hide file tree
Showing 11 changed files with 753 additions and 17 deletions.
1 change: 1 addition & 0 deletions .metamaskrc.dist
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ INFURA_PROJECT_ID=00000000000

; Set this to test changes to the phishing warning page.
;PHISHING_WARNING_PAGE_URL=
BLOCKAID_FILE_CDN=
File renamed without changes.
File renamed without changes.
110 changes: 110 additions & 0 deletions app/scripts/lib/ppom/ppom-middleware.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { createPPOMMiddleware } from './ppom-middleware';

Object.defineProperty(globalThis, 'fetch', {
writable: true,
value: () => undefined,
});

Object.defineProperty(globalThis, 'performance', {
writable: true,
value: () => undefined,
});

describe('PPOMMiddleware', () => {
it('should call ppomController.usePPOM for requests of type confirmation', async () => {
const useMock = jest.fn();
const controller = {
usePPOM: useMock,
};
const middlewareFunction = createPPOMMiddleware(controller as any);
await middlewareFunction(
{ method: 'eth_sendTransaction' },
undefined,
() => undefined,
);
expect(useMock).toHaveBeenCalledTimes(1);
});

it('should add validation response on confirmation requests', async () => {
const controller = {
usePPOM: async () => Promise.resolve('VALIDATION_RESULT'),
};
const middlewareFunction = createPPOMMiddleware(controller as any);
const req = { method: 'eth_sendTransaction', ppomResponse: undefined };
await middlewareFunction(req, undefined, () => undefined);
expect(req.ppomResponse).toBeDefined();
});

it('should call next method when ppomController.usePPOM completes', async () => {
const ppom = {
validateJsonRpc: () => undefined,
};
const controller = {
usePPOM: async (callback: any) => {
callback(ppom);
},
};
const middlewareFunction = createPPOMMiddleware(controller as any);
const nextMock = jest.fn();
await middlewareFunction(
{ method: 'eth_sendTransaction' },
undefined,
nextMock,
);
expect(nextMock).toHaveBeenCalledTimes(1);
});

it('should call next method when ppomController.usePPOM throws error', async () => {
const controller = {
usePPOM: async (_callback: any) => {
throw Error('Some error');
},
};
const middlewareFunction = createPPOMMiddleware(controller as any);
const nextMock = jest.fn();
await middlewareFunction(
{ method: 'eth_sendTransaction' },
undefined,
nextMock,
);
expect(nextMock).toHaveBeenCalledTimes(1);
});

it('should call ppom.validateJsonRpc when invoked', async () => {
const validateMock = jest.fn();
const ppom = {
validateJsonRpc: validateMock,
};
const controller = {
usePPOM: async (callback: any) => {
callback(ppom);
},
};
const middlewareFunction = createPPOMMiddleware(controller as any);
await middlewareFunction(
{ method: 'eth_sendTransaction' },
undefined,
() => undefined,
);
expect(validateMock).toHaveBeenCalledTimes(1);
});

it('should not call ppom.validateJsonRpc when request is not for confirmation method', async () => {
const validateMock = jest.fn();
const ppom = {
validateJsonRpc: validateMock,
};
const controller = {
usePPOM: async (callback: any) => {
callback(ppom);
},
};
const middlewareFunction = createPPOMMiddleware(controller as any);
await middlewareFunction(
{ method: 'eth_someRequest' },
undefined,
() => undefined,
);
expect(validateMock).toHaveBeenCalledTimes(0);
});
});
43 changes: 43 additions & 0 deletions app/scripts/lib/ppom/ppom-middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { PPOM } from '@blockaid/ppom';

import { PPOMController } from '@metamask/ppom-validator';

const ConfirmationMethods = Object.freeze([
'eth_sendRawTransaction',
'eth_sendTransaction',
'eth_sign',
'eth_signTypedData',
'eth_signTypedData_v1',
'eth_signTypedData_v3',
'eth_signTypedData_v4',
'personal_sign',
]);

/**
* Middleware function that handles JSON RPC requests.
* This function will be called for every JSON RPC request.
* It will call the PPOM to check if the request is malicious or benign.
* If the request is benign, it will be forwarded to the next middleware.
* If the request is malicious or warning, it will trigger the PPOM alert dialog,
* after the user has confirmed or rejected the request,
* the request will be forwarded to the next middleware, together with the PPOM response.
*
* @param ppomController - Instance of PPOMController.
* @returns PPOMMiddleware function.
*/
export function createPPOMMiddleware(ppomController: PPOMController) {
return async (req: any, _res: any, next: () => void) => {
try {
if (ConfirmationMethods.includes(req.method)) {
// eslint-disable-next-line require-atomic-updates
req.ppomResponse = await ppomController.usePPOM(async (ppom: PPOM) => {
return ppom.validateJsonRpc(req);
});
}
} catch (error: unknown) {
console.error('Error validating JSON RPC using PPOM: ', error);
} finally {
next();
}
};
}
Loading

0 comments on commit 98279b6

Please sign in to comment.