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 1d325f4deb20f2ee5d2be97e3c618339009d259f
Author: Robert Lazarski <[email protected]>
AuthorDate: Wed Aug 5 08:30:30 2026 -1000

    Permit only https as a reply scheme unless told otherwise
    
    The previous default list -- http, https, jms, mailto, tcp -- was wider than
    any real deployment needs. Dropping to https alone matters most for the http
    exclusion, which is not merely tidying: the cloud instance-metadata services
    this class of SSRF is aimed at are HTTP-only. AWS and Azure on 
169.254.169.254
    and GCP on metadata.google.internal offer no HTTPS listener, so refusing the
    scheme removes that target without relying on the address checks at all.
    
    jms, mailto and tcp keep their senders but become opt-in; a deployment 
naming
    one of them, or genuinely replying over plain HTTP inside a trusted network,
    lists it in allowedResponseEndpointSchemes.
    
    Worth being clear about the size of the win: this list is only consulted 
once
    allowNonAnonymousResponseEndpoints is on, and that now defaults to false, so
    this is defence in depth inside an already opt-in path rather than a
    first-line control.
    
    Also renames the class to AddressingResponseEndpointPolicy. It is reached 
from
    two places, both WS-Addressing: the header parsing in AddressingInHandler, 
and
    MessageContextBuilder where a non-anonymous response endpoint acquires its
    transport. Now that it sits in the kernel and is called from generic code, 
the
    bare name under-specified what it governs; the new one matches the
    AddressingHelper / AddressingFaultsHelper / AddressingConstants family 
beside
    it in the same package.
    
    Co-Authored-By: Claude Fable 5 <[email protected]>
---
 .../handlers/addressing/AddressingInHandler.java   |   4 +-
 .../addressing/AddressingFinalInHandlerTest.java   |   3 +
 ...dressingResponseEndpointPolicyHandlerTest.java} |  11 +-
 modules/integration/pom.xml                        |  18 +++
 .../test-resources/mtom/MTOM-enabled-axis2.xml     |   1 +
 .../mtom/MTOM-fileCache-enabled-axis2.xml          |   1 +
 modules/kernel/conf/axis2.xml                      |  14 ++-
 ....java => AddressingResponseEndpointPolicy.java} |  36 ++++--
 .../org/apache/axis2/engine/AxisConfiguration.java |   2 +-
 .../apache/axis2/util/MessageContextBuilder.java   |   6 +-
 ...a => AddressingResponseEndpointPolicyTest.java} | 122 +++++++++++++--------
 modules/webapp/conf/axis2.xml                      |  14 ++-
 12 files changed, 155 insertions(+), 77 deletions(-)

diff --git 
a/modules/addressing/src/org/apache/axis2/handlers/addressing/AddressingInHandler.java
 
b/modules/addressing/src/org/apache/axis2/handlers/addressing/AddressingInHandler.java
index 8b64481476..c7266d8b51 100644
--- 
a/modules/addressing/src/org/apache/axis2/handlers/addressing/AddressingInHandler.java
+++ 
b/modules/addressing/src/org/apache/axis2/handlers/addressing/AddressingInHandler.java
@@ -32,7 +32,7 @@ import org.apache.axis2.addressing.AddressingFaultsHelper;
 import org.apache.axis2.addressing.EndpointReference;
 import org.apache.axis2.addressing.EndpointReferenceHelper;
 import org.apache.axis2.addressing.RelatesTo;
-import org.apache.axis2.addressing.ResponseEndpointPolicy;
+import org.apache.axis2.addressing.AddressingResponseEndpointPolicy;
 import org.apache.axis2.client.Options;
 import org.apache.axis2.context.MessageContext;
 import org.apache.axis2.description.HandlerDescription;
@@ -451,7 +451,7 @@ public class AddressingInHandler extends 
AbstractTemplatedHandler implements Add
         if (!messageContext.isServerSide()) {
             return;
         }
