From 053ee5cb1f4e335b90e12f033e5e5f15b6a579fa Mon Sep 17 00:00:00 2001 From: Esteban Beltran Date: Wed, 19 Mar 2025 07:24:32 -0600 Subject: [PATCH] Frontend Sandbox: Use DOMPurify to sanitize innerHTML (#102302) --- .../app/features/plugins/sandbox/distortion_map.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/public/app/features/plugins/sandbox/distortion_map.ts b/public/app/features/plugins/sandbox/distortion_map.ts index dac4301cbde..1680a4cd2df 100644 --- a/public/app/features/plugins/sandbox/distortion_map.ts +++ b/public/app/features/plugins/sandbox/distortion_map.ts @@ -1,4 +1,5 @@ import { ProxyTarget } from '@locker/near-membrane-shared'; +import DOMPurify from 'dompurify'; import { cloneDeep, isFunction } from 'lodash'; import { config } from '@grafana/runtime'; @@ -203,7 +204,8 @@ function distortInnerHTML(distortions: DistortionMap) { function getInnerHTMLDistortion(originalMethod: unknown, meta: SandboxPluginMeta) { const pluginId = meta.id; return function innerHTMLDistortion(this: HTMLElement, ...args: string[]) { - for (const arg of args) { + for (let i = 0; i < args.length; i++) { + const arg = args[i]; // NOTE: DOMPurify anti-tamper mechanism requires us to clone the string // calling any method whatsoever on a string will cause the string to be tampered // and DOMPurify will return empty strings @@ -223,6 +225,16 @@ function distortInnerHTML(distortions: DistortionMap) { throw new Error('<' + forbiddenElement + '> is not allowed in sandboxed plugins'); } } + // prevent some dom operations that use direct callbacks + if (lowerCase.match(/onerror|onload|onsuccess|onbeforeunload/)) { + logWarning(`Plugin ${pluginId} tried to set forbidden attribute in innerHTML`, { + pluginId, + attrOrMethod: 'innerHTML', + param: arg, + entity: 'HTMLElement', + }); + args[i] = DOMPurify.sanitize(args[i]); + } } if (isFunction(originalMethod)) {