This is an automated email from the ASF dual-hosted git repository. robertlazarski pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/axis-axis2-java-core.git
commit 0afbb74417b065412e11c99432b7d5a0fc02817c Author: Robert Lazarski <[email protected]> AuthorDate: Mon May 18 07:46:39 2026 -1000 Fix SSRF bypass via relative path + remote base URI, harden WSDL locators Gemini review found that the SSRF check on raw schemaLocation/importLocation can be bypassed when a relative path resolves to a remote URL via the base URI (e.g., relative "evil.xsd" + base "http://attacker/wsdl/" = remote fetch). Fixes: 1. WSDLToAxisServiceBuilder restrictive resolver: resolve loc against base before checking scheme. 2. WarFileBasedURIResolver: resolve schemaLocation against baseUri before the absolute/remote check (AARFileBasedURIResolver already resolves first). 3. AARBasedWSDLLocator: block remote URLs in getImportInputSource() for <wsdl:import> targets (was only patched for <xsd:import>). 4. WarBasedWSDLLocator: same fix, check resolved URI not raw importLocation. 5. URIResolverTest: add relative-path-with-remote-base bypass test. 404 kernel tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> --- .../deployment/resolver/AARBasedWSDLLocator.java | 13 +++++++-- .../deployment/resolver/WarBasedWSDLLocator.java | 14 +++++++-- .../resolver/WarFileBasedURIResolver.java | 33 +++++++++++++--------- .../description/WSDLToAxisServiceBuilder.java | 21 ++++++++++---- .../apache/axis2/deployment/URIResolverTest.java | 16 +++++++++++ 5 files changed, 73 insertions(+), 24 deletions(-) diff --git a/modules/kernel/src/org/apache/axis2/deployment/resolver/AARBasedWSDLLocator.java b/modules/kernel/src/org/apache/axis2/deployment/resolver/AARBasedWSDLLocator.java index 7177075972..c3e4b367ad 100644 --- a/modules/kernel/src/org/apache/axis2/deployment/resolver/AARBasedWSDLLocator.java +++ b/modules/kernel/src/org/apache/axis2/deployment/resolver/AARBasedWSDLLocator.java @@ -70,8 +70,17 @@ public class AARBasedWSDLLocator extends DefaultURIResolver implements WSDLLocat */ public InputSource getImportInputSource(String parentLocation, String importLocation) { lastImportLocation = URI.create(parentLocation).resolve(importLocation); - - if (isAbsolute(lastImportLocation.toString())) { + String loc = lastImportLocation.toString(); + + if (isAbsolute(loc)) { + // Block remote URLs to prevent SSRF in WSDL imports + if (loc.regionMatches(true, 0, "http:", 0, 5) + || loc.regionMatches(true, 0, "https:", 0, 6) + || loc.regionMatches(true, 0, "ftp:", 0, 4) + || loc.regionMatches(true, 0, "jar:", 0, 4)) { + throw new RuntimeException( + "Remote WSDL import blocked: " + loc); + } return super.resolveEntity( null, importLocation, parentLocation); } else { diff --git a/modules/kernel/src/org/apache/axis2/deployment/resolver/WarBasedWSDLLocator.java b/modules/kernel/src/org/apache/axis2/deployment/resolver/WarBasedWSDLLocator.java index 4b8e5803bf..fcc860f6b4 100644 --- a/modules/kernel/src/org/apache/axis2/deployment/resolver/WarBasedWSDLLocator.java +++ b/modules/kernel/src/org/apache/axis2/deployment/resolver/WarBasedWSDLLocator.java @@ -57,13 +57,21 @@ public class WarBasedWSDLLocator extends DefaultURIResolver implements WSDLLocat */ public InputSource getImportInputSource(String parentLocation, String importLocation) { lastImportLocation = URI.create(parentLocation).resolve(importLocation); + String loc = lastImportLocation.toString(); - if (isAbsolute(importLocation)) { + if (isAbsolute(loc)) { + // Block remote URLs to prevent SSRF in WSDL imports + if (loc.regionMatches(true, 0, "http:", 0, 5) + || loc.regionMatches(true, 0, "https:", 0, 6) + || loc.regionMatches(true, 0, "ftp:", 0, 4) + || loc.regionMatches(true, 0, "jar:", 0, 4)) { + throw new RuntimeException( + "Remote WSDL import blocked: " + loc); + } return super.resolveEntity( null, importLocation, parentLocation); } else { - String searchingStr = lastImportLocation.toString(); - return new InputSource(classLoader.getResourceAsStream(searchingStr)); + return new InputSource(classLoader.getResourceAsStream(loc)); } } diff --git a/modules/kernel/src/org/apache/axis2/deployment/resolver/WarFileBasedURIResolver.java b/modules/kernel/src/org/apache/axis2/deployment/resolver/WarFileBasedURIResolver.java index d418b4b646..ec8fa2a79b 100644 --- a/modules/kernel/src/org/apache/axis2/deployment/resolver/WarFileBasedURIResolver.java +++ b/modules/kernel/src/org/apache/axis2/deployment/resolver/WarFileBasedURIResolver.java @@ -41,15 +41,25 @@ public class WarFileBasedURIResolver extends DefaultURIResolver { String targetNamespace, String schemaLocation, String baseUri) { - //no issue with - if (isAbsolute(schemaLocation)) { - // Block remote URLs to prevent SSRF. Only allow resolution - // of absolute URIs that are local file paths. - if (schemaLocation.regionMatches(true, 0, "http:", 0, 5) - || schemaLocation.regionMatches(true, 0, "https:", 0, 6) - || schemaLocation.regionMatches(true, 0, "ftp:", 0, 4) - || schemaLocation.regionMatches(true, 0, "jar:", 0, 4)) { - log.warn("Blocked remote schema resolution in WAR deployment: " + schemaLocation); + // Resolve against base URI first to catch relative + remote base bypass + URI resolvedURI; + try { + resolvedURI = (baseUri != null) + ? URI.create(baseUri).resolve(schemaLocation) + : URI.create(schemaLocation); + } catch (IllegalArgumentException e) { + log.warn("Invalid URI syntax for schema location: " + schemaLocation); + return new InputSource(new java.io.ByteArrayInputStream(new byte[0])); + } + String resolved = resolvedURI.toString(); + + if (isAbsolute(resolved)) { + // Block remote URLs to prevent SSRF + if (resolved.regionMatches(true, 0, "http:", 0, 5) + || resolved.regionMatches(true, 0, "https:", 0, 6) + || resolved.regionMatches(true, 0, "ftp:", 0, 4) + || resolved.regionMatches(true, 0, "jar:", 0, 4)) { + log.warn("Blocked remote schema resolution in WAR deployment: " + resolved); return new InputSource(new java.io.ByteArrayInputStream(new byte[0])); } return super.resolveEntity( @@ -60,10 +70,7 @@ public class WarFileBasedURIResolver extends DefaultURIResolver { throw new RuntimeException( "Unsupported schema location " + schemaLocation); } - - URI lastImportLocation = URI.create(baseUri).resolve(schemaLocation); - String searchingStr = lastImportLocation.toString(); - return new InputSource(classLoader.getResourceAsStream(searchingStr)); + return new InputSource(classLoader.getResourceAsStream(resolved)); } } } diff --git a/modules/kernel/src/org/apache/axis2/description/WSDLToAxisServiceBuilder.java b/modules/kernel/src/org/apache/axis2/description/WSDLToAxisServiceBuilder.java index 5fdfe4f697..b271b069ce 100644 --- a/modules/kernel/src/org/apache/axis2/description/WSDLToAxisServiceBuilder.java +++ b/modules/kernel/src/org/apache/axis2/description/WSDLToAxisServiceBuilder.java @@ -154,13 +154,22 @@ public abstract class WSDLToAxisServiceBuilder { delegate = new org.apache.ws.commons.schema.resolver.DefaultURIResolver(); public org.xml.sax.InputSource resolveEntity( String ns, String loc, String base) { - if (loc != null - && (loc.regionMatches(true, 0, "http:", 0, 5) - || loc.regionMatches(true, 0, "https:", 0, 6) - || loc.regionMatches(true, 0, "ftp:", 0, 4) - || loc.regionMatches(true, 0, "jar:", 0, 4))) { + // Resolve against base URI before checking — + // a relative loc with a remote base must be caught + String resolved = loc; + if (base != null && loc != null) { + try { + resolved = java.net.URI.create(base).resolve(loc).toString(); + } catch (IllegalArgumentException ignored) { + } + } + if (resolved != null + && (resolved.regionMatches(true, 0, "http:", 0, 5) + || resolved.regionMatches(true, 0, "https:", 0, 6) + || resolved.regionMatches(true, 0, "ftp:", 0, 4) + || resolved.regionMatches(true, 0, "jar:", 0, 4))) { throw new RuntimeException( - "Remote schemaLocation blocked: " + loc + "Remote schemaLocation blocked: " + resolved + " (use setCustomResolver to opt in)"); } return delegate.resolveEntity(ns, loc, base); diff --git a/modules/kernel/test/org/apache/axis2/deployment/URIResolverTest.java b/modules/kernel/test/org/apache/axis2/deployment/URIResolverTest.java index 92ef18d604..e74693dc2c 100644 --- a/modules/kernel/test/org/apache/axis2/deployment/URIResolverTest.java +++ b/modules/kernel/test/org/apache/axis2/deployment/URIResolverTest.java @@ -62,4 +62,20 @@ public class URIResolverTest extends TestCase { assertNull("AAR resolver must block remote https URLs (SSRF)", inputSource.getSystemId()); } + + /** + * Verify that a relative schemaLocation with a remote baseUri is + * blocked — prevents the bypass where a relative path resolves to + * a remote URL via the base URI. + */ + public void testRelativePathWithRemoteBaseBlocked() { + WarFileBasedURIResolver war = new WarFileBasedURIResolver(null); + InputSource inputSource = war.resolveEntity(null, + "evil.xsd", + "http://attacker.example.com/wsdl/"); + assertNotNull(inputSource); + // Resolved URI is http://attacker.example.com/wsdl/evil.xsd — must be blocked + assertNull("WAR resolver must block relative path resolving to remote URL", + inputSource.getSystemId()); + } }
