mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Sandbox: Add SRI validation for CDN assets loaded in the sandbox (#99023)
Add SRI validation for frontend sandbox
This commit is contained in:
parent
f9cc08a42d
commit
f65e9fa9ab
@ -52,7 +52,12 @@ export async function getPluginCode(meta: SandboxPluginMeta): Promise<string> {
|
||||
// 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();
|
||||
if (!verifySRI(pluginCode, meta.moduleHash)) {
|
||||
throw new Error('Invalid SRI for plugin module file');
|
||||
}
|
||||
|
||||
pluginCode = transformPluginSourceForCDN({
|
||||
url,
|
||||
source: pluginCode,
|
||||
@ -66,7 +71,12 @@ export async function getPluginCode(meta: SandboxPluginMeta): Promise<string> {
|
||||
// to ensure correct cached version is served for local plugins
|
||||
const pluginCodeUrl = resolveWithCache(modulePath);
|
||||
const response = await fetch(pluginCodeUrl);
|
||||
|
||||
let pluginCode = await response.text();
|
||||
if (!verifySRI(pluginCode, meta.moduleHash)) {
|
||||
throw new Error('Invalid SRI for plugin module file');
|
||||
}
|
||||
|
||||
pluginCode = transformPluginSourceForCDN({
|
||||
url: pluginCodeUrl,
|
||||
source: pluginCode,
|
||||
@ -78,6 +88,27 @@ export async function getPluginCode(meta: SandboxPluginMeta): Promise<string> {
|
||||
}
|
||||
}
|
||||
|
||||
async function verifySRI(pluginCode: string, moduleHash?: string): Promise<boolean> {
|
||||
if (!config.featureToggles.pluginsSriChecks) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!moduleHash || moduleHash.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const [algorithm, _] = moduleHash.split('-');
|
||||
const cleanAlgorithm = algorithm.replace('sha', 'SHA-');
|
||||
|
||||
const encoder = new TextEncoder();
|
||||
const data = encoder.encode(pluginCode);
|
||||
|
||||
const digest = await crypto.subtle.digest(cleanAlgorithm, data);
|
||||
const actualHash = btoa(String.fromCharCode(...new Uint8Array(digest)));
|
||||
|
||||
return `${algorithm}-${actualHash}` === moduleHash;
|
||||
}
|
||||
|
||||
function patchPluginAPIs(pluginCode: string): string {
|
||||
return pluginCode.replace(/window\.location/gi, 'window.locationSandbox');
|
||||
}
|
||||
@ -113,6 +144,7 @@ export function getPluginLoadData(pluginId: string): SandboxPluginMeta {
|
||||
id: pluginId,
|
||||
type: PluginType.app,
|
||||
module: app.path,
|
||||
moduleHash: app.moduleHash,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -11,4 +11,4 @@ export type SandboxedPluginObject = {
|
||||
|
||||
export type SandboxEnvironment = ReturnType<typeof createVirtualEnvironment>;
|
||||
|
||||
export type SandboxPluginMeta = Pick<PluginMeta, 'id' | 'type' | 'module'>;
|
||||
export type SandboxPluginMeta = Pick<PluginMeta, 'id' | 'type' | 'module' | 'moduleHash'>;
|
||||
|
Loading…
Reference in New Issue
Block a user