Skip to content

Commit 0afbb74

Browse files
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) <noreply@anthropic.com>
1 parent 7ac84d8 commit 0afbb74

5 files changed

Lines changed: 73 additions & 24 deletions

File tree

modules/kernel/src/org/apache/axis2/deployment/resolver/AARBasedWSDLLocator.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,17 @@ public InputSource getBaseInputSource() {
7070
*/
7171
public InputSource getImportInputSource(String parentLocation, String importLocation) {
7272
lastImportLocation = URI.create(parentLocation).resolve(importLocation);
73-
74-
if (isAbsolute(lastImportLocation.toString())) {
73+
String loc = lastImportLocation.toString();
74+
75+
if (isAbsolute(loc)) {
76+
// Block remote URLs to prevent SSRF in WSDL imports
77+
if (loc.regionMatches(true, 0, "http:", 0, 5)
78+
|| loc.regionMatches(true, 0, "https:", 0, 6)
79+
|| loc.regionMatches(true, 0, "ftp:", 0, 4)
80+
|| loc.regionMatches(true, 0, "jar:", 0, 4)) {
81+
throw new RuntimeException(
82+
"Remote WSDL import blocked: " + loc);
83+
}
7584
return super.resolveEntity(
7685
null, importLocation, parentLocation);
7786
} else {

modules/kernel/src/org/apache/axis2/deployment/resolver/WarBasedWSDLLocator.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,21 @@ public InputSource getBaseInputSource() {
5757
*/
5858
public InputSource getImportInputSource(String parentLocation, String importLocation) {
5959
lastImportLocation = URI.create(parentLocation).resolve(importLocation);
60+
String loc = lastImportLocation.toString();
6061

61-
if (isAbsolute(importLocation)) {
62+
if (isAbsolute(loc)) {
63+
// Block remote URLs to prevent SSRF in WSDL imports
64+
if (loc.regionMatches(true, 0, "http:", 0, 5)
65+
|| loc.regionMatches(true, 0, "https:", 0, 6)
66+
|| loc.regionMatches(true, 0, "ftp:", 0, 4)
67+
|| loc.regionMatches(true, 0, "jar:", 0, 4)) {
68+
throw new RuntimeException(
69+
"Remote WSDL import blocked: " + loc);
70+
}
6271
return super.resolveEntity(
6372
null, importLocation, parentLocation);
6473
} else {
65-
String searchingStr = lastImportLocation.toString();
66-
return new InputSource(classLoader.getResourceAsStream(searchingStr));
74+
return new InputSource(classLoader.getResourceAsStream(loc));
6775
}
6876
}
6977

modules/kernel/src/org/apache/axis2/deployment/resolver/WarFileBasedURIResolver.java

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,25 @@ public InputSource resolveEntity(
4141
String targetNamespace,
4242
String schemaLocation,
4343
String baseUri) {
44-
//no issue with
45-
if (isAbsolute(schemaLocation)) {
46-
// Block remote URLs to prevent SSRF. Only allow resolution
47-
// of absolute URIs that are local file paths.
48-
if (schemaLocation.regionMatches(true, 0, "http:", 0, 5)
49-
|| schemaLocation.regionMatches(true, 0, "https:", 0, 6)
50-
|| schemaLocation.regionMatches(true, 0, "ftp:", 0, 4)
51-
|| schemaLocation.regionMatches(true, 0, "jar:", 0, 4)) {
52-
log.warn("Blocked remote schema resolution in WAR deployment: " + schemaLocation);
44+
// Resolve against base URI first to catch relative + remote base bypass
45+
URI resolvedURI;
46+
try {
47+
resolvedURI = (baseUri != null)
48+
? URI.create(baseUri).resolve(schemaLocation)
49+
: URI.create(schemaLocation);
50+
} catch (IllegalArgumentException e) {
51+
log.warn("Invalid URI syntax for schema location: " + schemaLocation);
52+
return new InputSource(new java.io.ByteArrayInputStream(new byte[0]));
53+
}
54+
String resolved = resolvedURI.toString();
55+
56+
if (isAbsolute(resolved)) {
57+
// Block remote URLs to prevent SSRF
58+
if (resolved.regionMatches(true, 0, "http:", 0, 5)
59+
|| resolved.regionMatches(true, 0, "https:", 0, 6)
60+
|| resolved.regionMatches(true, 0, "ftp:", 0, 4)
61+
|| resolved.regionMatches(true, 0, "jar:", 0, 4)) {
62+
log.warn("Blocked remote schema resolution in WAR deployment: " + resolved);
5363
return new InputSource(new java.io.ByteArrayInputStream(new byte[0]));
5464
}
5565
return super.resolveEntity(
@@ -60,10 +70,7 @@ public InputSource resolveEntity(
6070
throw new RuntimeException(
6171
"Unsupported schema location " + schemaLocation);
6272
}
63-
64-
URI lastImportLocation = URI.create(baseUri).resolve(schemaLocation);
65-
String searchingStr = lastImportLocation.toString();
66-
return new InputSource(classLoader.getResourceAsStream(searchingStr));
73+
return new InputSource(classLoader.getResourceAsStream(resolved));
6774
}
6875
}
6976
}

modules/kernel/src/org/apache/axis2/description/WSDLToAxisServiceBuilder.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -154,13 +154,22 @@ protected XmlSchema getXMLSchema(Element element, String baseUri) {
154154
delegate = new org.apache.ws.commons.schema.resolver.DefaultURIResolver();
155155
public org.xml.sax.InputSource resolveEntity(
156156
String ns, String loc, String base) {
157-
if (loc != null
158-
&& (loc.regionMatches(true, 0, "http:", 0, 5)
159-
|| loc.regionMatches(true, 0, "https:", 0, 6)
160-
|| loc.regionMatches(true, 0, "ftp:", 0, 4)
161-
|| loc.regionMatches(true, 0, "jar:", 0, 4))) {
157+
// Resolve against base URI before checking —
158+
// a relative loc with a remote base must be caught
159+
String resolved = loc;
160+
if (base != null && loc != null) {
161+
try {
162+
resolved = java.net.URI.create(base).resolve(loc).toString();
163+
} catch (IllegalArgumentException ignored) {
164+
}
165+
}
166+
if (resolved != null
167+
&& (resolved.regionMatches(true, 0, "http:", 0, 5)
168+
|| resolved.regionMatches(true, 0, "https:", 0, 6)
169+
|| resolved.regionMatches(true, 0, "ftp:", 0, 4)
170+
|| resolved.regionMatches(true, 0, "jar:", 0, 4))) {
162171
throw new RuntimeException(
163-
"Remote schemaLocation blocked: " + loc
172+
"Remote schemaLocation blocked: " + resolved
164173
+ " (use setCustomResolver to opt in)");
165174
}
166175
return delegate.resolveEntity(ns, loc, base);

modules/kernel/test/org/apache/axis2/deployment/URIResolverTest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,20 @@ public void testHttpsUrlBlocked() {
6262
assertNull("AAR resolver must block remote https URLs (SSRF)",
6363
inputSource.getSystemId());
6464
}
65+
66+
/**
67+
* Verify that a relative schemaLocation with a remote baseUri is
68+
* blocked — prevents the bypass where a relative path resolves to
69+
* a remote URL via the base URI.
70+
*/
71+
public void testRelativePathWithRemoteBaseBlocked() {
72+
WarFileBasedURIResolver war = new WarFileBasedURIResolver(null);
73+
InputSource inputSource = war.resolveEntity(null,
74+
"evil.xsd",
75+
"http://attacker.example.com/wsdl/");
76+
assertNotNull(inputSource);
77+
// Resolved URI is http://attacker.example.com/wsdl/evil.xsd — must be blocked
78+
assertNull("WAR resolver must block relative path resolving to remote URL",
79+
inputSource.getSystemId());
80+
}
6581
}

0 commit comments

Comments
 (0)