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 02845d09459c4bbd057aca08e3106ae3412e5166
Author: Robert Lazarski <[email protected]>
AuthorDate: Mon Apr 6 18:44:24 2026 -1000

    JSON-RPC error hardening: correlation ID replaces raw AxisFault.makeFault
    
    Malformed JSON-RPC bodies (wrong envelope, invalid JSON) previously threw
    AxisFault.makeFault(e), which serialises the IOException message and may
    expose structural detail — a finding in the annual penetration test.
    
    Replace with a correlation ID pattern in both JsonRpcMessageReceiver and
    JsonInOnlyRPCMessageReceiver: a UUID is generated at catch time, the full
    context (operation name + exception message + stack trace) is logged
    server-side with [errorRef=<uuid>], and only "Bad Request [errorRef=<uuid>]"
    is returned to the caller.  Developers grep the UUID; pen-testers see no
    class names, field paths, or exception text.
    
    Add postForResponse() helper to UtilTest (captures body on non-2xx) and six
    new integration tests verifying: "Bad Request" text present, errorRef 
present,
    errorRef is a valid UUID, no exception class name leaked, and InOnly 
receiver
    applies the same pattern.
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
---
 .../gson/rpc/JsonInOnlyRPCMessageReceiver.java     |  12 ++-
 .../json/gson/rpc/JsonRpcMessageReceiver.java      |  12 ++-
 .../test/org/apache/axis2/json/gson/UtilTest.java  |  23 +++++
 .../json/gson/rpc/JSONRPCIntegrationTest.java      | 103 ++++++++++++++++++++-
 4 files changed, 141 insertions(+), 9 deletions(-)

diff --git 
a/modules/json/src/org/apache/axis2/json/gson/rpc/JsonInOnlyRPCMessageReceiver.java
 
b/modules/json/src/org/apache/axis2/json/gson/rpc/JsonInOnlyRPCMessageReceiver.java
index 5f933e6988..4b8873443f 100644
--- 
a/modules/json/src/org/apache/axis2/json/gson/rpc/JsonInOnlyRPCMessageReceiver.java
+++ 
b/modules/json/src/org/apache/axis2/json/gson/rpc/JsonInOnlyRPCMessageReceiver.java
@@ -32,6 +32,7 @@ import org.apache.commons.logging.LogFactory;
 import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.util.UUID;
 
 public class JsonInOnlyRPCMessageReceiver extends RPCInOnlyMessageReceiver {
     private static final Log log = 
LogFactory.getLog(JsonInOnlyRPCMessageReceiver.class);
@@ -90,10 +91,13 @@ public class JsonInOnlyRPCMessageReceiver extends 
RPCInOnlyMessageReceiver {
             log.error(msg, e);
             throw AxisFault.makeFault(e);
         } catch (IOException e) {
-            msg = "Exception occur while encording or " +
-                    "access to the input string at the JsonRpcMessageReceiver";
-            log.error(msg, e);
-            throw AxisFault.makeFault(e);
+            // Correlation ID: full error context is logged server-side; only 
the
+            // opaque reference is returned to the client so malformed-request
+            // failures remain safe under penetration testing.
+            String errorRef = UUID.randomUUID().toString();
+            log.error("[errorRef=" + errorRef + "] Bad Request parsing 
JSON-RPC body " +
+                    "for operation '" + operation_name + "': " + 
e.getMessage(), e);
+            throw new AxisFault("Bad Request [errorRef=" + errorRef + "]");
         }
     }
 }
diff --git 
a/modules/json/src/org/apache/axis2/json/gson/rpc/JsonRpcMessageReceiver.java 
b/modules/json/src/org/apache/axis2/json/gson/rpc/JsonRpcMessageReceiver.java
index e13dd99c8b..972cc9da21 100644
--- 
a/modules/json/src/org/apache/axis2/json/gson/rpc/JsonRpcMessageReceiver.java
+++ 
b/modules/json/src/org/apache/axis2/json/gson/rpc/JsonRpcMessageReceiver.java
@@ -31,6 +31,7 @@ import org.apache.commons.logging.LogFactory;
 import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.util.UUID;
 
 
 public class JsonRpcMessageReceiver extends RPCMessageReceiver {
@@ -94,10 +95,13 @@ public class JsonRpcMessageReceiver extends 
RPCMessageReceiver {
             log.error(msg, e);
             throw AxisFault.makeFault(e);
         } catch (IOException e) {
-            msg = "Exception occur while encording or " +
-                    "access to the input string at the JsonRpcMessageReceiver";
-            log.error(msg, e);
-            throw AxisFault.makeFault(e);
+            // Correlation ID: full error context is logged server-side; only 
the
+            // opaque reference is returned to the client so malformed-request
+            // failures remain safe under penetration testing.
+            String errorRef = UUID.randomUUID().toString();
+            log.error("[errorRef=" + errorRef + "] Bad Request parsing 
JSON-RPC body " +
+                    "for operation '" + operation_name + "': " + 
e.getMessage(), e);
+            throw new AxisFault("Bad Request [errorRef=" + errorRef + "]");
         }
     }
 }
