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 9a0cbdbc Fixing one more DefaultURIResolver case
9a0cbdbc is described below

commit 9a0cbdbc58bc55c4e3949cbadb27ca6d0e229181
Author: Colm O hEigeartaigh <[email protected]>
AuthorDate: Wed Sep 16 11:38:44 2026 +0100

    Fixing one more DefaultURIResolver case
---
 THREAT-MODEL.md                                            | 12 ++++++++----
 .../ws/commons/schema/resolver/DefaultURIResolver.java     | 14 ++++++++++++--
 .../src/test/java/tests/DefaultURIResolverTest.java        | 13 +++++++++++++
 3 files changed, 33 insertions(+), 6 deletions(-)

diff --git a/THREAT-MODEL.md b/THREAT-MODEL.md
index dffe0103..24ac6f76 100644
--- a/THREAT-MODEL.md
+++ b/THREAT-MODEL.md
@@ -452,9 +452,10 @@ matching disclaimer.
   `https`, `file` and `jar`, judged through any `jar:` wrapper — and
   refuses a location that changes the scheme of a remote base. It also
   refuses, for every location and whatever the base, a `file:` URL that
-  names a non-local authority (a UNC path on Windows, so an SMB
-  connection to a host the schema author chose) and a `jar:` URL whose
-  archive would be fetched over the network. Within the `http` and
+  names a non-local host — in its authority, or as a path beginning
+  `//`, which is a UNC path on Windows and so an SMB connection to a
+  host the schema author chose — and a `jar:` URL whose archive would be
+  fetched over the network. Within the `http` and
   `https` targets it does allow, it applies **no host or address
   filtering of any kind**: any
   `http(s)` host is fetched on request, including loopback, link-local
@@ -702,7 +703,10 @@ Revise this document when any of the following lands:
   `file://host/share/x.xsd` and `jar:http://host/a.jar!/x.xsd` resolved
   from a local or absent base. §4 B3 and §9 are updated. This does not
   change the Q12(b) posture: absolute `http(s)` and local `file:`
-  locations are still followed.
+  locations are still followed. A follow-up closed a gap in the `file:`
+  rule as first written: it tested only the URI authority, so
+  `file:////host/share/x.xsd`, which parses with no authority and
+  carries the host in its path instead, was not caught.
 - **2026-09-16** — "Fix up DTD handling" (#147) changed the default
   parser DTD posture, a revision trigger under the second bullet above:
   external DTD and external entity resolution are now disabled
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 1f7a45f8..347b2653 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
@@ -241,9 +241,19 @@ public class DefaultURIResolver implements 
CollectionURIResolver {
     private static boolean isLocalFileUri(String uri) {
         try {
             URI parsed = new URI(uri);
+            if (!"file".equalsIgnoreCase(parsed.getScheme())) {
+                return false;
+            }
             final String authority = parsed.getAuthority();
-            return "file".equalsIgnoreCase(parsed.getScheme())
-                && (authority == null || authority.length() == 0 || 
"localhost".equalsIgnoreCase(authority));
+            if (authority != null && authority.length() > 0
+                && !"localhost".equalsIgnoreCase(authority)) {
+                return false;
+            }
+            // A host can also arrive in the path rather than the authority: 
"file:////host/share"
+            // parses with no authority at all, and a path beginning "//" is a 
UNC path on Windows.
+            // No schema names a local file that way, so refuse the shape 
outright.
+            final String path = parsed.getPath();
+            return path == null || !path.startsWith("//");
         } catch (URISyntaxException e) {
             return false;
         }
diff --git a/xmlschema-core/src/test/java/tests/DefaultURIResolverTest.java 
b/xmlschema-core/src/test/java/tests/DefaultURIResolverTest.java
index 6cadfa04..0144066e 100644
--- a/xmlschema-core/src/test/java/tests/DefaultURIResolverTest.java
+++ b/xmlschema-core/src/test/java/tests/DefaultURIResolverTest.java
@@ -234,4 +234,17 @@ public class DefaultURIResolverTest extends Assert {
 
         assertEquals("jar:file:///tmp/a.jar!/has space.xsd", 
result.getSystemId());
     }
+
+    @Test
+    public void testFileUrlCarryingItsHostInThePathIsRefused() {
+        // "file:////host/share/x.xsd" parses with a null authority and the 
host in the path, so an
+        // authority-only check lets it through. On Windows that path is a UNC 
path.
+        assertSchemeRefused("file:////attacker.example/share/x.xsd", null, 
"non-local authority");
+        assertSchemeRefused("file:////attacker.example/share/x.xsd", 
localBase(), "non-local authority");
+        assertSchemeRefused("file://///attacker.example/share/x.xsd", null, 
"non-local authority");
+        
assertSchemeRefused("jar:file:////attacker.example/share/a.jar!/x.xsd", null,
+                            "non-local authority");
+        // The same shape reached by composing a relative location against a 
local base.
+        assertSchemeRefused("////attacker.example/share/x.xsd", localBase(), 
"non-local authority");
+    }
 }

Reply via email to