diff --git a/core/src/main/java/org/apache/struts2/result/PostbackResult.java b/core/src/main/java/org/apache/struts2/result/PostbackResult.java index f46f7c52c3..519feee9db 100644 --- a/core/src/main/java/org/apache/struts2/result/PostbackResult.java +++ b/core/src/main/java/org/apache/struts2/result/PostbackResult.java @@ -23,6 +23,7 @@ import org.apache.struts2.inject.Inject; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.text.StringEscapeUtils; import org.apache.struts2.dispatcher.mapper.ActionMapper; import org.apache.struts2.dispatcher.mapper.ActionMapping; @@ -104,7 +105,7 @@ protected void doExecute(String finalLocation, ActionInvocation invocation) thro // Render PrintWriter pw = new PrintWriter(response.getOutputStream()); - pw.write("
"); + pw.write(""); writeFormElements(request, pw); writePrologueScript(pw); pw.write(""); diff --git a/core/src/test/java/org/apache/struts2/result/PostbackResultTest.java b/core/src/test/java/org/apache/struts2/result/PostbackResultTest.java index 2bd310985e..c17bfdbd3b 100644 --- a/core/src/test/java/org/apache/struts2/result/PostbackResultTest.java +++ b/core/src/test/java/org/apache/struts2/result/PostbackResultTest.java @@ -145,5 +145,108 @@ public void testPassingNullInvocation() throws Exception{ } } + /** + * WW-5623: Verify that HTML special characters in finalLocation are properly + * escaped in the rendered form action attribute to prevent XSS. + */ + public void testFormActionHtmlEscaping() throws Exception { + ActionContext context = ActionContext.getContext(); + ValueStack stack = context.getValueStack(); + MockHttpServletRequest req = new MockHttpServletRequest(); + MockHttpServletResponse res = new MockHttpServletResponse(); + context.put(ServletActionContext.HTTP_REQUEST, req); + context.put(ServletActionContext.HTTP_RESPONSE, res); + + // Push an object with a malicious property onto the value stack + stack.push(new Object() { + public String getTargetUrl() { + return "/test\"onmouseover=\"alert(1)"; + } + }); + + PostbackResult result = new PostbackResult(); + result.setLocation("/redirect?url=${targetUrl}"); + result.setPrependServletContext(false); + + IMocksControl control = createControl(); + ActionInvocation mockInvocation = control.createMock(ActionInvocation.class); + expect(mockInvocation.getInvocationContext()).andReturn(context).anyTimes(); + expect(mockInvocation.getStack()).andReturn(stack).anyTimes(); + + control.replay(); + result.setActionMapper(container.getInstance(ActionMapper.class)); + + // Call doExecute directly with a malicious location containing all critical chars + result.doExecute("/test\"onmouseover=\"alert(1)\"¶m=