{"record":{"id":"a4955e408d5a78b6","repo":"666ghj/MiroFish","slug":"generated-svg-contains-forbidden-xml-directives","errorCode":null,"errorMessage":"generated SVG contains forbidden XML directives","messagePattern":"generated SVG contains forbidden XML directives","errorType":"exception","errorClass":"StarHistoryError","httpStatus":null,"severity":"error","filePath":"scripts/star_history.py","lineNumber":1083,"sourceCode":"                )\n        return None\n    return None\n\n\ndef _validate_svg(payload: bytes) -> None:\n    try:\n        decoded_payload = payload.decode(\"utf-8\", errors=\"strict\")\n    except UnicodeDecodeError as exc:\n        raise StarHistoryError(\"generated SVG must be strict UTF-8\") from exc\n    if decoded_payload.startswith(\"\\ufeff\") or \"\\x00\" in decoded_payload:\n        raise StarHistoryError(\"generated SVG must be canonical UTF-8\")\n    upper_payload = decoded_payload.upper()\n    if (\n        \"<!DOCTYPE\" in upper_payload\n        or \"<!ENTITY\" in upper_payload\n        or \"<?\" in decoded_payload\n    ):\n        raise StarHistoryError(\"generated SVG contains forbidden XML directives\")\n    try:\n        root = ET.fromstring(decoded_payload)\n    except ET.ParseError as exc:\n        raise StarHistoryError(\"generated SVG is not valid XML\") from exc\n    svg_namespace = \"{http://www.w3.org/2000/svg}\"\n    if root.tag != f\"{svg_namespace}svg\":\n        raise StarHistoryError(\"generated SVG root is invalid\")\n    allowed_attributes: dict[str, set[str]] = {\n        \"svg\": {\n            \"viewBox\",\n            \"width\",\n            \"height\",\n            \"preserveAspectRatio\",\n            \"role\",\n            \"aria-labelledby\",\n        },\n        \"title\": {\"id\"},\n        \"desc\": {\"id\"},","sourceCodeStart":1065,"sourceCodeEnd":1101,"githubUrl":"https://github.com/666ghj/MiroFish/blob/b5b53acc57189a4a42e44a23e149dc655c98fe82/scripts/star_history.py#L1065-L1101","documentation":"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.","triggerScenarios":"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.","commonSituations":"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?>`).","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."],"exampleFix":"# before: serializer emits a declaration -> validator sees '<?'\nsvg_text = ET.tostring(root, encoding=\"utf-8\", xml_declaration=True)\n\n# after: no declaration; canonical payload\nsvg_text = ET.tostring(root, encoding=\"unicode\").encode(\"utf-8\")","handlingStrategy":"try-catch","validationCode":"def has_forbidden_directives(payload: bytes) -> bool:\n    up = payload.upper()\n    return b\"<!DOCTYPE\" in up or b\"<!ENTITY\" in up or b\"<?\" in payload\n\nassert not has_forbidden_directives(payload)","typeGuard":null,"tryCatchPattern":"try:\n    _validate_svg(payload)\nexcept StarHistoryError as exc:\n    if \"forbidden XML directives\" in str(exc):\n        raise ValueError(\"strip XML declaration/DOCTYPE/PI before validation\") from exc\n    raise","preventionTips":["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."],"tags":["svg","xml","security","doctype","self-check","xxe"],"backgroundTag":null,"analyzedSha":"b5b53acc57189a4a42e44a23e149dc655c98fe82","analyzedAt":"2026-08-14T22:29:33.146Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}