FE Sandbox: Get plugin module path from bootdata instead of plugin settings (#86702)

* FE Sandbox: Get plugin module path from bootdata instead of plugin settings

* Remove unnecessary async/await
This commit is contained in:
Esteban Beltran
2024-04-24 12:05:20 +02:00
committed by GitHub
parent e7f40493e4
commit 1e1c62fef1
5 changed files with 60 additions and 30 deletions
@@ -1,10 +1,11 @@
import { PluginMeta, patchArrayVectorProrotypeMethods } from '@grafana/data';
import { PluginType, patchArrayVectorProrotypeMethods } from '@grafana/data';
import { config } from '@grafana/runtime';
import { transformPluginSourceForCDN } from '../cdn/utils';
import { resolveWithCache } from '../loader/cache';
import { isHostedOnCDN, resolveModulePath } from '../loader/utils';
import { SandboxEnvironment } from './types';
import { SandboxEnvironment, SandboxPluginMeta } from './types';
function isSameDomainAsHost(url: string): boolean {
const locationUrl = new URL(window.location.href);
@@ -12,7 +13,7 @@ function isSameDomainAsHost(url: string): boolean {
return locationUrl.host === paramUrl.host;
}
export async function loadScriptIntoSandbox(url: string, meta: PluginMeta, sandboxEnv: SandboxEnvironment) {
export async function loadScriptIntoSandbox(url: string, sandboxEnv: SandboxEnvironment) {
let scriptCode = '';
// same-domain
@@ -46,7 +47,7 @@ export async function loadScriptIntoSandbox(url: string, meta: PluginMeta, sandb
sandboxEnv.evaluate(scriptCode);
}
export async function getPluginCode(meta: PluginMeta): Promise<string> {
export async function getPluginCode(meta: SandboxPluginMeta): 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;
@@ -88,3 +89,33 @@ export function patchSandboxEnvironmentPrototype(sandboxEnvironment: SandboxEnvi
`${patchArrayVectorProrotypeMethods.toString()};${patchArrayVectorProrotypeMethods.name}()`
);
}
export function getPluginLoadData(pluginId: string): SandboxPluginMeta {
// find it in datasources
for (const datasource of Object.values(config.datasources)) {
if (datasource.type === pluginId) {
return datasource.meta;
}
}
//find it in panels
for (const panel of Object.values(config.panels)) {
if (panel.id === pluginId) {
return panel;
}
}
//find it in apps
//the information inside the apps object is more limited
for (const app of Object.values(config.apps)) {
if (app.id === pluginId) {
return {
id: pluginId,
type: PluginType.app,
module: app.path,
};
}
}
throw new Error(`Could not find plugin ${pluginId}`);
}
@@ -1,14 +1,13 @@
import { ProxyTarget } from '@locker/near-membrane-shared';
import { cloneDeep, isFunction } from 'lodash';
import { PluginMeta } from '@grafana/data';
import { config } from '@grafana/runtime';
import { Monaco } from '@grafana/ui';
import { loadScriptIntoSandbox } from './code_loader';
import { forbiddenElements } from './constants';
import { recursivePatchObjectAsLiveTarget } from './document_sandbox';
import { SandboxEnvironment } from './types';
import { SandboxEnvironment, SandboxPluginMeta } from './types';
import { logWarning, unboxRegexesFromMembraneProxy } from './utils';
/**
@@ -64,7 +63,7 @@ import { logWarning, unboxRegexesFromMembraneProxy } from './utils';
type DistortionMap = Map<
unknown,
(originalAttrOrMethod: unknown, pluginMeta: PluginMeta, sandboxEnv?: SandboxEnvironment) => unknown
(originalAttrOrMethod: unknown, pluginMeta: SandboxPluginMeta, sandboxEnv?: SandboxEnvironment) => unknown
>;
const generalDistortionMap: DistortionMap = new Map();
@@ -91,7 +90,7 @@ export function getGeneralSandboxDistortionMap() {
return generalDistortionMap;
}
function failToSet(originalAttrOrMethod: unknown, meta: PluginMeta) {
function failToSet(originalAttrOrMethod: unknown, meta: SandboxPluginMeta) {
logWarning(`Plugin ${meta.id} tried to set a sandboxed property`, {
pluginId: meta.id,
attrOrMethod: String(originalAttrOrMethod),
@@ -112,7 +111,7 @@ function distortIframeAttributes(distortions: DistortionMap) {
for (const property of iframeHtmlForbiddenProperties) {
const descriptor = Object.getOwnPropertyDescriptor(HTMLIFrameElement.prototype, property);
if (descriptor) {
function fail(originalAttrOrMethod: unknown, meta: PluginMeta) {
function fail(originalAttrOrMethod: unknown, meta: SandboxPluginMeta) {
const pluginId = meta.id;
logWarning(`Plugin ${pluginId} tried to access iframe.${property}`, {
pluginId,
@@ -146,7 +145,7 @@ function distortIframeAttributes(distortions: DistortionMap) {
function distortConsole(distortions: DistortionMap) {
const descriptor = Object.getOwnPropertyDescriptor(window, 'console');
if (descriptor?.value) {
function getSandboxConsole(originalAttrOrMethod: unknown, meta: PluginMeta) {
function getSandboxConsole(originalAttrOrMethod: unknown, meta: SandboxPluginMeta) {
const pluginId = meta.id;
// we don't monitor the console because we expect a high volume of calls
if (monitorOnly) {
@@ -175,7 +174,7 @@ function distortConsole(distortions: DistortionMap) {
// set distortions to alert to always output to the console
function distortAlert(distortions: DistortionMap) {
function getAlertDistortion(originalAttrOrMethod: unknown, meta: PluginMeta) {
function getAlertDistortion(originalAttrOrMethod: unknown, meta: SandboxPluginMeta) {
const pluginId = meta.id;
logWarning(`Plugin ${pluginId} accessed window.alert`, {
pluginId,
@@ -201,7 +200,7 @@ function distortAlert(distortions: DistortionMap) {
}
function distortInnerHTML(distortions: DistortionMap) {
function getInnerHTMLDistortion(originalMethod: unknown, meta: PluginMeta) {
function getInnerHTMLDistortion(originalMethod: unknown, meta: SandboxPluginMeta) {
const pluginId = meta.id;
return function innerHTMLDistortion(this: HTMLElement, ...args: string[]) {
for (const arg of args) {
@@ -246,7 +245,7 @@ function distortInnerHTML(distortions: DistortionMap) {
}
function distortCreateElement(distortions: DistortionMap) {
function getCreateElementDistortion(originalMethod: unknown, meta: PluginMeta) {
function getCreateElementDistortion(originalMethod: unknown, meta: SandboxPluginMeta) {
const pluginId = meta.id;
return function createElementDistortion(this: HTMLElement, arg?: string, options?: unknown) {
if (arg && forbiddenElements.includes(arg)) {
@@ -272,7 +271,7 @@ function distortCreateElement(distortions: DistortionMap) {
}
function distortInsert(distortions: DistortionMap) {
function getInsertDistortion(originalMethod: unknown, meta: PluginMeta) {
function getInsertDistortion(originalMethod: unknown, meta: SandboxPluginMeta) {
const pluginId = meta.id;
return function insertChildDistortion(this: HTMLElement, node?: Node, ref?: Node) {
const nodeType = node?.nodeName?.toLowerCase() || '';
@@ -294,7 +293,7 @@ function distortInsert(distortions: DistortionMap) {
};
}
function getinsertAdjacentElementDistortion(originalMethod: unknown, meta: PluginMeta) {
function getinsertAdjacentElementDistortion(originalMethod: unknown, meta: SandboxPluginMeta) {
const pluginId = meta.id;
return function insertAdjacentElementDistortion(this: HTMLElement, position?: string, node?: Node) {
const nodeType = node?.nodeName?.toLowerCase() || '';
@@ -336,7 +335,7 @@ function distortInsert(distortions: DistortionMap) {
// set distortions to append elements to the document
function distortAppend(distortions: DistortionMap) {
// append accepts an array of nodes to append https://developer.mozilla.org/en-US/docs/Web/API/Node/append
function getAppendDistortion(originalMethod: unknown, meta: PluginMeta) {
function getAppendDistortion(originalMethod: unknown, meta: SandboxPluginMeta) {
const pluginId = meta.id;
return function appendDistortion(this: HTMLElement, ...args: Node[]) {
let acceptedNodes = args;
@@ -363,7 +362,7 @@ function distortAppend(distortions: DistortionMap) {
}
// appendChild accepts a single node to add https://developer.mozilla.org/en-US/docs/Web/API/Node/appendChild
function getAppendChildDistortion(originalMethod: unknown, meta: PluginMeta, sandboxEnv?: SandboxEnvironment) {
function getAppendChildDistortion(originalMethod: unknown, meta: SandboxPluginMeta, sandboxEnv?: SandboxEnvironment) {
const pluginId = meta.id;
return function appendChildDistortion(this: HTMLElement, arg?: Node) {
const nodeType = arg?.nodeName?.toLowerCase() || '';
@@ -383,7 +382,7 @@ function distortAppend(distortions: DistortionMap) {
// this allows webpack chunks to be loaded into the sandbox
// loadScriptIntoSandbox has restrictions on what scripts can be loaded
if (sandboxEnv && arg && nodeType === 'script' && arg instanceof HTMLScriptElement) {
loadScriptIntoSandbox(arg.src, meta, sandboxEnv)
loadScriptIntoSandbox(arg.src, sandboxEnv)
.then(() => {
arg.onload?.call(arg, new Event('load'));
})
@@ -1,9 +1,9 @@
import { isFunction } from 'lodash';
import React, { ComponentType, FC } from 'react';
import { GrafanaPlugin, PluginExtensionConfig, PluginMeta, PluginType } from '@grafana/data';
import { GrafanaPlugin, PluginExtensionConfig, PluginType } from '@grafana/data';
import { SandboxedPluginObject } from './types';
import { SandboxPluginMeta, SandboxedPluginObject } from './types';
import { isSandboxedPluginObject } from './utils';
/**
@@ -27,7 +27,7 @@ import { isSandboxedPluginObject } from './utils';
*/
export async function sandboxPluginComponents(
pluginExports: System.Module,
meta: PluginMeta
meta: SandboxPluginMeta
): Promise<SandboxedPluginObject | System.Module> {
if (!isSandboxedPluginObject(pluginExports)) {
// we should monitor these cases. There should not be any plugins without a plugin export loaded inside the sandbox
@@ -89,7 +89,7 @@ export async function sandboxPluginComponents(
const withSandboxWrapper = <P extends object>(
WrappedComponent: ComponentType<P>,
pluginMeta: PluginMeta
pluginMeta: SandboxPluginMeta
): React.MemoExoticComponent<FC<P>> => {
const WithWrapper = React.memo((props: P) => {
return (
@@ -1,13 +1,11 @@
import createVirtualEnvironment from '@locker/near-membrane-dom';
import { ProxyTarget } from '@locker/near-membrane-shared';
import { BootData, PluginMeta } from '@grafana/data';
import { BootData } from '@grafana/data';
import { config } from '@grafana/runtime';
import { defaultTrustedTypesPolicy } from 'app/core/trustedTypePolicies';
import { getPluginSettings } from '../pluginSettings';
import { getPluginCode, patchSandboxEnvironmentPrototype } from './code_loader';
import { getPluginCode, getPluginLoadData, patchSandboxEnvironmentPrototype } from './code_loader';
import { getGeneralSandboxDistortionMap, distortLiveApis } from './distortion_map';
import {
getSafeSandboxDomElement,
@@ -19,7 +17,7 @@ import {
} from './document_sandbox';
import { sandboxPluginDependencies } from './plugin_dependencies';
import { sandboxPluginComponents } from './sandbox_components';
import { CompartmentDependencyModule, PluginFactoryFunction, SandboxEnvironment } from './types';
import { CompartmentDependencyModule, PluginFactoryFunction, SandboxEnvironment, SandboxPluginMeta } from './types';
import { logError, logInfo } from './utils';
// Loads near membrane custom formatter for near membrane proxy objects.
@@ -33,7 +31,7 @@ const pluginLogCache: Record<string, boolean> = {};
export async function importPluginModuleInSandbox({ pluginId }: { pluginId: string }): Promise<System.Module> {
patchWebAPIs();
try {
const pluginMeta = await getPluginSettings(pluginId);
const pluginMeta = getPluginLoadData(pluginId);
if (!pluginImportCache.has(pluginId)) {
pluginImportCache.set(pluginId, doImportPluginModuleInSandbox(pluginMeta));
}
@@ -48,7 +46,7 @@ export async function importPluginModuleInSandbox({ pluginId }: { pluginId: stri
}
}
async function doImportPluginModuleInSandbox(meta: PluginMeta): Promise<System.Module> {
async function doImportPluginModuleInSandbox(meta: SandboxPluginMeta): Promise<System.Module> {
logInfo('Loading with sandbox', {
pluginId: meta.id,
});
+3 -1
View File
@@ -1,6 +1,6 @@
import createVirtualEnvironment from '@locker/near-membrane-dom';
import { GrafanaPlugin } from '@grafana/data';
import { GrafanaPlugin, PluginMeta } from '@grafana/data';
export type CompartmentDependencyModule = unknown;
export type PluginFactoryFunction = (...args: CompartmentDependencyModule[]) => SandboxedPluginObject;
@@ -10,3 +10,5 @@ export type SandboxedPluginObject = {
};
export type SandboxEnvironment = ReturnType<typeof createVirtualEnvironment>;
export type SandboxPluginMeta = Pick<PluginMeta, 'id' | 'type' | 'module'>;