Spaces:
Sleeping
Sleeping
Claude
refactor(core): extraire safe_parse_xml en cercle 1 + appliquer aux 3 sites XXE résiduels
180bb96 unverified | """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. La règle est appliquée par un audit récurrent | |
| (grep ``ET.fromstring`` dans ``picarones/``). | |
| """ | |
| 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 les entités externes. | |
| 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) | |
| except (ET.ParseError, defusedxml.DefusedXmlException): | |
| return None | |
| __all__ = ["safe_parse_xml"] | |