diff --git a/public/app/features/plugins/sandbox/code_loader.ts b/public/app/features/plugins/sandbox/code_loader.ts index 3f2d614bf41..04bd418d33a 100644 --- a/public/app/features/plugins/sandbox/code_loader.ts +++ b/public/app/features/plugins/sandbox/code_loader.ts @@ -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 { +export async function getPluginCode(meta: SandboxPluginMeta): Promise { 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}`); +} diff --git a/public/app/features/plugins/sandbox/distortion_map.ts b/public/app/features/plugins/sandbox/distortion_map.ts index a883b9a763f..2989172874d 100644 --- a/public/app/features/plugins/sandbox/distortion_map.ts +++ b/public/app/features/plugins/sandbox/distortion_map.ts @@ -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')); }) diff --git a/public/app/features/plugins/sandbox/sandbox_components.tsx b/public/app/features/plugins/sandbox/sandbox_components.tsx index 9ff02f2da2f..6e26d86a37b 100644 --- a/public/app/features/plugins/sandbox/sandbox_components.tsx +++ b/public/app/features/plugins/sandbox/sandbox_components.tsx @@ -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 { 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 =

( WrappedComponent: ComponentType

, - pluginMeta: PluginMeta + pluginMeta: SandboxPluginMeta ): React.MemoExoticComponent> => { const WithWrapper = React.memo((props: P) => { return ( diff --git a/public/app/features/plugins/sandbox/sandbox_plugin_loader.ts b/public/app/features/plugins/sandbox/sandbox_plugin_loader.ts index 78619d3b93a..dc0b26dc2c0 100644 --- a/public/app/features/plugins/sandbox/sandbox_plugin_loader.ts +++ b/public/app/features/plugins/sandbox/sandbox_plugin_loader.ts @@ -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 = {}; export async function importPluginModuleInSandbox({ pluginId }: { pluginId: string }): Promise { 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 { +async function doImportPluginModuleInSandbox(meta: SandboxPluginMeta): Promise { logInfo('Loading with sandbox', { pluginId: meta.id, }); diff --git a/public/app/features/plugins/sandbox/types.ts b/public/app/features/plugins/sandbox/types.ts index aacd915ce0d..3b9e104f801 100644 --- a/public/app/features/plugins/sandbox/types.ts +++ b/public/app/features/plugins/sandbox/types.ts @@ -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; + +export type SandboxPluginMeta = Pick;