diff --git a/modules/json/test/org/apache/axis2/json/gson/UtilTest.java 
b/modules/json/test/org/apache/axis2/json/gson/UtilTest.java
index 474cafdf1a..7592d39243 100644
--- a/modules/json/test/org/apache/axis2/json/gson/UtilTest.java
+++ b/modules/json/test/org/apache/axis2/json/gson/UtilTest.java
@@ -33,6 +33,29 @@ import java.io.IOException;
 
 public class UtilTest {
 
+    /**
+     * Post {@code jsonString} to {@code strURL} and return a two-element 
array:
+     * {@code [statusCode, responseBody]}.  Unlike {@link #post}, this method
+     * does NOT throw on non-2xx status codes — callers that test error paths
+     * need the response body even when HTTP 500 is returned.
+     */
+    public static Object[] postForResponse(String jsonString, String strURL)
+            throws IOException {
+        HttpEntity stringEntity = new StringEntity(jsonString, 
ContentType.APPLICATION_JSON);
+        HttpPost httpPost = new HttpPost(strURL);
+        httpPost.setEntity(stringEntity);
+        CloseableHttpClient httpclient = HttpClients.createDefault();
+        try {
+            CloseableHttpResponse response = httpclient.execute(httpPost);
+            int status = response.getCode();
+            HttpEntity entity = response.getEntity();
+            String body = entity != null ? EntityUtils.toString(entity, 
"UTF-8") : "";
+            return new Object[]{status, body};
+        } finally {
+            httpclient.close();
+        }
+    }
+
     public static String post(String jsonString, String strURL)
             throws IOException {
         HttpEntity stringEntity = new 
StringEntity(jsonString,ContentType.APPLICATION_JSON);
diff --git 
a/modules/json/test/org/apache/axis2/json/gson/rpc/JSONRPCIntegrationTest.java 
b/modules/json/test/org/apache/axis2/json/gson/rpc/JSONRPCIntegrationTest.java
index 1b364eaafc..94984b8f60 100644
--- 
a/modules/json/test/org/apache/axis2/json/gson/rpc/JSONRPCIntegrationTest.java
+++ 
b/modules/json/test/org/apache/axis2/json/gson/rpc/JSONRPCIntegrationTest.java
@@ -25,10 +25,16 @@ import org.junit.Assert;
 import org.junit.ClassRule;
 import org.junit.Test;
 
+import java.util.regex.Pattern;
+
 public class JSONRPCIntegrationTest {
     @ClassRule
     public static Axis2Server server = new Axis2Server("target/repo/gson");
-    
+
+    // UUID pattern: 8-4-4-4-12 hex digits
+    private static final Pattern UUID_PATTERN =
+            
Pattern.compile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}");
+
     @Test
     public void testJsonRpcMessageReceiver() throws Exception {
         String jsonRequest = 
"{\"echoPerson\":[{\"arg0\":{\"name\":\"Simon\",\"age\":\"35\",\"gender\":\"male\"}}]}";
@@ -46,4 +52,99 @@ public class JSONRPCIntegrationTest {
         String response = UtilTest.post(jsonRequest, echoPersonUrl);
         Assert.assertEquals("", response);
     }
+
+    // ── correlation ID / error hardening tests 
────────────────────────────────
+
+    /**
+     * A completely malformed JSON body (not even valid JSON) must return an
+     * error response that contains "Bad Request" — the security-safe message —
+     * rather than leaking a Java exception class name or stack trace.
+     */
+    @Test
+    public void testMalformedJsonBodyReturnsBadRequest() throws Exception {
+        String echoPersonUrl = server.getEndpoint("JSONPOJOService") + 
"echoPerson";
+        Object[] result = UtilTest.postForResponse("NOT_VALID_JSON", 
echoPersonUrl);
+        String body = (String) result[1];
+        Assert.assertTrue("Response must contain 'Bad Request' for malformed 
JSON body",
+                body.contains("Bad Request"));
+    }
+
+    /**
+     * A malformed request must include an errorRef (correlation ID) so that
+     * developers can grep server logs without the client seeing any structural
+     * detail about the failure.
+     */
+    @Test
+    public void testMalformedJsonBodyIncludesCorrelationId() throws Exception {
+        String echoPersonUrl = server.getEndpoint("JSONPOJOService") + 
"echoPerson";
+        Object[] result = UtilTest.postForResponse("NOT_VALID_JSON", 
echoPersonUrl);
+        String body = (String) result[1];
+        Assert.assertTrue("Response must contain 'errorRef=' correlation ID",
+                body.contains("errorRef="));
+    }
+
+    /**
+     * The errorRef value embedded in the fault message must be a valid UUID so
+     * that it is grep-able in server logs and carries no structural 
information
+     * about the request path.
+     */
+    @Test
+    public void testMalformedJsonBodyCorrelationIdIsUuid() throws Exception {
+        String echoPersonUrl = server.getEndpoint("JSONPOJOService") + 
"echoPerson";
+        Object[] result = UtilTest.postForResponse("NOT_VALID_JSON", 
echoPersonUrl);
+        String body = (String) result[1];
+        Assert.assertTrue("errorRef in fault must be a UUID",
+                UUID_PATTERN.matcher(body).find());
+    }
+
+    /**
+     * A correctly enveloped request that uses the wrong operation name wrapper
+     * (e.g. missing the outer array) must return "Bad Request" with an 
errorRef,
+     * not a Java stack trace or IOException message.
+     */
+    @Test
+    public void testMissingOuterArrayReturnsBadRequestWithCorrelationId() 
throws Exception {
+        // Valid JSON but wrong envelope: missing the [{...}] wrapper
+        String badEnvelope = "{\"echoPerson\":{\"name\":\"Simon\"}}";
+        String echoPersonUrl = server.getEndpoint("JSONPOJOService") + 
"echoPerson";
+        Object[] result = UtilTest.postForResponse(badEnvelope, echoPersonUrl);
+        String body = (String) result[1];
+        Assert.assertTrue("Wrong-envelope request must return 'Bad Request'",
+                body.contains("Bad Request"));
+        Assert.assertTrue("Wrong-envelope response must contain an errorRef",
+                body.contains("errorRef="));
+    }
+
+    /**
+     * Error responses must NOT leak Java exception class names (e.g.
+     * "MalformedJsonException" or "IOException").  The correlation ID pattern
+     * ensures the fault message is purely "Bad Request [errorRef=<uuid>]".
+     */
+    @Test
+    public void testMalformedJsonBodyDoesNotLeakExceptionClassName() throws 
Exception {
+        String echoPersonUrl = server.getEndpoint("JSONPOJOService") + 
"echoPerson";
+        Object[] result = UtilTest.postForResponse("NOT_VALID_JSON", 
echoPersonUrl);
+        String body = (String) result[1];
+        Assert.assertFalse("Response must not leak 'MalformedJsonException'",
+                body.contains("MalformedJsonException"));
+        Assert.assertFalse("Response must not leak 'IOException'",
+                body.contains("IOException"));
+        Assert.assertFalse("Response must not leak stack trace element 'at 
org.apache'",
+                body.contains("at org.apache"));
+    }
+
+    /**
+     * The InOnly receiver (fire-and-forget) must apply the same correlation ID
+     * pattern for malformed requests — no exception leak on that path either.
+     */
+    @Test
+    public void 
testInOnlyMalformedJsonBodyReturnsBadRequestWithCorrelationId() throws 
Exception {
+        String pingUrl = server.getEndpoint("JSONPOJOService") + "ping";
+        Object[] result = UtilTest.postForResponse("NOT_VALID_JSON", pingUrl);
+        String body = (String) result[1];
+        Assert.assertTrue("InOnly malformed request must return 'Bad Request'",
+                body.contains("Bad Request"));
+        Assert.assertTrue("InOnly malformed request must contain an errorRef",
+                body.contains("errorRef="));
+    }
 }

Reply via email to