Skip to content

fix: Preserve XML structural entities during decode - #7838

Merged
camdecoster merged 4 commits into
masterfrom
cam/avoid-decoding-unsafe-characters-tosvg
Jun 16, 2026
Merged

fix: Preserve XML structural entities during decode#7838
camdecoster merged 4 commits into
masterfrom
cam/avoid-decoding-unsafe-characters-tosvg

Conversation

@camdecoster

@camdecoster camdecoster commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Description

Preserve XML structural entities during decode when exporting SVG.

Changes

  • Update htmlEntityDecode to preserve the five XML structural entities (&, ", ', <, >) in named, decimal, and hex numeric forms instead of decoding them
  • Add tests to check if entities are preserved decoding

Testing

  • Be on master
  • Open Plotly devtools
  • In browser devtools, enter the following in the console:
    const fig = {
      data: [{ x: [1], y: [1], type: "scatter" }],
      layout: {
        annotations: [
          {
            x: 1,
            y: 1,
            showarrow: false,
            text: '<span style="x:&#34; onmouseover=&#34;alert(1)&#34; a=&#34;">Zoidberg</span>',
          },
        ],
      },
    };
    
    Plotly.newPlot(gd, fig)
      .then(() =>
        Plotly.toImage(gd, {
          format: "svg",
          imageDataOnly: true,
        }),
      )
      .then((svg) => console.log(decodeURIComponent(svg)));
  • Toward the end of the log statement, note that onmouseover has been injected into the SVG as an attribute
  • Switch to this branch
  • Go through the same process
  • Toward the end of the log statement, note that onmouseover is only part of the style attribute

Notes

  • Attack path. htmlEntityDecode in tosvg.js ran after XMLSerializer.serializeToString and decoded &quot; / &#34; / &#x22; back to literal " chars in attribute values the serializer had safely escaped, letting the payload break out of style="..." and inject arbitrary attributes (onmouseover=, onclick=, …).
  • Affected callers. Plotly.toImage, Plotly.downloadImage, and downstream products that embed user-authored figure SVG into a privileged origin (shared-plot previews, dashboards, exported-image galleries).
  • Non-browser SVG renderers. htmlEntityDecode exists partly so exported SVG renders correctly in Batik / Illustrator / kaleido (for PDF/EPS). The fix preserves five more entities in the output; SVG is still valid XML.
  • References:
@camdecoster
camdecoster marked this pull request as ready for review June 11, 2026 21:45
emilykl
emilykl previously approved these changes Jun 15, 2026

@emilykl emilykl left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, thanks @camdecoster 🚀 Can you add a draftlog?

@camdecoster
camdecoster merged commit 1a7dcfd into master Jun 16, 2026
83 of 84 checks passed
@camdecoster
camdecoster deleted the cam/avoid-decoding-unsafe-characters-tosvg branch June 16, 2026 20:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants