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=<script>alert(1)</script>&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");