From 0ec9c3e01a1e8c98ebb0fbe3758779c63443a679 Mon Sep 17 00:00:00 2001 From: Esteban Beltran Date: Tue, 23 Apr 2024 14:08:34 +0200 Subject: [PATCH] FE Sandbox: Fix worker post message not handling proxy objects correctly (#86654) * FE Sandbox: Fix worker post message not handling proxy objects correctly * use expect error instead of ignore * use assertion instead of ignore * Fix formatting --- .../plugins/sandbox/document_sandbox.ts | 23 ++++++++++++++++++- public/app/features/plugins/sandbox/utils.ts | 22 ++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/public/app/features/plugins/sandbox/document_sandbox.ts b/public/app/features/plugins/sandbox/document_sandbox.ts index 9dcb1a3a1aa..fdd4b0c9740 100644 --- a/public/app/features/plugins/sandbox/document_sandbox.ts +++ b/public/app/features/plugins/sandbox/document_sandbox.ts @@ -6,7 +6,7 @@ import { CustomVariableSupport, DataSourceApi } from '@grafana/data'; import { config } from '@grafana/runtime'; import { forbiddenElements } from './constants'; -import { isReactClassComponent, logWarning } from './utils'; +import { isReactClassComponent, logWarning, unboxNearMembraneProxies } from './utils'; // IMPORTANT: NEVER export this symbol from a public (e.g `@grafana/*`) package const SANDBOX_LIVE_VALUE = Symbol.for('@@SANDBOX_LIVE_VALUE'); @@ -194,9 +194,30 @@ export function patchWebAPIs() { if (!nativeAPIsPatched) { nativeAPIsPatched = true; patchHistoryReplaceState(); + patchWorkerPostMessage(); } } +/* + * + * Worker.postMessage uses internally structureClone which won't work with proxies. + * + * In case where the blue realm code is directly handling proxy objects that + * should be send over a post message the blue realm will call postMessage and try to + * send the proxy resulting in an error. + * + * This makes sure all proxies are unboxed before being sent over the post message + */ +function patchWorkerPostMessage() { + const originalPostMessage = Worker.prototype.postMessage; + Object.defineProperty(Worker.prototype, 'postMessage', { + value: function (...args: Parameters) { + // eslint-disable-next-line + return originalPostMessage.apply(this, unboxNearMembraneProxies(args) as typeof args); + }, + }); +} + /* * window.history.replaceState is a native API that won't work with proxies * so we need to patch it to unwrap any possible proxies you pass to it. diff --git a/public/app/features/plugins/sandbox/utils.ts b/public/app/features/plugins/sandbox/utils.ts index 30aab341881..af97b19c9ec 100644 --- a/public/app/features/plugins/sandbox/utils.ts +++ b/public/app/features/plugins/sandbox/utils.ts @@ -1,4 +1,5 @@ import { isNearMembraneProxy } from '@locker/near-membrane-shared'; +import { cloneDeep } from 'lodash'; import React from 'react'; import { PluginSignatureType, PluginType } from '@grafana/data'; @@ -112,3 +113,24 @@ export function unboxRegexesFromMembraneProxy(structure: unknown): unknown { } return structure; } + +export function unboxNearMembraneProxies(structure: unknown): unknown { + if (!structure) { + return structure; + } + + if (isNearMembraneProxy(structure)) { + return cloneDeep(structure); + } + + if (Array.isArray(structure)) { + return structure.map(unboxNearMembraneProxies); + } + if (typeof structure === 'object') { + return Object.keys(structure).reduce((acc, key) => { + Reflect.set(acc, key, unboxNearMembraneProxies(Reflect.get(structure, key))); + return acc; + }, {}); + } + return structure; +}