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:
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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'>;
|
||||
|
||||
Reference in New Issue
Block a user