From cf64fe3b2b8f639d7d3f0ed4786bc7b8058e30c2 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 8 Apr 2025 13:03:32 +0200 Subject: [PATCH] Added additional sanitation to get rid of the tracker element. --- .env.sample | 1 + lib/envs.js | 9 +++++++++ lib/export.js | 3 ++- lib/sanitize.js | 54 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 1 deletion(-) diff --git a/.env.sample b/.env.sample index 34a9095c..f09fd5fa 100644 --- a/.env.sample +++ b/.env.sample @@ -81,6 +81,7 @@ OTHER_LISTEN_TO_PROCESS_EXITS = true OTHER_NO_LOGO = false OTHER_HARD_RESET_PAGE = false OTHER_BROWSER_SHELL_MODE = true +OTHER_EXCLUDE_CLASSES = highcharts-tracker-area # DEBUG CONFIG DEBUG_ENABLE = false diff --git a/lib/envs.js b/lib/envs.js index 5399293e..a8cd7bcd 100644 --- a/lib/envs.js +++ b/lib/envs.js @@ -232,6 +232,15 @@ export const Config = z.object({ OTHER_HARD_RESET_PAGE: v.boolean(), OTHER_BROWSER_SHELL_MODE: v.boolean(), OTHER_ALLOW_XLINK: v.boolean(), + OTHER_EXCLUDE_CLASSES: z + .string() + .transform((value) => + value + .split(',') + .map((value) => value.trim()) + .filter((value) => value !== '') + ) + .transform((value) => (value.length ? value : undefined)), // debugger DEBUG_ENABLE: v.boolean(), diff --git a/lib/export.js b/lib/export.js index ed14bd80..368f3e76 100644 --- a/lib/export.js +++ b/lib/export.js @@ -16,6 +16,7 @@ import { addPageResources, clearPageResources } from './browser.js'; import { getCache } from './cache.js'; import { triggerExport } from './highcharts.js'; import { log } from './logger.js'; +import { sanitizeSVG } from './sanitize.js'; import svgTemplate from './../templates/svg_export/svg_export.js'; @@ -292,7 +293,7 @@ export default async (page, chart, options) => { // Rasterization process if (exportOptions.type === 'svg') { // SVG - data = await createSVG(page); + data = sanitizeSVG(await createSVG(page)); } else if (['png', 'jpeg'].includes(exportOptions.type)) { // PNG or JPEG data = await createImage( diff --git a/lib/sanitize.js b/lib/sanitize.js index a348a6f1..fdfc7604 100644 --- a/lib/sanitize.js +++ b/lib/sanitize.js @@ -21,12 +21,65 @@ import { JSDOM } from 'jsdom'; import DOMPurify from 'dompurify'; import { envs } from './envs.js'; + +// List of class names to be excluded from sanitization of SVG +const excludedClasses = envs.OTHER_EXCLUDE_CLASSES || []; + +/** + * Registers a DOMPurify hook to remove elements with specific class names + * during sanitization. + * + * @param {Window} window - The window object, used to access DOM constructors + * like `Element`. + * @param {DOMPurify.DOMPurifyI} purify - The DOMPurify instance to which the + * hook will be added. + */ +export function excludeElementsByClass(window, purify) { + // Add a hook to remove elements during sanitization + purify.addHook('uponSanitizeElement', (node) => { + excludedClasses.forEach((className) => { + // Check if the element has the excluded class + if ( + node instanceof window.Element && + node.getAttribute('class')?.includes(className) + ) { + // Remove the node from the DOM + node.parentNode?.removeChild(node); + } + }); + }); +} + +/** + * Sanitizes a given SVG string by removing potential element of specified class + * names. + * + * @param {string} input The SVG string to be sanitized. + * @param {Object} [options={}] Optional configuration options for DOMPurify. + * + * @returns {string} The sanitized SVG string. + */ +export function sanitizeSVG(input, options = {}) { + // Check if sanitization is needed + if (excludedClasses.length) { + const window = new JSDOM('').window; + const purify = DOMPurify(window); + + excludeElementsByClass(window, purify); + return purify.sanitize(input, { + RETURN_DOM: false, + ...options + }); + } +} + /** * Sanitizes a given HTML string by removing tags and any content within them. * * @param {string} input The HTML string to be sanitized. + * * @returns {string} The sanitized HTML string. */ export function sanitize(input) { @@ -38,6 +91,7 @@ export function sanitize(input) { const window = new JSDOM('').window; const purify = DOMPurify(window); + return purify.sanitize(input, { ADD_TAGS: ['foreignObject'], FORBID_ATTR: forbidden