Skip to content

Commit ea7dace

Browse files
mxaminclaude
andcommitted
Skip the external-DTD tests where nothing may load one (#356)
The sdist job fails on `test_sign_and_verify_with_an_id_an_external_dtd_ declares`, added with the external-subset fix in 3a50b04 and never yet through CI. Ubuntu 22.04 ships libxmlsec1 1.2.33 with the XXE patch backported, and that xmlsec installs its no-XXE external entity loader globally at xmlSecInit — so importing xmlsec refuses lxml its own `load_dtd=True` parse, well before any shadow exists. libxml2 is matched in that job (lxml is built with --no-binary), so the raw path is what runs: no declaration is made, `#ext` resolves to nothing and the sign fails. The test's premise, not its subject, is what the environment removes. Both tests that need a loaded subset now go through `parse_with_external_dtd`, which skips when `docinfo.externalDTD` comes back None — the same signal `PyXmlSec_LxmlDocumentSubsets` reads to decide whether the copy should load one. Verified by hiding tests/data/id_attr.dtd: 4 skips, no failures. 318 passed / 6 skipped on the mismatch build; 330 / 6 on the matched static wheel, plain and with PYXMLSEC_FORCE_SHADOW=1. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 48e8d0d commit ea7dace

1 file changed

Lines changed: 16 additions & 3 deletions

File tree

tests/test_ds.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,7 @@ def test_register_id_rejects_a_value_a_sibling_attribute_declares(self):
108108
def test_register_id_accepts_the_declared_attribute_beside_a_twin(self):
109109
"""A second attribute repeating the value is not the declared one, so registering the declared one is the no-op."""
110110
xml = b'<!DOCTYPE Root SYSTEM "id_attr.dtd">\n<Root><Node ID="dup" other="dup"/></Root>\n'
111-
root = etree.fromstring(xml, etree.XMLParser(load_dtd=True), base_url=self.path('doc.xml'))
112-
xmlsec.SignatureContext().register_id(root[0], 'ID')
111+
xmlsec.SignatureContext().register_id(self.parse_with_external_dtd(xml)[0], 'ID')
113112

114113
def test_register_id_rejects_a_value_a_namespaced_registration_claimed(self):
115114
"""Two attributes of one element differing only in namespace are two attributes: the second cannot win the lookup."""
@@ -288,9 +287,23 @@ def test_sign_and_verify_with_registered_id(self):
288287
# loads it knows "#ext" resolves to the Node (issue #356).
289288
EXTERNAL_DTD_XML = b'<!DOCTYPE Root SYSTEM "id_attr.dtd">\n<Root><Node ID="ext"><Data>signed</Data></Node></Root>\n'
290289

290+
def parse_with_external_dtd(self, xml):
291+
"""Parse `xml` with its external subset loaded, or skip the test when this build forbids that.
292+
293+
xmlsec installs a no-XXE external entity loader globally at
294+
``xmlSecInit`` (1.2.34 and later, and the patched 1.2.33 some
295+
distributions ship), so merely importing xmlsec can refuse lxml its
296+
own ``load_dtd=True`` parse. Nothing then types the id, on either
297+
path, and there is no declaration left for the copy to carry across.
298+
"""
299+
root = etree.fromstring(xml, etree.XMLParser(load_dtd=True), base_url=self.path('doc.xml'))
300+
if root.getroottree().docinfo.externalDTD is None:
301+
self.skipTest('this build refuses to load an external DTD subset')
302+
return root
303+
291304
def test_sign_and_verify_with_an_id_an_external_dtd_declares(self):
292305
"""Should resolve a #id reference whose id attribute an external subset the caller loaded declares."""
293-
root = etree.fromstring(self.EXTERNAL_DTD_XML, etree.XMLParser(load_dtd=True), base_url=self.path('doc.xml'))
306+
root = self.parse_with_external_dtd(self.EXTERNAL_DTD_XML)
294307
sign = xmlsec.template.create(root, consts.TransformExclC14N, consts.TransformRsaSha1, ns='ds')
295308
root.append(sign)
296309
ref = xmlsec.template.add_reference(sign, consts.TransformSha1, uri='#ext')

0 commit comments

Comments
 (0)