This is an automated email from the ASF dual-hosted git repository.

coheigea pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ws-xmlschema.git


The following commit(s) were added to refs/heads/master by this push:
     new 5c9ad53c More DefaultURIResolver hardening
5c9ad53c is described below

commit 5c9ad53c9b2f9bc1758502ae66c1fa1023d3cd8d
Author: Colm O hEigeartaigh <[email protected]>
AuthorDate: Wed Sep 16 13:57:46 2026 +0100

    More DefaultURIResolver hardening
---
 .../schema/resolver/DefaultURIResolver.java        | 10 ++++-
 .../test/java/tests/DefaultURIResolverTest.java    | 47 ++++++++++++++++++++++
 2 files changed, 56 insertions(+), 1 deletion(-)

diff --git 
a/xmlschema-core/src/main/java/org/apache/ws/commons/schema/resolver/DefaultURIResolver.java
 
b/xmlschema-core/src/main/java/org/apache/ws/commons/schema/resolver/DefaultURIResolver.java
index 347b2653..5a645cf9 100644
--- 
a/xmlschema-core/src/main/java/org/apache/ws/commons/schema/resolver/DefaultURIResolver.java
+++ 
b/xmlschema-core/src/main/java/org/apache/ws/commons/schema/resolver/DefaultURIResolver.java
@@ -108,7 +108,10 @@ public class DefaultURIResolver implements 
CollectionURIResolver {
             }
 
         }
-        if (isAbsoluteUri(schemaLocation)) {
+        // A location carrying a scheme is checked even when java.net.URI will 
not parse it.
+        // URI is stricter than the java.net.URL the parser goes on to build, 
so a location URI
+        // rejects is not thereby harmless.
+        if (isAbsoluteUri(schemaLocation) || extractScheme(schemaLocation) != 
null) {
             verifyPermittedLocation(schemaLocation, schemaLocation);
             return new InputSource(schemaLocation);
         }
@@ -165,6 +168,11 @@ public class DefaultURIResolver implements 
CollectionURIResolver {
         }
         final String trimmed = uri.trim();
         final boolean wrapped = "jar".equals(extractScheme(trimmed));
+        if (wrapped && extractScheme(trimmed.substring(4)) == null) {
+            throw new XmlSchemaException("The schema location \"" + 
schemaLocation
+                                         + "\" is a jar: URL whose archive 
names no scheme this"
+                                         + " resolver recognises.");
+        }
         // A jar: URL delegates to the URL of the archive; the entry after 
"!/" is inside it.
         final String archive = wrapped ? stripJarEntry(trimmed.substring(4)) : 
trimmed;
         if (wrapped && isNetworkScheme(scheme)) {
diff --git a/xmlschema-core/src/test/java/tests/DefaultURIResolverTest.java 
b/xmlschema-core/src/test/java/tests/DefaultURIResolverTest.java
index 0144066e..4e394092 100644
--- a/xmlschema-core/src/test/java/tests/DefaultURIResolverTest.java
+++ b/xmlschema-core/src/test/java/tests/DefaultURIResolverTest.java
@@ -247,4 +247,51 @@ public class DefaultURIResolverTest extends Assert {
         // The same shape reached by composing a relative location against a 
local base.
         assertSchemeRefused("////attacker.example/share/x.xsd", localBase(), 
"non-local authority");
     }
+
+    @Test
+    public void testSchemeCheckSurvivesALocationUriCannotParse() {
+        // java.net.URI rejects a space, so isAbsoluteUri() said "not 
absolute" and the location
+        // fell through to the relative-path branch, which returned it 
unchecked. java.net.URL is
+        // laxer, so the parser still fetched it: a space was enough to defeat 
the scheme allowlist.
+        assertSchemeRefused("ftp://attacker.example/a b.xsd", null);
+        assertSchemeRefused("mailto:[email protected]?x= y", null);
+        assertSchemeRefused("jrt:/java.base/a b", null);
+    }
+
+    @Test
+    public void testUnparseableFileLocationIsRefused() {
+        DefaultURIResolver resolver = new DefaultURIResolver();
+        try {
+            resolver.resolveEntity("urn:x", 
"file:///\\\\attacker.example\\share\\x.xsd", null);
+            fail("A file: location that will not parse as a URI must be 
refused.");
+        } catch (XmlSchemaException expected) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testJarWrappingAnUnrecognisedSchemeIsRefused() {
+        assertSchemeRefused("jar:fi%6Ce://attacker.example/a.jar!/x.xsd", 
null, "names no scheme");
+        // Against a base the URL parser rejects the unknown nested protocol 
before this check,
+        // so only require that it is refused.
+        DefaultURIResolver resolver = new DefaultURIResolver();
+        try {
+            resolver.resolveEntity("urn:x", 
"jar:fi%6Ce://attacker.example/a.jar!/x.xsd",
+                                   localBase());
+            fail("A jar: URL wrapping an unrecognised scheme must be 
refused.");
+        } catch (XmlSchemaException expected) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testRelativeLocationsAreStillNotSchemeChecked() {
+        DefaultURIResolver resolver = new DefaultURIResolver();
+
+        // No scheme: extractScheme() returns null for a colon that follows a 
path separator, so
+        // these stay plain relative paths.
+        assertEquals("sub/x.xsd", resolver.resolveEntity("urn:x", "sub/x.xsd", 
null).getSystemId());
+        assertEquals("dir/a:b.xsd",
+                     resolver.resolveEntity("urn:x", "dir/a:b.xsd", 
null).getSystemId());
+    }
 }

Reply via email to