-        if (!ResponseEndpointPolicy.isAllowed(epr, messageContext)) {
+        if (!AddressingResponseEndpointPolicy.isAllowed(epr, messageContext)) {
             AddressingFaultsHelper
                     .triggerInvalidEPRFault(messageContext, 
soapHeaderBlock.getLocalName());
         }
diff --git 
a/modules/addressing/test/org/apache/axis2/handlers/addressing/AddressingFinalInHandlerTest.java
 
b/modules/addressing/test/org/apache/axis2/handlers/addressing/AddressingFinalInHandlerTest.java
index 0015569bc9..4ce651ad97 100644
--- 
a/modules/addressing/test/org/apache/axis2/handlers/addressing/AddressingFinalInHandlerTest.java
+++ 
b/modules/addressing/test/org/apache/axis2/handlers/addressing/AddressingFinalInHandlerTest.java
@@ -223,6 +223,9 @@ public class AddressingFinalInHandlerTest extends 
AddressingInHandlerTestBase {
         mc.getConfigurationContext().getAxisConfiguration().addParameter(
                 new org.apache.axis2.description.Parameter(
                         "allowNonAnonymousResponseEndpoints", "true"));
+        mc.getConfigurationContext().getAxisConfiguration().addParameter(
+                new org.apache.axis2.description.Parameter(
+                        "allowedResponseEndpointSchemes", "http,https"));
         try {
             mc.setSoapAction("http://ws.apache.org/tests/action";);
             basicExtractAddressingInformationFromHeaders(testfile, mc);
diff --git 
a/modules/addressing/test/org/apache/axis2/handlers/addressing/ResponseEndpointPolicyHandlerTest.java
 
b/modules/addressing/test/org/apache/axis2/handlers/addressing/AddressingResponseEndpointPolicyHandlerTest.java
similarity index 92%
rename from 
modules/addressing/test/org/apache/axis2/handlers/addressing/ResponseEndpointPolicyHandlerTest.java
rename to 
modules/addressing/test/org/apache/axis2/handlers/addressing/AddressingResponseEndpointPolicyHandlerTest.java
index 8a2cbe3810..02ceaa84e2 100644
--- 
a/modules/addressing/test/org/apache/axis2/handlers/addressing/ResponseEndpointPolicyHandlerTest.java
+++ 
b/modules/addressing/test/org/apache/axis2/handlers/addressing/AddressingResponseEndpointPolicyHandlerTest.java
@@ -29,7 +29,7 @@ import org.apache.axis2.addressing.AddressingConstants;
 import org.apache.axis2.context.ConfigurationContext;
 import org.apache.axis2.context.ConfigurationContextFactory;
 import org.apache.axis2.context.MessageContext;
-import org.apache.axis2.addressing.ResponseEndpointPolicy;
+import org.apache.axis2.addressing.AddressingResponseEndpointPolicy;
 import org.apache.axis2.description.Parameter;
 
 /**
@@ -43,7 +43,7 @@ import org.apache.axis2.description.Parameter;
  * from outside is a {@code wsa:ReplyTo} header on an inbound message, which is
  * what these tests send.
  */
-public class ResponseEndpointPolicyHandlerTest extends TestCase {
+public class AddressingResponseEndpointPolicyHandlerTest extends TestCase {
 
     private AddressingInHandler handler;
     private ConfigurationContext configurationContext;
@@ -55,7 +55,10 @@ public class ResponseEndpointPolicyHandlerTest extends 
TestCase {
         // Decoupled responses are off by default; opt in so these tests reach 
the
         // address checks. testDecoupledReplyToRefusedByDefault covers the 
default.
         configurationContext.getAxisConfiguration().addParameter(
-                new Parameter(ResponseEndpointPolicy.ALLOW_NON_ANONYMOUS, 
"true"));
+                new 
Parameter(AddressingResponseEndpointPolicy.ALLOW_NON_ANONYMOUS, "true"));
+        // Only https ships as permitted; these cases use http addresses.
+        configurationContext.getAxisConfiguration().addParameter(
+                new 
Parameter(AddressingResponseEndpointPolicy.ALLOWED_SCHEMES_PARAMETER, 
"http,https"));
     }
 
     /**
@@ -114,7 +117,7 @@ public class ResponseEndpointPolicyHandlerTest extends 
TestCase {
      */
     public void testPrivateReplyToRejectedUnderStrictPolicy() throws Exception 
{
         configurationContext.getAxisConfiguration().addParameter(
-                new Parameter(ResponseEndpointPolicy.BLOCK_PRIVATE_NETWORKS, 
"true"));
+                new 
Parameter(AddressingResponseEndpointPolicy.BLOCK_PRIVATE_NETWORKS, "true"));
         try {
             invokeWith("ReplyTo", "http://10.1.2.3/internal";);
             fail("A private ReplyTo should have faulted under the strict 
policy");
diff --git a/modules/integration/pom.xml b/modules/integration/pom.xml
index 2ad66dba7b..5647538334 100644
--- a/modules/integration/pom.xml
+++ b/modules/integration/pom.xml
@@ -267,6 +267,12 @@
                                         
<name>allowNonAnonymousResponseEndpoints</name>
                                         <value>true</value>
                                     </parameter>
+                                    <!-- Only https is permitted by default; 
these
+                                         callbacks are plain http on loopback. 
-->
+                                    <parameter>
+                                        
<name>allowedResponseEndpointSchemes</name>
+                                        <value>http,https</value>
+                                    </parameter>
                                 </parameters>
                                 <handlers>
                                     <handler>
@@ -305,6 +311,12 @@
                                         
<name>allowNonAnonymousResponseEndpoints</name>
                                         <value>true</value>
                                     </parameter>
+                                    <!-- Only https is permitted by default; 
these
+                                         callbacks are plain http on loopback. 
-->
+                                    <parameter>
+                                        
<name>allowedResponseEndpointSchemes</name>
+                                        <value>http,https</value>
+                                    </parameter>
                                 </parameters>
                             </generatedAxis2xml>
                             <modules>addressing</modules>
@@ -370,6 +382,12 @@
                                         
<name>allowNonAnonymousResponseEndpoints</name>
                                         <value>true</value>
                                     </parameter>
+                                    <!-- Only https is permitted by default; 
these
+                                         callbacks are plain http on loopback. 
-->
+                                    <parameter>
+                                        
<name>allowedResponseEndpointSchemes</name>
+                                        <value>http,https</value>
+                                    </parameter>
                                 </parameters>
                             </generatedAxis2xml>
                             <modules>addressing</modules>
diff --git a/modules/integration/test-resources/mtom/MTOM-enabled-axis2.xml 
b/modules/integration/test-resources/mtom/MTOM-enabled-axis2.xml
index 2c55ca73d5..d97cf59ff3 100644
--- a/modules/integration/test-resources/mtom/MTOM-enabled-axis2.xml
+++ b/modules/integration/test-resources/mtom/MTOM-enabled-axis2.xml
@@ -21,6 +21,7 @@
     <!-- The separate-listener MTOM tests use a decoupled wsa:ReplyTo, which is
          off by default; opt in so they exercise that path. -->
     <parameter name="allowNonAnonymousResponseEndpoints">true</parameter>
+    <parameter name="allowedResponseEndpointSchemes">http,https</parameter>
 
     <!-- ================================================= -->
     <!-- Parameters -->
diff --git 
a/modules/integration/test-resources/mtom/MTOM-fileCache-enabled-axis2.xml 
b/modules/integration/test-resources/mtom/MTOM-fileCache-enabled-axis2.xml
index 0a86f27802..e3fbf0dec6 100644
--- a/modules/integration/test-resources/mtom/MTOM-fileCache-enabled-axis2.xml
+++ b/modules/integration/test-resources/mtom/MTOM-fileCache-enabled-axis2.xml
@@ -21,6 +21,7 @@
     <!-- The separate-listener MTOM tests use a decoupled wsa:ReplyTo, which is
          off by default; opt in so they exercise that path. -->
     <parameter name="allowNonAnonymousResponseEndpoints">true</parameter>
+    <parameter name="allowedResponseEndpointSchemes">http,https</parameter>
 
     <!-- ================================================= -->
     <!-- Parameters -->
diff --git a/modules/kernel/conf/axis2.xml b/modules/kernel/conf/axis2.xml
index 8bc34b9091..68614835b0 100644
--- a/modules/kernel/conf/axis2.xml
+++ b/modules/kernel/conf/axis2.xml
@@ -92,15 +92,19 @@
     used by the checks above, so a slow resolver cannot hold a request thread.
     Literal addresses are never looked up.
 
-    allowedResponseEndpointSchemes defaults to http,https,jms,mailto,tcp - a
-    decoupled reply over JMS, mail or TCP is a legitimate configuration, so the
-    list is not restricted to HTTP. Schemes outside it, such as file or gopher,
-    are refused. Add to this list if a custom transport is in use.
+    allowedResponseEndpointSchemes defaults to https ALONE. Plain http is
+    excluded on purpose: the cloud instance-metadata services this class of 
SSRF
+    targets (169.254.169.254 on AWS and Azure, metadata.google.internal on GCP)
+    are HTTP-only, so refusing the scheme removes that target outright. jms,
+    mailto and tcp have senders in the tree but are vanishingly rare as reply
+    destinations, so they are opt-in too. Name whatever a deployment genuinely
+    replies over, e.g. http,https for plain-HTTP callbacks inside a trusted
+    network.
     -->
     <parameter name="allowNonAnonymousResponseEndpoints">false</parameter>
     <parameter name="blockPrivateNetworkResponseEndpoints">false</parameter>
     <!--<parameter 
name="allowedResponseEndpointHosts">replies.example.com</parameter>-->
-    <!--<parameter 
name="allowedResponseEndpointSchemes">http,https</parameter>-->
+    <!--<parameter name="allowedResponseEndpointSchemes">https</parameter>-->
 
 
     <!--Uncomment if you want to plugin your own attachments lifecycle 
implementation -->
diff --git 
a/modules/kernel/src/org/apache/axis2/addressing/ResponseEndpointPolicy.java 
b/modules/kernel/src/org/apache/axis2/addressing/AddressingResponseEndpointPolicy.java
similarity index 92%
rename from 
modules/kernel/src/org/apache/axis2/addressing/ResponseEndpointPolicy.java
rename to 
modules/kernel/src/org/apache/axis2/addressing/AddressingResponseEndpointPolicy.java
index 6aab3227a8..c9b10946d3 100644
--- a/modules/kernel/src/org/apache/axis2/addressing/ResponseEndpointPolicy.java
+++ 
b/modules/kernel/src/org/apache/axis2/addressing/AddressingResponseEndpointPolicy.java
@@ -82,11 +82,16 @@ import java.util.concurrent.TimeoutException;
  * <dt>{@code allowedResponseEndpointHosts} (no default)</dt>
  * <dd>A comma-separated host allow-list. When set, a response endpoint host 
must
  * appear in it, which supersedes the network-range check.</dd>
+ *
+ * <dt>{@code allowedResponseEndpointSchemes} (default {@code https})</dt>
+ * <dd>A comma-separated scheme allow-list, applied once decoupled responses 
are
+ * enabled at all. See {@link #DEFAULT_ALLOWED_SCHEMES} for why the default is
+ * HTTPS alone.</dd>
  * </dl>
  */
-public final class ResponseEndpointPolicy {
+public final class AddressingResponseEndpointPolicy {
 
-    private static final Log log = 
LogFactory.getLog(ResponseEndpointPolicy.class);
+    private static final Log log = 
LogFactory.getLog(AddressingResponseEndpointPolicy.class);
 
     public static final String ALLOW_NON_ANONYMOUS = 
"allowNonAnonymousResponseEndpoints";
     public static final String BLOCK_PRIVATE_NETWORKS = 
"blockPrivateNetworkResponseEndpoints";
@@ -106,23 +111,30 @@ public final class ResponseEndpointPolicy {
     private static final int RESOLVER_QUEUE_DEPTH = 64;
 
     /**
-     * Schemes Axis2 ships a sender for that can carry a decoupled reply.
+     * Schemes a decoupled reply may use, unless {@link 
#ALLOWED_SCHEMES_PARAMETER}
+     * widens it. Just {@code https}.
+     *
+     * <p>Plain {@code http} is excluded deliberately, and it is the exclusion 
that
+     * carries the most weight: the cloud instance-metadata services this 
class of
+     * SSRF is usually aimed at — AWS and Azure on 169.254.169.254, GCP on
+     * metadata.google.internal — are reachable over HTTP only and offer no 
HTTPS
+     * listener. Refusing plain HTTP therefore takes that target away by scheme
+     * alone, without depending on the address checks below.
      *
-     * <p>This is deliberately wider than http/https: {@code 
setupCorrectTransportOut}
-     * resolves the response transport from the endpoint reference's scheme
-     * against whatever is registered, so a JMS, mail or TCP reply address is a
-     * legitimate configuration and must not be refused here. What the list 
keeps
-     * out are the schemes that only ever serve as an SSRF pivot — file, 
gopher,
-     * jar, ftp and the like.
+     * <p>{@code jms}, {@code mailto} and {@code tcp} have senders in the tree 
but
+     * are vanishingly rare as reply destinations, so they are opt-in rather 
than
+     * default-on. A deployment that needs any of these, or that genuinely 
replies
+     * over plain HTTP inside a trusted network, names them in
+     * {@link #ALLOWED_SCHEMES_PARAMETER}.
      */
-    private static final Set<String> DEFAULT_ALLOWED_SCHEMES = new 
HashSet<String>(
-            Arrays.asList("http", "https", "jms", "mailto", "tcp"));
+    private static final Set<String> DEFAULT_ALLOWED_SCHEMES =
+            new HashSet<String>(Arrays.asList("https"));
 
     /** Schemes for which a missing host means the address is unusable. */
     private static final Set<String> HOST_BEARING_SCHEMES =
             new HashSet<String>(Arrays.asList("http", "https"));
 
-    private ResponseEndpointPolicy() {
+    private AddressingResponseEndpointPolicy() {
     }
 
     /**
diff --git a/modules/kernel/src/org/apache/axis2/engine/AxisConfiguration.java 
b/modules/kernel/src/org/apache/axis2/engine/AxisConfiguration.java
index a0dc32ac12..9582287a62 100644
--- a/modules/kernel/src/org/apache/axis2/engine/AxisConfiguration.java
+++ b/modules/kernel/src/org/apache/axis2/engine/AxisConfiguration.java
@@ -1343,7 +1343,7 @@ public class AxisConfiguration extends AxisDescription {
         // redeployment and pin this web application's class loader.
         MultipartTempFileTracker.shutdown();
         // Same reason: the WS-Addressing endpoint resolver runs a thread pool.
-        org.apache.axis2.addressing.ResponseEndpointPolicy.shutdown();
+        
org.apache.axis2.addressing.AddressingResponseEndpointPolicy.shutdown();
         this.policySupportedModules.clear();
         this.moduleConfigmap.clear();
         this.allEndpoints.clear();
diff --git 
a/modules/kernel/src/org/apache/axis2/util/MessageContextBuilder.java 
b/modules/kernel/src/org/apache/axis2/util/MessageContextBuilder.java
index 91a3f786f2..87f78f42f9 100644
--- a/modules/kernel/src/org/apache/axis2/util/MessageContextBuilder.java
+++ b/modules/kernel/src/org/apache/axis2/util/MessageContextBuilder.java
@@ -43,7 +43,7 @@ import org.apache.axis2.addressing.AddressingConstants;
 import org.apache.axis2.addressing.AddressingConstants.Final;
 import org.apache.axis2.addressing.AddressingHelper;
 import org.apache.axis2.addressing.EndpointReference;
-import org.apache.axis2.addressing.ResponseEndpointPolicy;
+import org.apache.axis2.addressing.AddressingResponseEndpointPolicy;
 import org.apache.axis2.addressing.RelatesTo;
 import org.apache.axis2.context.ConfigurationContext;
 import org.apache.axis2.context.MessageContext;
@@ -390,10 +390,10 @@ public class MessageContextBuilder {
                     // check cannot be walked around by reaching the sender 
another
                     // way. Apache CXF places its equivalent check at the same
                     // depth, in DecoupledDestination.getBackChannel.
-                    if (!ResponseEndpointPolicy.isAllowed(responseEPR, 
context)) {
+                    if 
(!AddressingResponseEndpointPolicy.isAllowed(responseEPR, context)) {
                         throw new AxisFault("Refusing to send a response to 
the "
                                 + "endpoint reference named by this message; 
see the "
-                                + ResponseEndpointPolicy.ALLOW_NON_ANONYMOUS + 
" parameter");
+                                + 
AddressingResponseEndpointPolicy.ALLOW_NON_ANONYMOUS + " parameter");
                     }
                     URI uri = new URI(responseEPR.getAddress());
                     String scheme = uri.getScheme();
diff --git 
a/modules/kernel/test/org/apache/axis2/addressing/ResponseEndpointPolicyTest.java
 
b/modules/kernel/test/org/apache/axis2/addressing/AddressingResponseEndpointPolicyTest.java
similarity index 64%
rename from 
modules/kernel/test/org/apache/axis2/addressing/ResponseEndpointPolicyTest.java
rename to 
modules/kernel/test/org/apache/axis2/addressing/AddressingResponseEndpointPolicyTest.java
index d672157d5c..67522a170a 100644
--- 
a/modules/kernel/test/org/apache/axis2/addressing/ResponseEndpointPolicyTest.java
+++ 
b/modules/kernel/test/org/apache/axis2/addressing/AddressingResponseEndpointPolicyTest.java
@@ -29,7 +29,7 @@ import org.apache.axis2.engine.AxisConfiguration;
  * Unit tests for the egress policy applied to inbound WS-Addressing ReplyTo 
and
  * FaultTo endpoint references.
  */
-public class ResponseEndpointPolicyTest extends TestCase {
+public class AddressingResponseEndpointPolicyTest extends TestCase {
 
     private AxisConfiguration axisConfiguration;
     private MessageContext messageContext;
@@ -43,7 +43,36 @@ public class ResponseEndpointPolicyTest extends TestCase {
         // Decoupled responses are off by default. Most tests here cover what 
the
         // policy does to an endpoint it is actually willing to consider, so 
they
         // opt in; testDecoupledResponsesAreOffByDefault covers the default.
-        setParameter(ResponseEndpointPolicy.ALLOW_NON_ANONYMOUS, "true");
+        setParameter(AddressingResponseEndpointPolicy.ALLOW_NON_ANONYMOUS, 
"true");
+        // Only https is permitted by default. These tests exercise the address
+        // checks over plain http for readability, so widen the list here;
+        // testOnlyHttpsIsAllowedByDefault covers the shipped default.
+        
setParameter(AddressingResponseEndpointPolicy.ALLOWED_SCHEMES_PARAMETER, 
"http,https");
+    }
+
+    /**
+     * Plain HTTP is not permitted unless asked for. This matters beyond 
tidiness:
+     * the cloud instance-metadata endpoints are HTTP-only, so refusing the 
scheme
+     * removes that target without relying on the address checks at all.
+     */
+    public void testOnlyHttpsIsAllowedByDefault() throws Exception {
+        AxisConfiguration config = new AxisConfiguration();
+        config.addParameter(
+                new 
Parameter(AddressingResponseEndpointPolicy.ALLOW_NON_ANONYMOUS, "true"));
+        MessageContext mc = new 
ConfigurationContext(config).createMessageContext();
+        mc.setServerSide(true);
+
+        assertTrue("https must be allowed", 
AddressingResponseEndpointPolicy.isAllowed(
+                new EndpointReference("https://192.0.2.25/cb";), mc));
+        assertFalse("plain http must be opt-in", 
AddressingResponseEndpointPolicy.isAllowed(
+                new EndpointReference("http://192.0.2.25/cb";), mc));
+        assertFalse("the metadata endpoint is refused on scheme alone",
+                AddressingResponseEndpointPolicy.isAllowed(
+                        new 
EndpointReference("http://169.254.169.254/latest/meta-data/";), mc));
+        assertFalse("jms must be opt-in", 
AddressingResponseEndpointPolicy.isAllowed(
+                new EndpointReference("jms:/ReplyQueue"), mc));
+        assertFalse("tcp must be opt-in", 
AddressingResponseEndpointPolicy.isAllowed(
+                new EndpointReference("tcp://192.0.2.25:6060/svc"), mc));
     }
 
     /** A context without the opt-in, for testing the shipped default. */
@@ -65,8 +94,8 @@ public class ResponseEndpointPolicyTest extends TestCase {
     public void testAnonymousAddressIsAlwaysAllowed() {
         EndpointReference anonymous =
                 new 
EndpointReference(AddressingConstants.Final.WSA_ANONYMOUS_URL);
-        assertTrue(ResponseEndpointPolicy.isAllowed(anonymous, 
messageContext));
-        assertTrue(ResponseEndpointPolicy.isAllowed(null, messageContext));
+        assertTrue(AddressingResponseEndpointPolicy.isAllowed(anonymous, 
messageContext));
+        assertTrue(AddressingResponseEndpointPolicy.isAllowed(null, 
messageContext));
     }
 
     /**
@@ -76,7 +105,7 @@ public class ResponseEndpointPolicyTest extends TestCase {
     public void testInstanceMetadataAddressIsBlockedByDefault() {
         EndpointReference metadata =
                 new 
EndpointReference("http://169.254.169.254/latest/meta-data/";);
-        assertFalse(ResponseEndpointPolicy.isAllowed(metadata, 
messageContext));
+        assertFalse(AddressingResponseEndpointPolicy.isAllowed(metadata, 
messageContext));
     }
 
     /**
@@ -86,24 +115,24 @@ public class ResponseEndpointPolicyTest extends TestCase {
      * must not refuse them.
      */
     public void testLoopbackAndPrivateAddressesAllowedWhenDecoupledEnabled() {
-        assertTrue(ResponseEndpointPolicy.isAllowed(
+        assertTrue(AddressingResponseEndpointPolicy.isAllowed(
                 new EndpointReference("http://127.0.0.1:8080/sink";), 
messageContext));
-        assertTrue(ResponseEndpointPolicy.isAllowed(
+        assertTrue(AddressingResponseEndpointPolicy.isAllowed(
                 new EndpointReference("http://10.1.2.3/internal";), 
messageContext));
-        assertTrue(ResponseEndpointPolicy.isAllowed(
+        assertTrue(AddressingResponseEndpointPolicy.isAllowed(
                 new EndpointReference("http://192.168.1.10/admin";), 
messageContext));
     }
 
     /** Opting in to the strict posture refuses them. */
     public void testLoopbackAndPrivateAddressesBlockedWhenOptedIn() throws 
Exception {
-        setParameter(ResponseEndpointPolicy.BLOCK_PRIVATE_NETWORKS, "true");
-        assertFalse(ResponseEndpointPolicy.isAllowed(
+        setParameter(AddressingResponseEndpointPolicy.BLOCK_PRIVATE_NETWORKS, 
"true");
+        assertFalse(AddressingResponseEndpointPolicy.isAllowed(
                 new EndpointReference("http://127.0.0.1:8080/sink";), 
messageContext));
-        assertFalse(ResponseEndpointPolicy.isAllowed(
+        assertFalse(AddressingResponseEndpointPolicy.isAllowed(
                 new EndpointReference("http://10.1.2.3/internal";), 
messageContext));
-        assertFalse(ResponseEndpointPolicy.isAllowed(
+        assertFalse(AddressingResponseEndpointPolicy.isAllowed(
                 new EndpointReference("http://192.168.1.10/admin";), 
messageContext));
-        assertFalse(ResponseEndpointPolicy.isAllowed(
+        assertFalse(AddressingResponseEndpointPolicy.isAllowed(
                 new EndpointReference("http://172.16.5.5/admin";), 
messageContext));
     }
 
@@ -112,9 +141,9 @@ public class ResponseEndpointPolicyTest extends TestCase {
      * are refused without opting in.
      */
     public void testWildcardAndMulticastAlwaysBlocked() {
-        assertFalse(ResponseEndpointPolicy.isAllowed(
+        assertFalse(AddressingResponseEndpointPolicy.isAllowed(
                 new EndpointReference("http://0.0.0.0/sink";), messageContext));
-        assertFalse(ResponseEndpointPolicy.isAllowed(
+        assertFalse(AddressingResponseEndpointPolicy.isAllowed(
                 new EndpointReference("http://239.1.2.3/sink";), 
messageContext));
     }
 
@@ -123,13 +152,13 @@ public class ResponseEndpointPolicyTest extends TestCase {
      * host check.
      */
     public void testPivotSchemesAreRejected() {
-        assertFalse(ResponseEndpointPolicy.isAllowed(
+        assertFalse(AddressingResponseEndpointPolicy.isAllowed(
                 new EndpointReference("file:///etc/passwd"), messageContext));
-        assertFalse(ResponseEndpointPolicy.isAllowed(
+        assertFalse(AddressingResponseEndpointPolicy.isAllowed(
                 new EndpointReference("gopher://example.com/1";), 
messageContext));
-        assertFalse(ResponseEndpointPolicy.isAllowed(
+        assertFalse(AddressingResponseEndpointPolicy.isAllowed(
                 new EndpointReference("jar:http://example.com/a.jar!/";), 
messageContext));
-        assertFalse(ResponseEndpointPolicy.isAllowed(
+        assertFalse(AddressingResponseEndpointPolicy.isAllowed(
                 new EndpointReference("ftp://example.com/drop";), 
messageContext));
     }
 
@@ -139,13 +168,15 @@ public class ResponseEndpointPolicyTest extends TestCase {
      * reference's scheme against whatever is registered — so the policy must 
not
      * restrict replies to HTTP.
      */
-    public void testNonHttpTransportSchemesAreAllowed() {
-        assertTrue(ResponseEndpointPolicy.isAllowed(
+    public void testNonHttpTransportSchemesAreAllowedWhenNamed() throws 
Exception {
+        
setParameter(AddressingResponseEndpointPolicy.ALLOWED_SCHEMES_PARAMETER,
+                "http,https,jms,mailto,tcp");
+        assertTrue(AddressingResponseEndpointPolicy.isAllowed(
                 new 
EndpointReference("jms:/ReplyQueue?transport.jms.ConnectionFactory=qcf"),
                 messageContext));
-        assertTrue(ResponseEndpointPolicy.isAllowed(
+        assertTrue(AddressingResponseEndpointPolicy.isAllowed(
                 new EndpointReference("mailto:[email protected]";), 
messageContext));
-        assertTrue(ResponseEndpointPolicy.isAllowed(
+        assertTrue(AddressingResponseEndpointPolicy.isAllowed(
                 new EndpointReference("tcp://192.0.2.25:6060/svc"), 
messageContext));
     }
 
@@ -153,17 +184,18 @@ public class ResponseEndpointPolicyTest extends TestCase {
      * The range check is about the destination address, not the scheme, so a
      * non-HTTP address that does name a host is still screened.
      */
-    public void testNonHttpSchemeStillGetsTheRangeCheck() {
-        assertFalse(ResponseEndpointPolicy.isAllowed(
+    public void testNonHttpSchemeStillGetsTheRangeCheck() throws Exception {
+        
setParameter(AddressingResponseEndpointPolicy.ALLOWED_SCHEMES_PARAMETER, 
"http,https,tcp");
+        assertFalse(AddressingResponseEndpointPolicy.isAllowed(
                 new EndpointReference("tcp://169.254.169.254:6060/svc"), 
messageContext));
     }
 
     public void testSchemeListIsConfigurable() throws Exception {
-        setParameter(ResponseEndpointPolicy.ALLOWED_SCHEMES_PARAMETER, 
"http,https");
+        
setParameter(AddressingResponseEndpointPolicy.ALLOWED_SCHEMES_PARAMETER, 
"http,https");
         assertFalse("A scheme outside the configured list should be refused",
-                ResponseEndpointPolicy.isAllowed(
+                AddressingResponseEndpointPolicy.isAllowed(
                         new EndpointReference("mailto:[email protected]";), 
messageContext));
-        assertTrue(ResponseEndpointPolicy.isAllowed(
+        assertTrue(AddressingResponseEndpointPolicy.isAllowed(
                 new EndpointReference("http://192.0.2.25/replies";), 
messageContext));
     }
 
@@ -173,7 +205,7 @@ public class ResponseEndpointPolicyTest extends TestCase {
      */
     public void testPublicAddressAllowedWhenDecoupledEnabled() {
         EndpointReference publicEpr = new 
EndpointReference("http://192.0.2.25/replies";);
-        assertTrue(ResponseEndpointPolicy.isAllowed(publicEpr, 
messageContext));
+        assertTrue(AddressingResponseEndpointPolicy.isAllowed(publicEpr, 
messageContext));
     }
 
     /**
@@ -184,16 +216,16 @@ public class ResponseEndpointPolicyTest extends TestCase {
     public void testDecoupledResponsesAreOffByDefault() throws Exception {
         MessageContext defaults = defaultConfigured();
         assertFalse("A public reply endpoint must be refused by default",
-                ResponseEndpointPolicy.isAllowed(
+                AddressingResponseEndpointPolicy.isAllowed(
                         new EndpointReference("http://192.0.2.25/replies";), 
defaults));
         assertFalse("A private reply endpoint must be refused by default",
-                ResponseEndpointPolicy.isAllowed(
+                AddressingResponseEndpointPolicy.isAllowed(
                         new EndpointReference("http://10.1.2.3/internal";), 
defaults));
 
         // Anonymous must still work by default, or ordinary in-out messaging 
breaks.
-        assertTrue(ResponseEndpointPolicy.isAllowed(
+        assertTrue(AddressingResponseEndpointPolicy.isAllowed(
                 new 
EndpointReference(AddressingConstants.Final.WSA_ANONYMOUS_URL), defaults));
-        assertTrue(ResponseEndpointPolicy.isAllowed(null, defaults));
+        assertTrue(AddressingResponseEndpointPolicy.isAllowed(null, defaults));
     }
 
     /**
@@ -201,10 +233,10 @@ public class ResponseEndpointPolicyTest extends TestCase {
      * that really does reply into its own network can permit exactly that 
host.
      */
     public void testAllowListPermitsAnOtherwiseBlockedHost() throws Exception {
-        setParameter(ResponseEndpointPolicy.ALLOWED_HOSTS, 
"replies.example.com, 127.0.0.1");
-        assertTrue(ResponseEndpointPolicy.isAllowed(
+        setParameter(AddressingResponseEndpointPolicy.ALLOWED_HOSTS, 
"replies.example.com, 127.0.0.1");
+        assertTrue(AddressingResponseEndpointPolicy.isAllowed(
                 new EndpointReference("http://127.0.0.1:8080/sink";), 
messageContext));
-        assertFalse(ResponseEndpointPolicy.isAllowed(
+        assertFalse(AddressingResponseEndpointPolicy.isAllowed(
                 new EndpointReference("http://192.0.2.25/replies";), 
messageContext));
     }
 
@@ -213,10 +245,10 @@ public class ResponseEndpointPolicyTest extends TestCase {
      * explicitly off: link-local is refused unconditionally.
      */
     public void testMetadataAddressStillBlockedWithPrivateRangeCheckOff() 
throws Exception {
-        setParameter(ResponseEndpointPolicy.BLOCK_PRIVATE_NETWORKS, "false");
-        assertFalse(ResponseEndpointPolicy.isAllowed(
+        setParameter(AddressingResponseEndpointPolicy.BLOCK_PRIVATE_NETWORKS, 
"false");
+        assertFalse(AddressingResponseEndpointPolicy.isAllowed(
                 new 
EndpointReference("http://169.254.169.254/latest/meta-data/";), messageContext));
-        assertTrue(ResponseEndpointPolicy.isAllowed(
+        assertTrue(AddressingResponseEndpointPolicy.isAllowed(
                 new EndpointReference("http://127.0.0.1:8080/sink";), 
messageContext));
     }
 
@@ -226,7 +258,7 @@ public class ResponseEndpointPolicyTest extends TestCase {
      * .invalid TLD is reserved by RFC 2606 precisely so it never resolves.
      */
     public void testUnresolvableHostIsRejected() {
-        assertFalse(ResponseEndpointPolicy.isAllowed(
+        assertFalse(AddressingResponseEndpointPolicy.isAllowed(
                 new EndpointReference("http://no-such-host.invalid/replies";), 
messageContext));
     }
 
@@ -235,18 +267,18 @@ public class ResponseEndpointPolicyTest extends TestCase {
      * on the request path for the direct-IP cases.
      */
     public void testLiteralAddressNeedsNoResolution() throws Exception {
-        setParameter(ResponseEndpointPolicy.RESOLVE_TIMEOUT, "1");
+        setParameter(AddressingResponseEndpointPolicy.RESOLVE_TIMEOUT, "1");
         // Would time out if this went to the resolver; it must not.
-        assertFalse(ResponseEndpointPolicy.isAllowed(
+        assertFalse(AddressingResponseEndpointPolicy.isAllowed(
                 new 
EndpointReference("http://169.254.169.254/latest/meta-data/";), messageContext));
-        assertTrue(ResponseEndpointPolicy.isAllowed(
+        assertTrue(AddressingResponseEndpointPolicy.isAllowed(
                 new EndpointReference("http://192.0.2.25/replies";), 
messageContext));
     }
 
     public void testMalformedAddressIsRejected() {
-        assertFalse(ResponseEndpointPolicy.isAllowed(
+        assertFalse(AddressingResponseEndpointPolicy.isAllowed(
                 new EndpointReference("http://[not a uri"), messageContext));
-        assertFalse(ResponseEndpointPolicy.isAllowed(
+        assertFalse(AddressingResponseEndpointPolicy.isAllowed(
                 new EndpointReference("http:///no-host";), messageContext));
     }
 }
diff --git a/modules/webapp/conf/axis2.xml b/modules/webapp/conf/axis2.xml
index 2e8aecbe9b..922f1cfacf 100644
--- a/modules/webapp/conf/axis2.xml
+++ b/modules/webapp/conf/axis2.xml
@@ -92,15 +92,19 @@
     used by the checks above, so a slow resolver cannot hold a request thread.
     Literal addresses are never looked up.
 
-    allowedResponseEndpointSchemes defaults to http,https,jms,mailto,tcp - a
-    decoupled reply over JMS, mail or TCP is a legitimate configuration, so the
-    list is not restricted to HTTP. Schemes outside it, such as file or gopher,
-    are refused. Add to this list if a custom transport is in use.
+    allowedResponseEndpointSchemes defaults to https ALONE. Plain http is
+    excluded on purpose: the cloud instance-metadata services this class of 
SSRF
+    targets (169.254.169.254 on AWS and Azure, metadata.google.internal on GCP)
+    are HTTP-only, so refusing the scheme removes that target outright. jms,
+    mailto and tcp have senders in the tree but are vanishingly rare as reply
+    destinations, so they are opt-in too. Name whatever a deployment genuinely
+    replies over, e.g. http,https for plain-HTTP callbacks inside a trusted
+    network.
     -->
     <parameter name="allowNonAnonymousResponseEndpoints">false</parameter>
     <parameter name="blockPrivateNetworkResponseEndpoints">false</parameter>
     <!--<parameter 
name="allowedResponseEndpointHosts">replies.example.com</parameter>-->
-    <!--<parameter 
name="allowedResponseEndpointSchemes">http,https</parameter>-->
+    <!--<parameter name="allowedResponseEndpointSchemes">https</parameter>-->
 
     <!--Uncomment if you want to plugin your own attachments lifecycle 
implementation -->
     <!--<attachmentsLifecycleManager 
class="org.apache.axiom.attachments.lifecycle.impl.LifecycleManagerImpl"/>-->

Reply via email to