From 7b8945571cb66e2f13a5e9014b4f2c58367a7580 Mon Sep 17 00:00:00 2001 From: Esteban Beltran Date: Mon, 9 Oct 2023 14:23:47 +0200 Subject: [PATCH] Sandbox: Fix dynamic loaded chunks not processed correcly inside the sandbox (#76047) * Sandbox: Patch dynamic chunks using CDN method * Patch plugin apis when dynamically loaded * use same method to transform all code --- public/app/features/plugins/cdn/utils.ts | 8 +++- .../features/plugins/sandbox/code_loader.ts | 44 +++++++------------ 2 files changed, 22 insertions(+), 30 deletions(-) diff --git a/public/app/features/plugins/cdn/utils.ts b/public/app/features/plugins/cdn/utils.ts index 4184bcd1992..624fd04bf1f 100644 --- a/public/app/features/plugins/cdn/utils.ts +++ b/public/app/features/plugins/cdn/utils.ts @@ -7,10 +7,12 @@ export function transformPluginSourceForCDN({ url, source, transformSourceMapURL = false, + transformAssets = true, }: { url: string; source: string; transformSourceMapURL?: boolean; + transformAssets?: boolean; }): string { const splitUrl = url.split('/public/plugins/'); const baseAddress = splitUrl[0]; @@ -18,8 +20,10 @@ export function transformPluginSourceForCDN({ // handle basic asset paths that include public/plugins let newSource = source; - newSource = newSource.replace(/(\/?)(public\/plugins)/g, `${baseAddress}/$2`); - newSource = newSource.replace(/(["|'])(plugins\/.+?.css)(["|'])/g, `$1${baseAddress}/public/$2$3`); + if (transformAssets) { + newSource = newSource.replace(/(\/?)(public\/plugins)/g, `${baseAddress}/$2`); + newSource = newSource.replace(/(["|'])(plugins\/.+?.css)(["|'])/g, `$1${baseAddress}/public/$2$3`); + } if (transformSourceMapURL) { newSource = newSource.replace( diff --git a/public/app/features/plugins/sandbox/code_loader.ts b/public/app/features/plugins/sandbox/code_loader.ts index 110af632476..7f22aafbeb2 100644 --- a/public/app/features/plugins/sandbox/code_loader.ts +++ b/public/app/features/plugins/sandbox/code_loader.ts @@ -19,8 +19,13 @@ export async function loadScriptIntoSandbox(url: string, meta: PluginMeta, sandb if (isSameDomainAsHost(url)) { const response = await fetch(url); scriptCode = await response.text(); - scriptCode = patchPluginSourceMap(meta, scriptCode); - + //even though this is not loaded via a CDN we need to transform the sourceMapUrl + scriptCode = transformPluginSourceForCDN({ + url, + source: scriptCode, + transformSourceMapURL: true, + transformAssets: false, + }); // cdn loaded } else if (isHostedOnCDN(url)) { const response = await fetch(url); @@ -29,6 +34,7 @@ export async function loadScriptIntoSandbox(url: string, meta: PluginMeta, sandb url, source: scriptCode, transformSourceMapURL: true, + transformAssets: true, }); } @@ -36,6 +42,7 @@ export async function loadScriptIntoSandbox(url: string, meta: PluginMeta, sandb throw new Error('Only same domain scripts are allowed in sandboxed plugins'); } + scriptCode = patchPluginAPIs(scriptCode); sandboxEnv.evaluate(scriptCode); } @@ -49,6 +56,7 @@ export async function getPluginCode(meta: PluginMeta): Promise { url, source: pluginCode, transformSourceMapURL: true, + transformAssets: true, }); return pluginCode; } else { @@ -57,7 +65,12 @@ export async function getPluginCode(meta: PluginMeta): Promise { const pluginCodeUrl = resolveWithCache(meta.module); const response = await fetch(pluginCodeUrl); let pluginCode = await response.text(); - pluginCode = patchPluginSourceMap(meta, pluginCode); + pluginCode = transformPluginSourceForCDN({ + url: pluginCodeUrl, + source: pluginCode, + transformSourceMapURL: true, + transformAssets: false, + }); pluginCode = patchPluginAPIs(pluginCode); return pluginCode; } @@ -67,31 +80,6 @@ function patchPluginAPIs(pluginCode: string): string { return pluginCode.replace(/window\.location/gi, 'window.locationSandbox'); } -/** - * Patches the plugin's module.js source code references to sourcemaps to include the full url - * of the module.js file instead of the regular relative reference. - * - * Because the plugin module.js code is loaded via fetch and then "eval" as a string - * it can't find the references to the module.js.map directly and we need to patch it - * to point to the correct location - */ -function patchPluginSourceMap(meta: PluginMeta, pluginCode: string): string { - // skips inlined and files without source maps - if (pluginCode.includes('//# sourceMappingURL=module.js.map')) { - let replaceWith = ''; - // make sure we don't add the sourceURL twice - if (!pluginCode.includes('//# sourceURL') || !pluginCode.includes('//@ sourceUrl')) { - replaceWith += `//# sourceURL=module.js\n`; - } - // modify the source map url to point to the correct location - const sourceCodeMapUrl = meta.module + '.map'; - replaceWith += `//# sourceMappingURL=${sourceCodeMapUrl}`; - - return pluginCode.replace('//# sourceMappingURL=module.js.map', replaceWith); - } - return pluginCode; -} - export function patchSandboxEnvironmentPrototype(sandboxEnvironment: SandboxEnvironment) { // same as https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/types/vector.ts#L16 // Array is a "reflective" type in Near-membrane and doesn't get an identify continuity