You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ru...@apache.org on 2026/06/01 23:05:21 UTC

(superset) branch fix/nvd3-tooltip-html-sanitization created (now 4debd2d01ac)

This is an automated email from the ASF dual-hosted git repository.

rusackas pushed a change to branch fix/nvd3-tooltip-html-sanitization
in repository https://gitbox.apache.org/repos/asf/superset.git


      at 4debd2d01ac fix(legacy-preset-chart-nvd3): sanitize tooltip HTML built from chart data

This branch includes the following new commits:

     new 4debd2d01ac fix(legacy-preset-chart-nvd3): sanitize tooltip HTML built from chart data

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(superset) 01/01: fix(legacy-preset-chart-nvd3): sanitize tooltip HTML built from chart data

Posted by ru...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rusackas pushed a commit to branch fix/nvd3-tooltip-html-sanitization
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 4debd2d01ac40dfbed98e0a07467b35135000731
Author: Claude Code <no...@anthropic.com>
AuthorDate: Mon Jun 1 16:05:03 2026 -0700

    fix(legacy-preset-chart-nvd3): sanitize tooltip HTML built from chart data
    
    Several tooltip generators in this module build HTML strings from chart
    data and return them to be rendered via D3 `.html()`, but did not run the
    result through DOMPurify the way the sibling generators in the same file
    already do. Apply `dompurify.sanitize()` to the returned HTML in
    `generateBubbleTooltipContent`, `generateMultiLineTooltipContent`, and the
    `tipFactory` annotation tooltip callback so data-derived values are
    rendered as text rather than markup.
    
    Adds regression tests asserting that script/handler markup in the
    data-derived fields is stripped from the generated tooltip HTML.
    
    Co-Authored-By: Claude Opus 4.8 <no...@anthropic.com>
---
 .../plugins/legacy-preset-chart-nvd3/src/utils.ts  | 12 +++--
 .../legacy-preset-chart-nvd3/test/utils.test.ts    | 60 ++++++++++++++++++++++
 2 files changed, 67 insertions(+), 5 deletions(-)

diff --git a/superset-frontend/plugins/legacy-preset-chart-nvd3/src/utils.ts b/superset-frontend/plugins/legacy-preset-chart-nvd3/src/utils.ts
index 9fdeeeafa61..64e9a3a4945 100644
--- a/superset-frontend/plugins/legacy-preset-chart-nvd3/src/utils.ts
+++ b/superset-frontend/plugins/legacy-preset-chart-nvd3/src/utils.ts
@@ -162,7 +162,7 @@ export function generateMultiLineTooltipContent(d, xFormatter, yFormatters) {
 
   tooltip += '</tbody></table>';
 
-  return tooltip;
+  return dompurify.sanitize(tooltip);
 }
 
 export function generateTimePivotTooltip(d, xFormatter, yFormatter) {
@@ -223,7 +223,7 @@ export function generateBubbleTooltipContent({
   s += createHTMLRow(getLabel(sizeField), sizeFormatter(point.size));
   s += '</table>';
 
-  return s;
+  return dompurify.sanitize(s);
 }
 
 // shouldRemove indicates whether the nvtooltips should be removed from the DOM
@@ -287,9 +287,11 @@ export function tipFactory(layer) {
         ? layer.descriptionColumns.map(c => d[c])
         : Object.values(d);
 
-      return `<div><strong>${title}</strong></div><br/><div>${body.join(
-        ', ',
-      )}</div>`;
+      return dompurify.sanitize(
+        `<div><strong>${title}</strong></div><br/><div>${body.join(
+          ', ',
+        )}</div>`,
+      );
     });
 }
 
diff --git a/superset-frontend/plugins/legacy-preset-chart-nvd3/test/utils.test.ts b/superset-frontend/plugins/legacy-preset-chart-nvd3/test/utils.test.ts
index a883d4c6b7f..aed48eb8bb0 100644
--- a/superset-frontend/plugins/legacy-preset-chart-nvd3/test/utils.test.ts
+++ b/superset-frontend/plugins/legacy-preset-chart-nvd3/test/utils.test.ts
@@ -26,6 +26,9 @@ import {
   computeYDomain,
   getTimeOrNumberFormatter,
   formatLabel,
+  generateBubbleTooltipContent,
+  generateMultiLineTooltipContent,
+  tipFactory,
 } from '../src/utils';
 
 const DATA = [
@@ -181,4 +184,61 @@ describe('nvd3/utils', () => {
       ]);
     });
   });
+
+  describe('tooltip HTML sanitization', () => {
+    const identity = (v: unknown) => v;
+
+    test('generateBubbleTooltipContent strips scripts from entity/group', () => {
+      const html = generateBubbleTooltipContent({
+        point: {
+          name: '<img src=x onerror="alert(1)">',
+          group: '<script>alert(2)</script>',
+          color: 'red',
+          x: 1,
+          y: 2,
+          size: 3,
+        },
+        entity: 'name',
+        xField: 'x',
+        yField: 'y',
+        sizeField: 'size',
+        xFormatter: identity,
+        yFormatter: identity,
+        sizeFormatter: identity,
+      });
+
+      expect(html).not.toContain('onerror');
+      expect(html).not.toContain('<script>');
+    });
+
+    test('generateMultiLineTooltipContent strips scripts from series keys', () => {
+      const html = generateMultiLineTooltipContent(
+        {
+          value: 'x',
+          series: [
+            { key: '<img src=x onerror="alert(1)">', color: 'red', value: 1 },
+          ],
+        },
+        identity,
+        [identity],
+      );
+
+      expect(html).not.toContain('onerror');
+    });
+
+    test('tipFactory strips scripts from annotation data values', () => {
+      const tip = tipFactory({
+        titleColumn: 'title',
+        name: 'layer',
+        descriptionColumns: ['desc'],
+      });
+      const html = tip.html()({
+        title: '<img src=x onerror="alert(1)">',
+        desc: '<script>alert(2)</script>',
+      });
+
+      expect(html).not.toContain('onerror');
+      expect(html).not.toContain('<script>');
+    });
+  });
 });