grafana/public/app/features/plugins/sandbox/code_loader.ts
Esteban Beltran 7b8945571c
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
2023-10-09 14:23:47 +02:00

90 lines
3.0 KiB
TypeScript

import { PluginMeta, patchArrayVectorProrotypeMethods } from '@grafana/data';
import { transformPluginSourceForCDN } from '../cdn/utils';
import { resolveWithCache } from '../loader/cache';
import { isHostedOnCDN } from '../loader/utils';
import { SandboxEnvironment } from './types';
function isSameDomainAsHost(url: string): boolean {
const locationUrl = new URL(window.location.href);
const paramUrl = new URL(url);
return locationUrl.host === paramUrl.host;
}
export async function loadScriptIntoSandbox(url: string, meta: PluginMeta, sandboxEnv: SandboxEnvironment) {
let scriptCode = '';
// same-domain
if (isSameDomainAsHost(url)) {
const response = await fetch(url);
scriptCode = await response.text();
//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);
scriptCode = await response.text();
scriptCode = transformPluginSourceForCDN({
url,
source: scriptCode,
transformSourceMapURL: true,
transformAssets: true,
});
}
if (scriptCode.length === 0) {
throw new Error('Only same domain scripts are allowed in sandboxed plugins');
}
scriptCode = patchPluginAPIs(scriptCode);
sandboxEnv.evaluate(scriptCode);
}
export async function getPluginCode(meta: PluginMeta): Promise<string> {
if (isHostedOnCDN(meta.module)) {
// Load plugin from CDN, no need for "resolveWithCache" as CDN URLs already include the version
const url = meta.module;
const response = await fetch(url);
let pluginCode = await response.text();
pluginCode = transformPluginSourceForCDN({
url,
source: pluginCode,
transformSourceMapURL: true,
transformAssets: true,
});
return pluginCode;
} else {
// local plugin. resolveWithCache will append a query parameter with its version
// to ensure correct cached version is served
const pluginCodeUrl = resolveWithCache(meta.module);
const response = await fetch(pluginCodeUrl);
let pluginCode = await response.text();
pluginCode = transformPluginSourceForCDN({
url: pluginCodeUrl,
source: pluginCode,
transformSourceMapURL: true,
transformAssets: false,
});
pluginCode = patchPluginAPIs(pluginCode);
return pluginCode;
}
}
function patchPluginAPIs(pluginCode: string): string {
return pluginCode.replace(/window\.location/gi, 'window.locationSandbox');
}
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
sandboxEnvironment.evaluate(
`${patchArrayVectorProrotypeMethods.toString()};${patchArrayVectorProrotypeMethods.name}()`
);
}