Skip to content

fix(charts): sanitize tooltip HTML across nvd3, rose and partition plugins#40502

Open
sha174n wants to merge 9 commits into
apache:masterfrom
sha174n:fix/improve-validation-1779974800
Open

fix(charts): sanitize tooltip HTML across nvd3, rose and partition plugins#40502
sha174n wants to merge 9 commits into
apache:masterfrom
sha174n:fix/improve-validation-1779974800

Conversation

@sha174n
Copy link
Copy Markdown
Contributor

@sha174n sha174n commented May 28, 2026

SUMMARY

Closes the tooltip-HTML sinks across the legacy NVD3 preset and the Rose and Partition plugins. Routes user-controlled column / series / annotation values through DOMPurify (in the nvd3 preset, which already depends on it) and through sanitizeHtml from @superset-ui/core (in Rose and Partition, where adding a new dep is unwanted) before they reach d3-tip's or nv.models.tooltip's .html() sink.

Changes

superset-frontend/plugins/legacy-preset-chart-nvd3/src/utils.ts

  • tipFactory(): wrap the annotation-tooltip HTML in dompurify.sanitize(...) before returning, consistent with the sibling generateCompareTooltipContent / generateTimePivotTooltip helpers.
  • generateBubbleTooltipContent(): wrap the returned HTML in dompurify.sanitize(...). Closes the Bubble-chart tooltip variant.
  • generateMultiLineTooltipContent(): wrap the returned HTML in dompurify.sanitize(...), and pass shouldDompurify=true into getFormattedKey so the per-series key is also sanitised in line with generateCompareTooltipContent.
  • wrapTooltip(): sanitise the wrapped content. This is the single choke point through which every chart constructed in NVD3Vis.ts flows (it is invoked at the bottom of the chart factory), so it closes the default nvd3-fork tooltip path used by Line, Bar, Area, Pie, BoxPlot, etc. when no custom contentGenerator is set. Custom generators set earlier already return sanitised output, making this a belt-and-braces wrap.

superset-frontend/plugins/legacy-plugin-chart-rose/src/Rose.ts

  • tooltipData(): pass series[].key (a user-controlled column value) through sanitizeHtml before it reaches nv.models.tooltip().data(...), which renders the key via .html().
  • legendData(): sanitise key at the data boundary too. The legend currently renders key via .text(), but applying the same boundary makes the safety property local to the data layer rather than relying on the vendored legend module's rendering choice.

superset-frontend/plugins/legacy-plugin-chart-partition/src/Partition.ts

  • positionAndPopulate(): wrap the built tooltip HTML with sanitizeHtml(t) before tip.html(t). The xss-filters allowlist preserves table, tr, td, div, and style attributes so visual rendering is unchanged for benign input; dangerous markup (script, on*, iframe, etc.) is stripped.

scripts/oxlint.sh (drive-by)

  • Replaced the final [ -n "$output" ] && echo "$output" with an explicit if, because under set -e the bare conditional aborts the script when oxlint succeeds with empty output (i.e. clean lint). With oxlint 1.66+ installed the pre-commit hook was failing on no-output runs; this restores it.

Tests

superset-frontend/plugins/legacy-preset-chart-nvd3/test/utils.test.ts

Regression tests assert that DOMPurify strips a <script> / <img onerror> payload smuggled through:

  • generateBubbleTooltipContent via point.entity and point.group
  • generateMultiLineTooltipContent via series.key
  • tipFactory via a description column value

Rose and Partition rely on inner closures over d3 / nvd3-fork DOM state and are not unit-testable in isolation; their fixes are minimal wrappers around well-tested sanitisers. Manual verification covers each chart type end-to-end.

Testing instructions

  • cd superset-frontend && npx jest plugins/legacy-preset-chart-nvd3/test/utils.test.ts — 13 tests pass (9 existing + 4 new XSS regressions).
  • Manual: render a Bubble / Line / Bar / Area / Pie / BoxPlot / Rose / Partition / NVD3-with-annotation chart whose dataset contains a column value such as <img src=x onerror=alert(1)>; the tooltip on hover must render the text without firing the payload.

Additional information

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
  • Introduces new feature or API
  • Removes existing feature or API

Route the annotation tooltip HTML through DOMPurify before it is passed
to d3-tip's .html() sink, matching the sanitization already applied in
sibling tooltip helpers in this file.
@codecov
Copy link
Copy Markdown

codecov Bot commented May 28, 2026

Codecov Report

❌ Patch coverage is 71.42857% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.02%. Comparing base (838ee27) to head (481e9c0).

Files with missing lines Patch % Lines
...tend/plugins/legacy-preset-chart-nvd3/src/utils.ts 71.42% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #40502      +/-   ##
==========================================
+ Coverage   63.99%   64.02%   +0.02%     
==========================================
  Files        2637     2637              
  Lines      141678   141677       -1     
  Branches    32570    32570              
==========================================
+ Hits        90670    90705      +35     
+ Misses      49452    49416      -36     
  Partials     1556     1556              
Flag Coverage Δ
hive 39.70% <ø> (ø)
javascript 67.38% <71.42%> (+0.04%) ⬆️
mysql 58.51% <ø> (ø)
postgres 58.59% <ø> (ø)
presto 41.32% <ø> (ø)
python 60.09% <ø> (ø)
sqlite 58.25% <ø> (ø)
unit 100.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…ugins

