This is an automated email from the ASF dual-hosted git repository.

lukaszlenart pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/struts.git


The following commit(s) were added to refs/heads/main by this push:
     new 8f9b4b8a9 WW-5636 Harden redirect URL escaping in non-302 response 
body (#1737)
8f9b4b8a9 is described below

commit 8f9b4b8a90fec8df59b037599e57a3f65680c9ef
Author: Arun <[email protected]>
AuthorDate: Sun Jun 14 22:14:43 2026 +0530

    WW-5636 Harden redirect URL escaping in non-302 response body (#1737)
    
    * Implement test for status code 200 with HTML escaping
    
    * Escape HTML in ServletRedirectResult response
    
    Escape HTML in the final location before writing to the response.
---
 .../struts2/result/ServletRedirectResult.java      |  3 ++-
 .../struts2/result/ServletRedirectResultTest.java  | 28 ++++++++++++++++++++++
 2 files changed, 30 insertions(+), 1 deletion(-)

diff --git 
a/core/src/main/java/org/apache/struts2/result/ServletRedirectResult.java 
b/core/src/main/java/org/apache/struts2/result/ServletRedirectResult.java
index 7bc8ab616..18fb93577 100644
--- a/core/src/main/java/org/apache/struts2/result/ServletRedirectResult.java
+++ b/core/src/main/java/org/apache/struts2/result/ServletRedirectResult.java
@@ -26,6 +26,7 @@ import org.apache.struts2.util.reflection.ReflectionException;
 import org.apache.struts2.util.reflection.ReflectionExceptionHandler;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import org.apache.commons.text.StringEscapeUtils;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.apache.struts2.dispatcher.Dispatcher;
@@ -248,7 +249,7 @@ public class ServletRedirectResult extends 
StrutsResultSupport implements Reflec
                 response.setStatus(statusCode);
                 response.setHeader("Location", finalLocation);
                 try {
-                    response.getWriter().write(finalLocation);
+                    
response.getWriter().write(StringEscapeUtils.escapeHtml4(finalLocation));
                 } finally {
                     response.getWriter().close();
                 }
diff --git 
a/core/src/test/java/org/apache/struts2/result/ServletRedirectResultTest.java 
b/core/src/test/java/org/apache/struts2/result/ServletRedirectResultTest.java
index ced68e3d9..fb276d7e1 100644
--- 
a/core/src/test/java/org/apache/struts2/result/ServletRedirectResultTest.java
+++ 
b/core/src/test/java/org/apache/struts2/result/ServletRedirectResultTest.java
@@ -47,6 +47,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import static jakarta.servlet.http.HttpServletResponse.SC_OK;
 import static jakarta.servlet.http.HttpServletResponse.SC_SEE_OTHER;
 import static org.easymock.EasyMock.createControl;
 import static org.easymock.EasyMock.createMock;
@@ -143,6 +144,33 @@ public class ServletRedirectResultTest extends 
StrutsInternalTestCase implements
         assertEquals("/context/bar/foo.jsp", writer.toString());
     }
 
+    public void testStatusCode200LocationIsHtmlEscapedInBody() {
+        String maliciousLocation = 
"/bar/foo.jsp?next=<script>alert(1)</script>&x=1";
+        String expandedLocation = "/context" + maliciousLocation;
+        String expectedBody = 
"/context/bar/foo.jsp?next=&lt;script&gt;alert(1)&lt;/script&gt;&amp;x=1";
+
+        view.setLocation(maliciousLocation);
+        view.setStatusCode(SC_OK);
+        responseMock.expectAndReturn("encodeRedirectURL", expandedLocation, 
expandedLocation);
+        responseMock.expect("setStatus", C.args(C.eq(SC_OK)));
+        responseMock.expect("setHeader", C.args(C.eq("Location"), 
C.eq(expandedLocation)));
+        StringWriter writer = new StringWriter();
+        responseMock.matchAndReturn("getWriter", new PrintWriter(writer));
+
+        try {
+            view.execute(ai);
+            requestMock.verify();
+            responseMock.verify();
+        } catch (Exception e) {
+            e.printStackTrace();
+            fail();
+      }
+
+        assertEquals(expectedBody, writer.toString());
+        assertFalse("response body must not contain a raw <script> tag",
+            writer.toString().contains("<script>"));
+    }
+
     public void testAbsoluteRedirectAnchor() {
         view.setLocation("/bar/foo.jsp");
         view.setAnchor("fragment");

Reply via email to