666ghj/MiroFish · error · StarHistoryError
generated SVG contains forbidden XML directives
Error message
generated SVG contains forbidden XML directives
What it means
Raised by `_validate_svg` (scripts/star_history.py:1083) when the payload contains XML processing directives: `<!DOCTYPE`, `<!ENTITY`, or `<?...` (e.g. `<?xml ...?>`). Because the SVG is embedded/published as a fixed, review-approved document, directives that could declare entities (XXE/billion-laughs vectors) or processing instructions are forbidden outright.
Source
Thrown at scripts/star_history.py:1083
)
return None
return None
def _validate_svg(payload: bytes) -> None:
try:
decoded_payload = payload.decode("utf-8", errors="strict")
except UnicodeDecodeError as exc:
raise StarHistoryError("generated SVG must be strict UTF-8") from exc
if decoded_payload.startswith("\ufeff") or "\x00" in decoded_payload:
raise StarHistoryError("generated SVG must be canonical UTF-8")
upper_payload = decoded_payload.upper()
if (
"<!DOCTYPE" in upper_payload
or "<!ENTITY" in upper_payload
or "<?" in decoded_payload
):
raise StarHistoryError("generated SVG contains forbidden XML directives")
try:
root = ET.fromstring(decoded_payload)
except ET.ParseError as exc:
raise StarHistoryError("generated SVG is not valid XML") from exc
svg_namespace = "{http://www.w3.org/2000/svg}"
if root.tag != f"{svg_namespace}svg":
raise StarHistoryError("generated SVG root is invalid")
allowed_attributes: dict[str, set[str]] = {
"svg": {
"viewBox",
"width",
"height",
"preserveAspectRatio",
"role",
"aria-labelledby",
},
"title": {"id"},
"desc": {"id"},View on GitHub (pinned to b5b53acc57)
Solutions
- Remove the XML declaration / DOCTYPE / processing instructions from the generation pipeline — the canonical output starts directly with `<svg ...>`.
- If using ElementTree serialization, keep `xml_declaration` off: `ET.tostring(root, encoding="unicode")` (no declaration).
- Drop xmllint/pretty-print post-processing steps that prepend `<?xml version="1.0"?>`.
- If you must keep a declaration for some consumer, strip it only after `_validate_svg`: publish `b"<?xml version=...?>" + payload` outside the validated artifact.
Example fix
# before: serializer emits a declaration -> validator sees '<?'
svg_text = ET.tostring(root, encoding="utf-8", xml_declaration=True)
# after: no declaration; canonical payload
svg_text = ET.tostring(root, encoding="unicode").encode("utf-8") Defensive patterns
Strategy: try-catch
Validate before calling
def has_forbidden_directives(payload: bytes) -> bool:
up = payload.upper()
return b"<!DOCTYPE" in up or b"<!ENTITY" in up or b"<?" in payload
assert not has_forbidden_directives(payload) Try / catch
try:
_validate_svg(payload)
except StarHistoryError as exc:
if "forbidden XML directives" in str(exc):
raise ValueError("strip XML declaration/DOCTYPE/PI before validation") from exc
raise Prevention
- Serialize with ET.tostring(root, encoding="unicode") — no declaration is emitted.
- Do not pretty-print or re-save the SVG with xmllint or editors that prepend <?xml ...?>.
- If a consumer needs an XML declaration, add it only after validation, outside the canonical artifact.
When it happens
Trigger: Any occurrence of the substrings `<!DOCTYPE` or `<!ENTITY` (case-insensitive via `upper_payload`) or `<?` anywhere in the decoded payload. Typical producers: XML serializers configured to emit an XML declaration, templates that start with `<?xml version="1.0"?>`, or a hand-edited SVG that gained a DOCTYPE.
Common situations: Regenerating the SVG with `xml.etree.ElementTree.tostring(xml_declaration=True)`; editors that auto-insert DOCTYPE declarations; forks adding an `<?xml-stylesheet?>` processing instruction; CI steps that pretty-print the SVG with xmllint (adds `<?xml?>`).
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- generated SVG contains a foreign namespace
- generated SVG is not valid XML
- generated SVG root is invalid
- generated SVG contains a forbidden element
- generated SVG contains an unreviewed image
AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14).
Data as JSON: /api/errors/a4955e408d5a78b6.
Report an issue: GitHub.