fix(tree): escape title, header, and JSON blob in emit_html

html.escape() the values that land in <title> and <h1>, and replace </
with <\/ in the JSON embedded inside <script> so crafted graph labels
or --label values cannot break out. Mirrors the _js_safe() pattern in
export.py.

Reported by Qodo on PR #557.
This commit is contained in:
dsremo
2026-04-27 11:55:21 +05:30
parent c3ba79f5aa
commit a52350940a
2 changed files with 11 additions and 3 deletions
+4
View File
@@ -16,6 +16,10 @@ Full release notes with details on each version: [GitHub Releases](https://githu
`--label NAME`.
- Implementation: `graphify/tree_html.py` (575 LOC, no external runtime
dependencies — D3 v7 is loaded from cdn.jsdelivr.net).
- Security: `emit_html()` now `html.escape()`s the page title and header,
and JS-escapes `</` sequences in the embedded JSON blob so crafted
graph labels or `--label` values cannot break out of `<title>`, `<h1>`,
or the `<script>` tag (matches the `_js_safe()` pattern in `export.py`).
## 0.4.23 (2026-04-18)
+7 -3
View File
@@ -34,6 +34,7 @@ Implementation notes:
from __future__ import annotations
import html as _html
import json
from collections import defaultdict
from pathlib import Path
@@ -546,12 +547,15 @@ def emit_html(
svg_width: int = 6000,
svg_height: int = 8000,
) -> str:
# Escape </script> sequences so embedded JSON cannot break out of the
# <script> tag, and HTML-escape values that land in <title>/<h1>.
data_json = json.dumps(tree, ensure_ascii=False, separators=(",", ":")).replace("</", "<\\/")
return _HTML_TEMPLATE.format(
title=title,
header=header,
title=_html.escape(title),
header=_html.escape(header),
svg_width=svg_width,
svg_height=svg_height,
data_json=json.dumps(tree, ensure_ascii=False, separators=(",", ":")),
data_json=data_json,
)