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>');
+ });
+ });
});