The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/public/app/features/plugins/sandbox/utils.ts

118 lines
3.4 KiB

import { isNearMembraneProxy } from '@locker/near-membrane-shared';
import React from 'react';
import { PluginSignatureType, PluginType } from '@grafana/data';
import { LogContext } from '@grafana/faro-web-sdk';
import { logWarning as logWarningRuntime, logError as logErrorRuntime, config } from '@grafana/runtime';
import { getPluginSettings } from '../pluginSettings';
import { SandboxedPluginObject } from './types';
const monitorOnly = Boolean(config.featureToggles.frontendSandboxMonitorOnly);
export function isSandboxedPluginObject(value: unknown): value is SandboxedPluginObject {
return !!value && typeof value === 'object' && value?.hasOwnProperty('plugin');
}
export function assertNever(x: never): never {
throw new Error(`Unexpected object: ${x}. This should never happen.`);
}
export function isReactClassComponent(obj: unknown): obj is React.Component {
return obj instanceof React.Component;
}
export function logWarning(message: string, context?: LogContext) {
context = {
...context,
source: 'sandbox',
monitorOnly: String(monitorOnly),
};
logWarningRuntime(message, context);
}
export function logError(error: Error, context?: LogContext) {
context = {
...context,
source: 'sandbox',
monitorOnly: String(monitorOnly),
};
logErrorRuntime(error, context);
}
export async function isFrontendSandboxSupported({
isAngular,
pluginId,
}: {
isAngular?: boolean;
pluginId: string;
}): Promise<boolean> {
// Only if the feature is not enabled no support for sandbox
if (!Boolean(config.featureToggles.pluginsFrontendSandbox)) {
return false;
}
// no support for angular plugins
if (isAngular) {
return false;
}
// To fast test and debug the sandbox in the browser.
const sandboxDisableQueryParam = location.search.includes('nosandbox') && config.buildInfo.env === 'development';
if (sandboxDisableQueryParam) {
return false;
}
// if disabled by configuration
const isPluginExcepted = config.disableFrontendSandboxForPlugins.includes(pluginId);
if (isPluginExcepted) {
return false;
}
// no sandbox in test mode. it often breaks e2e tests
if (process.env.NODE_ENV === 'test') {
return false;
}
// we don't run grafana-own apps in the sandbox
const pluginMeta = await getPluginSettings(pluginId);
if (pluginMeta.type === PluginType.app && pluginMeta.signatureType === PluginSignatureType.grafana) {
return false;
}
return true;
}
function isRegex(value: unknown): value is RegExp {
return value?.constructor?.name === 'RegExp';
}
/**
* Near membrane regex proxy objects behave just exactly the same as regular RegExp objects
* with only one difference: they are not `instanceof RegExp`.
* This function takes a structure and makes sure any regex that is a nearmembraneproxy
* and returns the same regex but in the bluerealm
*/
export function unboxRegexesFromMembraneProxy(structure: unknown): unknown {
if (!structure) {
return structure;
}
// Proxy regexes loook and behave like proxies but they
// are not instanceof RegExp
if (isRegex(structure) && isNearMembraneProxy(structure)) {
return new RegExp(structure);
}
if (Array.isArray(structure)) {
return structure.map(unboxRegexesFromMembraneProxy);
}
if (typeof structure === 'object') {
return Object.keys(structure).reduce((acc, key) => {
Reflect.set(acc, key, unboxRegexesFromMembraneProxy(Reflect.get(structure, key)));
return acc;
}, {});
}
return structure;
}