"""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 = ( '\n' "\n' ' \n' ' \n' ' \n' "]>\n" "&a3;" ) 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 &/< entities, still parses.""" (tmp_path / "ok.xml").write_text( 'cat & dog' ) reader = XMLCorpusReader(str(tmp_path), ["ok.xml"]) elt = reader.xml("ok.xml") assert elt.tag == "doc" assert reader.words("ok.xml") == ["cat", "dog"]