mirror of
https://github.com/apache/struts.git
synced 2026-08-05 06:36:58 +00:00
WW-5646 Modernize path normalization in Include component (#1780)
* Modernize path normalization in Include component and handle edge cases * Add tests for edge case dot-dot handling in Include path normalization * Fix reversed path segment order in getContextRelativePath() The for-each loop iterated the ArrayDeque head-to-tail (most recently pushed first), which is the reverse of the old Stack's insertion-order iteration. This caused rebuilt paths like "car/view.jsp" to come out as "view.jsp/car". Use descendingIterator() to restore the original oldest-first ordering when rebuilding the flat path string.
This commit is contained in:
@@ -47,8 +47,10 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* <!-- START SNIPPET: javadoc -->
|
||||
@@ -203,7 +205,7 @@ public class Include extends Component {
|
||||
// .. is illegal in an absolute path according to the Servlet Spec and will cause
|
||||
// known problems on Orion application servers.
|
||||
if (returnValue.contains("..")) {
|
||||
Stack<String> stack = new Stack<>();
|
||||
Deque<String> segments = new ArrayDeque<>();
|
||||
StringTokenizer pathParts = new StringTokenizer(returnValue.replace('\\', '/'), "/");
|
||||
|
||||
while (pathParts.hasMoreTokens()) {
|
||||
@@ -211,20 +213,23 @@ public class Include extends Component {
|
||||
|
||||
if (!part.equals(".")) {
|
||||
if (part.equals("..")) {
|
||||
stack.pop();
|
||||
if (!segments.isEmpty()) {
|
||||
segments.pop();
|
||||
}
|
||||
} else {
|
||||
stack.push(part);
|
||||
segments.push(part);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder flatPathBuffer = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < stack.size(); i++) {
|
||||
flatPathBuffer.append("/").append(stack.elementAt(i));
|
||||
Iterator<String> it = segments.descendingIterator();
|
||||
while (it.hasNext()) {
|
||||
flatPathBuffer.append("/").append(it.next());
|
||||
}
|
||||
|
||||
returnValue = flatPathBuffer.toString();
|
||||
returnValue = flatPathBuffer.length() > 0 ? flatPathBuffer.toString() : "/";
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
|
||||
@@ -220,6 +220,18 @@ public class IncludeTagTest extends AbstractTagTest {
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
public void testGetContextRelativePathExcessDotDot() {
|
||||
// Excess ".." segments beyond root should be silently clamped, not throw EmptyStackException
|
||||
String result = Include.getContextRelativePath(request, "/../../../other/resource.jsp");
|
||||
assertEquals("/other/resource.jsp", result);
|
||||
}
|
||||
|
||||
public void testGetContextRelativePathAllDotDot() {
|
||||
// All segments are ".." - should resolve to root
|
||||
String result = Include.getContextRelativePath(request, "/a/../../..");
|
||||
assertEquals("/", result);
|
||||
}
|
||||
|
||||
public void testIncludeSetUseResponseEncodingTrue() throws Exception {
|
||||
// TODO: If possible in future mock-test an actual content-includes with various encodings
|
||||
// while setting the response encoding to match. Doesn't appear to be possible
|
||||
|
||||
Reference in New Issue
Block a user