Expand the SEC-125 cluster's annotation-tooltip fix to cover every
confirmed tooltip HTML sink in the cluster, plus the default nvd3
tooltip path that shares the same root cause.

- legacy-preset-chart-nvd3/src/utils.ts: wrap generateBubbleTooltipContent,
  generateMultiLineTooltipContent, and wrapTooltip output with DOMPurify;
  flip getFormattedKey shouldDompurify to true for consistency.
- legacy-plugin-chart-rose/src/Rose.ts: sanitize series keys in
  tooltipData and legendData via sanitizeHtml.
- legacy-plugin-chart-partition/src/Partition.ts: sanitize tooltip HTML
  with sanitizeHtml(t) before tip.html(t).
- legacy-preset-chart-nvd3/test/utils.test.ts: regression tests for the
  three nvd3 utils helpers (script and img onerror payloads).
- scripts/oxlint.sh: drive-by; last line was '[ -n "$output" ] && echo "$output"'
  which under 'set -e' aborts the script when oxlint succeeds with empty
  output. Replaced with an explicit 'if [ -n "$output" ]; then ... fi'
  so a clean lint run no longer fails the pre-commit hook.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@pull-request-size pull-request-size Bot added size/L and removed size/S labels May 28, 2026
@github-actions github-actions Bot added the risk:ci-script PR modifies scripts that execute in CI (supply chain risk) label May 28, 2026
@sha174n sha174n changed the title fix(nvd3): sanitize annotation tooltip HTML content fix(charts): sanitize tooltip HTML across nvd3, rose and partition plugins May 28, 2026
@sha174n sha174n marked this pull request as ready for review May 28, 2026 14:14
@dosubot dosubot Bot added the viz:charts:partition Related to the Partition chart label May 28, 2026
Comment thread superset-frontend/plugins/legacy-plugin-chart-rose/src/Rose.ts Outdated
@bito-code-review
Copy link
Copy Markdown
Contributor

The PR comments file contains only the header row and no actual comment data. I cannot analyze or validate the review suggestion without the specific comment content.

@bito-code-review
Copy link
Copy Markdown
Contributor

bito-code-review Bot commented May 28, 2026

Code Review Agent Run #7ed07c

Actionable Suggestions - 0
Additional Suggestions - 2
  • superset-frontend/plugins/legacy-preset-chart-nvd3/src/utils.ts - 2
    • CWE-79: Missing XSS Test Coverage · Line 264-274
      `wrapTooltip` sanitizes the nvd3 default contentGenerator output but lacks test coverage. Add a test asserting that `<script>alert(1)</script>` embedded in a series key is stripped from the tooltip output. This function is invoked for Line, Bar, Area, Pie, BoxPlot, and other standard charts per line 1040 in `NVD3Vis.ts`.
    • Object injection via Object.values dynamic access · Line 292-292
      Line 292 uses `Object.values(d)` which is flagged as a generic object injection sink (CWE-1321). The object `d` may contain untrusted data. Ensure that the data source is validated and that only expected properties are accessed.
      Code suggestion
       @@ -291,3 +291,3 @@
              const rawBody = Array.isArray(layer.descriptionColumns)
                  ? layer.descriptionColumns.map(c => d[c])
      -          : Object.values(d);
      +          : Object.keys(d).filter(k => typeof k === 'string' && !k.startsWith('__')).map(k => d[k]);
Review Details
  • Files reviewed - 5 · Commit Range: 1fa6efe..89f5af6
    • scripts/oxlint.sh
    • superset-frontend/plugins/legacy-plugin-chart-partition/src/Partition.ts
    • superset-frontend/plugins/legacy-plugin-chart-rose/src/Rose.ts
    • superset-frontend/plugins/legacy-preset-chart-nvd3/src/utils.ts
    • superset-frontend/plugins/legacy-preset-chart-nvd3/test/utils.test.ts
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Eslint (Linter) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

sha174n and others added 2 commits May 29, 2026 00:30
Restore the raw `v.name` as the legend `key` in Rose. The legend's
color callback is keyed on `key`, while arcs are filled by `colorFn(d.name)`
on the raw name, so sanitizing one side and not the other made the two
identities diverge and swatch/arc colors stop matching. nvd3-fork's legend
renders `key` via `.text()`, so the raw value is already escaped at the
DOM sink, no markup can reach the page.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@bito-code-review
Copy link
Copy Markdown
Contributor

bito-code-review Bot commented May 29, 2026

Code Review Agent Run #bfa2b4

Actionable Suggestions - 0
Additional Suggestions - 1
  • superset-frontend/plugins/legacy-plugin-chart-rose/src/Rose.ts - 1
    • XSS safety verified safe · Line 154-154
      The removal of `sanitizeHtml` from `legendData` is safe per the updated comment rationale: nvd3-fork's legend uses `.text()` for the key, which escapes HTML at the DOM sink. This also ensures `key` matches the raw name used by `colorFn` on arc fills (line 170, 177), preserving color consistency.
Review Details
  • Files reviewed - 1 · Commit Range: 89f5af6..02dbe78
    • superset-frontend/plugins/legacy-plugin-chart-rose/src/Rose.ts
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Eslint (Linter) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

plugins risk:ci-script PR modifies scripts that execute in CI (supply chain risk) size/L viz:charts:partition Related to the Partition chart

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant