Bug
sanitize_label() in security.py calls html.escape() on all node labels and community names. These sanitized strings are then passed through json.dumps() and embedded inside a <script> tag in to_html().
This causes double-encoding: & becomes & in the JSON string values, which vis.js then renders literally as & in the browser instead of &.
Reproduction
Any node label containing & (e.g. "Abercrombie & Fitch", "CX & Behavioral Data") will display as Abercrombie & Fitch in the interactive graph.
Root cause
security.py:198 — html.escape() is appropriate for text injected into raw HTML, but the labels in to_html() are embedded inside json.dumps() within a <script> block (export.py:370-372). JavaScript string literals don't need HTML-escaping — json.dumps already handles the necessary escaping for that context.
Note:
I found this issue myself but I had Claude Opus 4.6 write it up
Bug
sanitize_label()insecurity.pycallshtml.escape()on all node labels and community names. These sanitized strings are then passed throughjson.dumps()and embedded inside a<script>tag into_html().This causes double-encoding:
&becomes&in the JSON string values, which vis.js then renders literally as&in the browser instead of&.Reproduction
Any node label containing
&(e.g. "Abercrombie & Fitch", "CX & Behavioral Data") will display asAbercrombie & Fitchin the interactive graph.Root cause
security.py:198—html.escape()is appropriate for text injected into raw HTML, but the labels into_html()are embedded insidejson.dumps()within a<script>block (export.py:370-372). JavaScript string literals don't need HTML-escaping —json.dumpsalready handles the necessary escaping for that context.Note:
I found this issue myself but I had Claude Opus 4.6 write it up