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);
+    }
 }

Reply via email to