Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.inject.Inject;
import org.apache.commons.text.StringEscapeUtils;
import org.apache.struts2.dispatcher.mapper.ActionMapper;
import org.apache.struts2.dispatcher.mapper.ActionMapping;

Expand Down Expand Up @@ -101,7 +102,7 @@ protected void doExecute(String finalLocation, ActionInvocation invocation) thro

// Render
PrintWriter pw = new PrintWriter(response.getOutputStream());
pw.write("<!DOCTYPE html><html><body><form action=\"" + finalLocation + "\" method=\"POST\">");
pw.write("<!DOCTYPE html><html><body><form action=\"" + StringEscapeUtils.escapeHtml4(finalLocation) + "\" method=\"POST\">");
writeFormElements(request, pw);
writePrologueScript(pw);
pw.write("</html>");
Expand Down
103 changes: 103 additions & 0 deletions core/src/test/java/org/apache/struts2/result/PostbackResultTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -146,5 +146,108 @@
}
}

/**
* WW-5623: Verify that HTML special characters in finalLocation are properly
* escaped in the rendered form action attribute.
*/
public void testFormActionHtmlEscaping() throws Exception {
ActionContext context = ActionContext.getContext();

Check warning on line 154 in core/src/test/java/org/apache/struts2/result/PostbackResultTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "ActionContext"; it is deprecated.

See more on https://sonarcloud.io/project/issues?id=apache_struts&issues=AZ5EL55PpmbvQSOQFgqy&open=AZ5EL55PpmbvQSOQFgqy&pullRequest=1701

Check warning on line 154 in core/src/test/java/org/apache/struts2/result/PostbackResultTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "ActionContext"; it is deprecated.

See more on https://sonarcloud.io/project/issues?id=apache_struts&issues=AZ5EL55PpmbvQSOQFgqz&open=AZ5EL55PpmbvQSOQFgqz&pullRequest=1701
ValueStack stack = context.getValueStack();

Check warning on line 155 in core/src/test/java/org/apache/struts2/result/PostbackResultTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "ValueStack"; it is deprecated.

See more on https://sonarcloud.io/project/issues?id=apache_struts&issues=AZ5EL55PpmbvQSOQFgq0&open=AZ5EL55PpmbvQSOQFgq0&pullRequest=1701
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);

Check warning on line 173 in core/src/test/java/org/apache/struts2/result/PostbackResultTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "ActionInvocation"; it is deprecated.

See more on https://sonarcloud.io/project/issues?id=apache_struts&issues=AZ5EL55PpmbvQSOQFgq2&open=AZ5EL55PpmbvQSOQFgq2&pullRequest=1701

Check warning on line 173 in core/src/test/java/org/apache/struts2/result/PostbackResultTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "ActionInvocation"; it is deprecated.

See more on https://sonarcloud.io/project/issues?id=apache_struts&issues=AZ5EL55PpmbvQSOQFgq1&open=AZ5EL55PpmbvQSOQFgq1&pullRequest=1701
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)\"&param=<script>", mockInvocation);

String output = res.getContentAsString();

// The action attribute must contain escaped HTML entities
assertTrue("Double quote should be escaped to &quot;",
output.contains("action=\"/test&quot;onmouseover=&quot;alert(1)&quot;&amp;param=&lt;script&gt;\""));
// Must not contain unescaped double-quote that breaks out of the attribute
assertFalse("Raw double-quote must not appear in action value",
output.contains("action=\"/test\""));

control.verify();
}

