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 7ac84d8339ad80f8f7f7977cf0caaa28fc3b7b98 Author: Robert Lazarski <[email protected]> AuthorDate: Mon May 18 07:35:24 2026 -1000 Harden schema import resolution against SSRF (CWE-918) Companion fix to the XXE hardening. xmlschema-core's DefaultURIResolver follows absolute schemaLocation URLs with no scheme or host restriction, enabling blind SSRF from the Axis2 JVM. Three vectors addressed: 1. WSDLToAxisServiceBuilder.getXMLSchema(): when no custom resolver is set, install a restrictive URIResolver that blocks http/https/ftp/jar/ file schemes on schemaLocation targets. Callers who need remote resolution can supply their own resolver via setCustomResolver(). 2. AARFileBasedURIResolver: block remote URLs when falling through to super.resolveEntity() for absolute URIs. Return empty InputSource instead of fetching. 3. WarFileBasedURIResolver: same fix as AAR resolver. Updated URIResolverTest to verify remote URLs are blocked (was previously asserting they were fetched — that was the SSRF). 403 kernel tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> --- .../resolver/AARFileBasedURIResolver.java | 10 ++++++++ .../resolver/WarFileBasedURIResolver.java | 9 +++++++ .../description/WSDLToAxisServiceBuilder.java | 25 ++++++++++++++++++ .../apache/axis2/deployment/URIResolverTest.java | 30 +++++++++++++++++++--- 4 files changed, 70 insertions(+), 4 deletions(-) diff --git a/modules/kernel/src/org/apache/axis2/deployment/resolver/AARFileBasedURIResolver.java b/modules/kernel/src/org/apache/axis2/deployment/resolver/AARFileBasedURIResolver.java index 661ea336cd..165ef7214e 100644 --- a/modules/kernel/src/org/apache/axis2/deployment/resolver/AARFileBasedURIResolver.java +++ b/modules/kernel/src/org/apache/axis2/deployment/resolver/AARFileBasedURIResolver.java @@ -58,6 +58,16 @@ public class AARFileBasedURIResolver extends DefaultURIResolver { lastImportLocation = URI.create(baseUri).resolve(schemaLocation); if (isAbsolute(lastImportLocation.toString())) { + // Block remote URLs to prevent SSRF. Only allow resolution + // of absolute URIs that are local file paths. + String loc = lastImportLocation.toString(); + 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)) { + log.warn("Blocked remote schema resolution in AAR deployment: " + loc); + return new InputSource(new java.io.ByteArrayInputStream(new byte[0])); + } return super.resolveEntity( targetNamespace, schemaLocation, baseUri); } else { 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 e0f6ff4cc6..d418b4b646 100644 --- a/modules/kernel/src/org/apache/axis2/deployment/resolver/WarFileBasedURIResolver.java +++ b/modules/kernel/src/org/apache/axis2/deployment/resolver/WarFileBasedURIResolver.java @@ -43,6 +43,15 @@ public class WarFileBasedURIResolver extends DefaultURIResolver { 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); + return new InputSource(new java.io.ByteArrayInputStream(new byte[0])); + } return super.resolveEntity( targetNamespace, schemaLocation, baseUri); } else { diff --git a/modules/kernel/src/org/apache/axis2/description/WSDLToAxisServiceBuilder.java b/modules/kernel/src/org/apache/axis2/description/WSDLToAxisServiceBuilder.java index 8cfb1fbbdf..5fdfe4f697 100644 --- a/modules/kernel/src/org/apache/axis2/description/WSDLToAxisServiceBuilder.java +++ b/modules/kernel/src/org/apache/axis2/description/WSDLToAxisServiceBuilder.java @@ -141,6 +141,31 @@ public abstract class WSDLToAxisServiceBuilder { if (customResolver != null) { schemaCollection.setSchemaResolver(customResolver); + } else { + // Install a restrictive resolver that blocks remote schema + // resolution (SSRF via absolute schemaLocation URLs). The + // default URIResolver in xmlschema-core follows any absolute + // URI including http://, https://, ftp://, and jar://. + // Local file:// and relative paths are allowed for co-packaged + // schemas in .aar/.war deployments. + schemaCollection.setSchemaResolver( + new org.apache.ws.commons.schema.resolver.URIResolver() { + private final org.apache.ws.commons.schema.resolver.DefaultURIResolver + 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))) { + throw new RuntimeException( + "Remote schemaLocation blocked: " + loc + + " (use setCustomResolver to opt in)"); + } + return delegate.resolveEntity(ns, loc, base); + } + }); } return schemaCollection.read(element); diff --git a/modules/kernel/test/org/apache/axis2/deployment/URIResolverTest.java b/modules/kernel/test/org/apache/axis2/deployment/URIResolverTest.java index e4d6d2fbc9..92ef18d604 100644 --- a/modules/kernel/test/org/apache/axis2/deployment/URIResolverTest.java +++ b/modules/kernel/test/org/apache/axis2/deployment/URIResolverTest.java @@ -27,17 +27,39 @@ import org.xml.sax.InputSource; public class URIResolverTest extends TestCase { - public void testResolveEntity() { + /** + * Verify that remote http/https URLs are blocked by the SSRF + * hardening in AAR and WAR resolvers. The resolvers should return + * an empty InputSource instead of fetching the remote URL. + */ + public void testRemoteUrlBlocked() { AARFileBasedURIResolver aar = new AARFileBasedURIResolver(null); - WarFileBasedURIResolver war = new WarFileBasedURIResolver(null); InputSource inputSource = aar.resolveEntity(null, "http://www.test.org/test.xsd", "http://www.test.org/schema.xsd"); assertNotNull(inputSource); - assertEquals(inputSource.getSystemId(), "http://www.test.org/test.xsd"); + // Should return empty InputSource, not one with the remote URL + assertNull("AAR resolver must block remote http URLs (SSRF)", + inputSource.getSystemId()); + + WarFileBasedURIResolver war = new WarFileBasedURIResolver(null); inputSource = war.resolveEntity(null, "http://www.test.org/test.xsd", "http://www.test.org/schema.xsd"); assertNotNull(inputSource); - assertEquals(inputSource.getSystemId(), "http://www.test.org/test.xsd"); + assertNull("WAR resolver must block remote http URLs (SSRF)", + inputSource.getSystemId()); + } + + /** + * Verify that https URLs are also blocked. + */ + public void testHttpsUrlBlocked() { + AARFileBasedURIResolver aar = new AARFileBasedURIResolver(null); + InputSource inputSource = aar.resolveEntity(null, + "https://www.test.org/test.xsd", + "https://www.test.org/schema.xsd"); + assertNotNull(inputSource); + assertNull("AAR resolver must block remote https URLs (SSRF)", + inputSource.getSystemId()); } }
