Files
nltk--nltk/nltk/test/unit/test_xml_entity_expansion_security.py
wehub-resource-sync 3454a55636
cffconvert / validate (push) Has been cancelled
ci-workflow / pre-commit (push) Has been cancelled
ci-workflow / Minimal NLTK Download Test (macos-latest) (push) Has been cancelled
ci-workflow / Minimal NLTK Download Test (ubuntu-latest) (push) Has been cancelled
ci-workflow / Minimal NLTK Download Test (windows-latest) (push) Has been cancelled
ci-workflow / Python 3.10 on macos-latest (push) Has been cancelled
ci-workflow / Python 3.11 on macos-latest (push) Has been cancelled
ci-workflow / Python 3.12 on macos-latest (push) Has been cancelled
ci-workflow / Python 3.13 on macos-latest (push) Has been cancelled
ci-workflow / Python 3.14 on macos-latest (push) Has been cancelled
ci-workflow / Python 3.14t on macos-latest (push) Has been cancelled
ci-workflow / Python 3.10 on ubuntu-latest (push) Has been cancelled
ci-workflow / Python 3.11 on ubuntu-latest (push) Has been cancelled
ci-workflow / Python 3.12 on ubuntu-latest (push) Has been cancelled
ci-workflow / Python 3.13 on ubuntu-latest (push) Has been cancelled
ci-workflow / Python 3.14 on ubuntu-latest (push) Has been cancelled
ci-workflow / Python 3.14t on ubuntu-latest (push) Has been cancelled
ci-workflow / Python 3.10 on windows-latest (push) Has been cancelled
ci-workflow / Python 3.11 on windows-latest (push) Has been cancelled
ci-workflow / Python 3.12 on windows-latest (push) Has been cancelled
ci-workflow / Python 3.13 on windows-latest (push) Has been cancelled
ci-workflow / Python 3.14 on windows-latest (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:46:15 +08:00

47 lines
1.8 KiB
Python

"""Regression tests for XML entity-expansion (Billion Laughs) in corpus readers (CWE-776).
NLTK's XML corpus readers parsed untrusted corpus XML with the stdlib
``xml.etree.ElementTree``, which performs entity expansion and is vulnerable to a
Billion-Laughs denial of service. They now parse via ``defusedxml`` (``safe_parse``
/ ``safe_fromstring``), which forbids the custom-entity definitions such an attack
relies on while leaving ordinary XML unaffected. This mirrors the fix applied to
the downloader's remote index in issue #3545 / PR #3544.
"""
import pytest
from defusedxml.common import EntitiesForbidden
from nltk.corpus.reader.xmldocs import XMLCorpusReader
# A Billion-Laughs payload: a tiny file whose nested entities would expand to a
# huge string with the stdlib parser.
_BOMB = (
'<?xml version="1.0"?>\n'
"<!DOCTYPE doc [\n"
' <!ENTITY a0 "AAAAAAAAAA">\n'
' <!ENTITY a1 "&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;">\n'
' <!ENTITY a2 "&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;">\n'
' <!ENTITY a3 "&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;">\n'
"]>\n"
"<doc>&a3;</doc>"
)
def test_xmlcorpusreader_blocks_entity_bomb(tmp_path):
"""A malicious corpus file with a nested-entity DTD must be refused."""
(tmp_path / "evil.xml").write_text(_BOMB)
reader = XMLCorpusReader(str(tmp_path), ["evil.xml"])
with pytest.raises(EntitiesForbidden):
reader.xml("evil.xml")
def test_xmlcorpusreader_parses_normal_xml(tmp_path):
"""Ordinary XML, including the standard &amp;/&lt; entities, still parses."""
(tmp_path / "ok.xml").write_text(
'<doc><w pos="NN">cat</w> &amp; <w pos="NN">dog</w></doc>'
)
reader = XMLCorpusReader(str(tmp_path), ["ok.xml"])
elt = reader.xml("ok.xml")
assert elt.tag == "doc"
assert reader.words("ok.xml") == ["cat", "dog"]