/**
* WW-5623: Verify that each individual HTML special character is properly escaped.
*/
public void testFormActionEscapesAllHtmlSpecialChars() throws Exception {
ActionContext context = ActionContext.getContext();

Check warning on line 199 in core/src/test/java/org/apache/struts2/result/PostbackResultTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "ActionContext"; it is deprecated.

See more on https://sonarcloud.io/project/issues?id=apache_struts&issues=AZ5EL55PpmbvQSOQFgq3&open=AZ5EL55PpmbvQSOQFgq3&pullRequest=1701

Check warning on line 199 in core/src/test/java/org/apache/struts2/result/PostbackResultTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "ActionContext"; it is deprecated.

See more on https://sonarcloud.io/project/issues?id=apache_struts&issues=AZ5EL55PpmbvQSOQFgq4&open=AZ5EL55PpmbvQSOQFgq4&pullRequest=1701
MockHttpServletRequest req = new MockHttpServletRequest();
MockHttpServletResponse res = new MockHttpServletResponse();
context.put(ServletActionContext.HTTP_REQUEST, req);
context.put(ServletActionContext.HTTP_RESPONSE, res);

IMocksControl control = createControl();
ActionInvocation mockInvocation = control.createMock(ActionInvocation.class);

Check warning on line 206 in core/src/test/java/org/apache/struts2/result/PostbackResultTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "ActionInvocation"; it is deprecated.

See more on https://sonarcloud.io/project/issues?id=apache_struts&issues=AZ5EL55PpmbvQSOQFgq6&open=AZ5EL55PpmbvQSOQFgq6&pullRequest=1701

Check warning on line 206 in core/src/test/java/org/apache/struts2/result/PostbackResultTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "ActionInvocation"; it is deprecated.

See more on https://sonarcloud.io/project/issues?id=apache_struts&issues=AZ5EL55PpmbvQSOQFgq5&open=AZ5EL55PpmbvQSOQFgq5&pullRequest=1701
expect(mockInvocation.getInvocationContext()).andReturn(context).anyTimes();

control.replay();

PostbackResult result = new PostbackResult();
result.setActionMapper(container.getInstance(ActionMapper.class));
result.doExecute("/path?a=1&b=2\"<>", mockInvocation);

String output = res.getContentAsString();

assertTrue("Ampersand should be escaped", output.contains("&amp;"));
assertTrue("Double-quote should be escaped", output.contains("&quot;"));
assertTrue("Less-than should be escaped", output.contains("&lt;"));
assertTrue("Greater-than should be escaped", output.contains("&gt;"));

control.verify();
}

/**
* WW-5623: Verify that a clean location (no special chars) renders unchanged.
*/
public void testFormActionCleanLocationUnchanged() throws Exception {
ActionContext context = ActionContext.getContext();

Check warning on line 229 in core/src/test/java/org/apache/struts2/result/PostbackResultTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "ActionContext"; it is deprecated.

See more on https://sonarcloud.io/project/issues?id=apache_struts&issues=AZ5EL55PpmbvQSOQFgq8&open=AZ5EL55PpmbvQSOQFgq8&pullRequest=1701

Check warning on line 229 in core/src/test/java/org/apache/struts2/result/PostbackResultTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "ActionContext"; it is deprecated.

See more on https://sonarcloud.io/project/issues?id=apache_struts&issues=AZ5EL55PpmbvQSOQFgq7&open=AZ5EL55PpmbvQSOQFgq7&pullRequest=1701
MockHttpServletRequest req = new MockHttpServletRequest();
MockHttpServletResponse res = new MockHttpServletResponse();
context.put(ServletActionContext.HTTP_REQUEST, req);
context.put(ServletActionContext.HTTP_RESPONSE, res);

IMocksControl control = createControl();
ActionInvocation mockInvocation = control.createMock(ActionInvocation.class);

Check warning on line 236 in core/src/test/java/org/apache/struts2/result/PostbackResultTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "ActionInvocation"; it is deprecated.

See more on https://sonarcloud.io/project/issues?id=apache_struts&issues=AZ5EL55PpmbvQSOQFgq-&open=AZ5EL55PpmbvQSOQFgq-&pullRequest=1701

Check warning on line 236 in core/src/test/java/org/apache/struts2/result/PostbackResultTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "ActionInvocation"; it is deprecated.

See more on https://sonarcloud.io/project/issues?id=apache_struts&issues=AZ5EL55PpmbvQSOQFgq9&open=AZ5EL55PpmbvQSOQFgq9&pullRequest=1701
expect(mockInvocation.getInvocationContext()).andReturn(context).anyTimes();

control.replay();

PostbackResult result = new PostbackResult();
result.setActionMapper(container.getInstance(ActionMapper.class));
result.doExecute("/clean/path/action.do", mockInvocation);

String output = res.getContentAsString();

assertTrue("Clean location should render as-is in action attribute",
output.contains("action=\"/clean/path/action.do\""));

control.verify();
}

}
Loading