This is an automated email from the ASF dual-hosted git repository. ppkarwasz pushed a commit to branch fix/saxparser-reset in repository https://gitbox.apache.org/repos/asf/commons-xml.git
commit e6cff05fc12cb337ef824d270366583fc43e82cf Author: Piotr P. Karwasz <[email protected]> AuthorDate: Tue Aug 18 09:09:08 2026 +0200 fix: Restore hardened state after Validator.reset() The JAXP reset contract reverts a validator to its just-created state, which removes the resource-resolver floor installed by the constructor. HardeningValidator.reset() now re-establishes the bare floor, matching HardeningDocumentBuilder.reset(). Assisted-By: Claude Fable 5 <[email protected]> --- .../java/org/apache/commons/xml/HardeningValidator.java | 4 +++- .../java/org/apache/commons/xml/ResetHardeningTest.java | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/apache/commons/xml/HardeningValidator.java b/src/main/java/org/apache/commons/xml/HardeningValidator.java index cb30279..e67f1e0 100644 --- a/src/main/java/org/apache/commons/xml/HardeningValidator.java +++ b/src/main/java/org/apache/commons/xml/HardeningValidator.java @@ -33,7 +33,7 @@ /** * {@link Validator} wrapper that rewrites the Source on every {@link Validator#validate(Source)} and {@link Validator#validate(Source, Result)} call through * {@link XmlFactories#harden(Source)} before delegating, and keeps an ignore-all {@link LSResourceResolver} floor so {@code xsi:schemaLocation} is not resolved at - * validation time. + * validation time. {@link #reset()} re-establishes the bare ignore-all floor, matching the just-constructed state. */ final class HardeningValidator extends Validator { @@ -71,6 +71,8 @@ public LSResourceResolver getResourceResolver() { @Override public void reset() { delegate.reset(); + floor.setDelegate(null); + delegate.setResourceResolver(floor); } @Override diff --git a/src/test/java/org/apache/commons/xml/ResetHardeningTest.java b/src/test/java/org/apache/commons/xml/ResetHardeningTest.java index b48fa6c..a7221bd 100644 --- a/src/test/java/org/apache/commons/xml/ResetHardeningTest.java +++ b/src/test/java/org/apache/commons/xml/ResetHardeningTest.java @@ -19,7 +19,9 @@ import static org.junit.jupiter.api.Assertions.assertFalse; +import javax.xml.XMLConstants; import javax.xml.parsers.SAXParser; +import javax.xml.validation.Validator; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -61,4 +63,18 @@ void saxParserResetKeepsEntityResolverFloor() throws Exception { } assertFalse(text.contains(AttackTestSupport.LEAKED_MARKER), "external entity leaked after reset:\n" + text); } + + @Test + @Tag("schema") + void validatorResetKeepsResourceResolverFloor() throws Exception { + // A Schema built without sources validates against the instance's xsi:schemaLocation hints, so the resolver floor is the only barrier between the + // validator and the external schema fetch. + final Validator validator = XmlFactories.newSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema().newValidator(); + AttackTestSupport.assumeDoesNotThrow(validator::reset); + validator.setErrorHandler(AttackTestSupport.STRICT_REPORTER); + // schema-location-instance.xml hints at schema-location.xsd, which declares its root: a validator whose floor was stripped fetches it and validates + // cleanly, while the floor resolves the hint to empty content, which fails the validation. + AttackTestSupport.assertParseFails(() -> validator.validate(AttackTestSupport.resourceSource("schema-location-instance.xml")), + "Validator after reset", SAXException.class, SecurityException.class); + } }
