From 5bccb4a9216481c0b2905ceabea21b016a92375f Mon Sep 17 00:00:00 2001 From: 1natsu <1natsu172@users.noreply.github.com> Date: Mon, 4 Nov 2024 00:31:25 +0900 Subject: [PATCH 1/3] feat: Verify message details on window&custom messaging --- packages/messaging/src/custom-event.ts | 10 ++++++---- packages/messaging/src/generic.ts | 7 ++++++- packages/messaging/src/utils.ts | 10 ++++++++++ packages/messaging/src/window.ts | 3 +++ 4 files changed, 25 insertions(+), 5 deletions(-) create mode 100644 packages/messaging/src/utils.ts diff --git a/packages/messaging/src/custom-event.ts b/packages/messaging/src/custom-event.ts index c462ee7..20e31d8 100644 --- a/packages/messaging/src/custom-event.ts +++ b/packages/messaging/src/custom-event.ts @@ -1,6 +1,7 @@ import { uid } from 'uid'; import { GenericMessenger, defineGenericMessanging } from './generic'; import { NamespaceMessagingConfig } from './types'; +import { prepareCustomEventDict } from './utils'; const REQUEST_EVENT = '@webext-core/messaging/custom-events'; const RESPONSE_EVENT = '@webext-core/messaging/custom-events/response'; @@ -89,8 +90,7 @@ export function defineCustomEventMessaging< sendMessage(message) { const reqDetail = { message, namespace, instanceId }; const requestEvent = new CustomEvent(REQUEST_EVENT, { - // @ts-expect-error not exist cloneInto types because implemented only in Firefox. - detail: typeof cloneInto !== 'undefined' ? cloneInto(reqDetail, window) : reqDetail, + detail: prepareCustomEventDict(reqDetail), }); return sendCustomMessage(requestEvent); }, @@ -105,8 +105,7 @@ export function defineCustomEventMessaging< const resDetail = { response, message, instanceId, namespace }; const responseEvent = new CustomEvent(RESPONSE_EVENT, { - // @ts-expect-error not exist cloneInto types because implemented only in Firefox. - detail: typeof cloneInto !== 'undefined' ? cloneInto(resDetail, window) : resDetail, + detail: prepareCustomEventDict(resDetail), }); window.dispatchEvent(responseEvent); }; @@ -114,6 +113,9 @@ export function defineCustomEventMessaging< window.addEventListener(REQUEST_EVENT, requestListener); return () => window.removeEventListener(REQUEST_EVENT, requestListener); }, + verifyMessageData(data) { + return structuredClone(data); + }, }); return { diff --git a/packages/messaging/src/generic.ts b/packages/messaging/src/generic.ts index d63ae8c..842677b 100644 --- a/packages/messaging/src/generic.ts +++ b/packages/messaging/src/generic.ts @@ -25,6 +25,7 @@ interface GenericMessagingConfig< message: Message & TMessageExtension, ) => void | Promise<{ res: any } | { err: unknown }>, ): RemoveListenerCallback; + verifyMessageData?(data: T): MaybePromise; } /** @@ -109,12 +110,13 @@ export function defineGenericMessanging< data: TProtocolMap[TType], ...args: TSendMessageArgs ): Promise { - const message: Message = { + const _message: Message = { id: getNextId(), type: type, data, timestamp: Date.now(), }; + const message = (await config.verifyMessageData?.(_message)) ?? _message; config.logger?.debug(`[messaging] sendMessage {id=${message.id}} ─ᐅ`, message, ...args); const response = await config.sendMessage(message, ...args); @@ -150,6 +152,9 @@ export function defineGenericMessanging< const res = listener(message); return Promise.resolve(res) + .then(res => { + return config.verifyMessageData?.(res) ?? res; + }) .then(res => { config?.logger?.debug(`[messaging] onMessage {id=${message.id}} ─ᐅ`, { res }); return { res }; diff --git a/packages/messaging/src/utils.ts b/packages/messaging/src/utils.ts new file mode 100644 index 0000000..c839016 --- /dev/null +++ b/packages/messaging/src/utils.ts @@ -0,0 +1,10 @@ +/** + * @description In firefox, when dispatching events externally from web-extension, it's necessary to clone all properties of the dictionary. ref: https://github.com/aklinker1/webext-core/pull/70#discussion_r1775031410 + */ +export function prepareCustomEventDict( + data: T, + options: { targetScope?: object } = { targetScope: window ?? undefined }, +): T { + // @ts-expect-error not exist cloneInto types because implemented only in Firefox. + return typeof cloneInto !== 'undefined' ? cloneInto(data, options.targetScope) : data; +} diff --git a/packages/messaging/src/window.ts b/packages/messaging/src/window.ts index b566175..aa1d79f 100644 --- a/packages/messaging/src/window.ts +++ b/packages/messaging/src/window.ts @@ -106,6 +106,9 @@ export function defineWindowMessaging< window.addEventListener('message', listener); return () => window.removeEventListener('message', listener); }, + verifyMessageData(data) { + return structuredClone(data); + }, }); return { From 58b78d3bccfd82477a69fd27525c33922de70a68 Mon Sep 17 00:00:00 2001 From: 1natsu <1natsu172@users.noreply.github.com> Date: Mon, 4 Nov 2024 03:24:48 +0900 Subject: [PATCH 2/3] test(messaging): add transfer non-serializable data case --- .../src/__tests__/browser/page.test.ts | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/packages/messaging/src/__tests__/browser/page.test.ts b/packages/messaging/src/__tests__/browser/page.test.ts index c9510b7..24ac8e3 100644 --- a/packages/messaging/src/__tests__/browser/page.test.ts +++ b/packages/messaging/src/__tests__/browser/page.test.ts @@ -114,6 +114,39 @@ describe.each< ); }); + it('should throw an error if the responder sends non-serializable data', async () => { + interface MessageSchema { + test(data: string): { getName: () => string }; + } + const messenger1 = defineTestMessaging(); + const messenger2 = defineTestMessaging(); + const invalidFn = () => 'testUser1'; + const onMessage = vi.fn().mockReturnValue({ getName: invalidFn }); + + messenger2.onMessage('test', onMessage); + + await expect(messenger1.sendMessage('test', 'data', ...sendArgs)).rejects.toThrowError( + `Failed to execute 'structuredClone' on 'Window': ${invalidFn.toString()} could not be cloned.`, + ); + }); + + it('should throw an error if the sender sends non-serializable data', async () => { + interface MessageSchema { + test(data: { getName: () => string }): boolean; + } + const messenger1 = defineTestMessaging(); + const messenger2 = defineTestMessaging(); + const invalidFn = () => 'testUser1'; + + messenger2.onMessage('test', () => true); + + await expect( + messenger1.sendMessage('test', { getName: invalidFn }, ...sendArgs), + ).rejects.toThrowError( + `Failed to execute 'structuredClone' on 'Window': ${invalidFn.toString()} could not be cloned.`, + ); + }); + it('should be messaging for the same message type between different instances', async () => { interface MessageSchema { test(data: string): string; From 81ad00409210e98f9d75b467d55bfd1f94f80295 Mon Sep 17 00:00:00 2001 From: Aaron Date: Wed, 13 Nov 2024 12:52:55 -0600 Subject: [PATCH 3/3] Update packages/messaging/src/__tests__/browser/page.test.ts --- packages/messaging/src/__tests__/browser/page.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/messaging/src/__tests__/browser/page.test.ts b/packages/messaging/src/__tests__/browser/page.test.ts index 24ac8e3..f785ebd 100644 --- a/packages/messaging/src/__tests__/browser/page.test.ts +++ b/packages/messaging/src/__tests__/browser/page.test.ts @@ -114,7 +114,7 @@ describe.each< ); }); - it('should throw an error if the responder sends non-serializable data', async () => { + it('should throw an error if the responder responds with non-serializable data', async () => { interface MessageSchema { test(data: string): { getName: () => string }; }