diff --git a/core/src/main/java/org/apache/struts2/components/Include.java b/core/src/main/java/org/apache/struts2/components/Include.java index f8decac25f..039b4b03eb 100644 --- a/core/src/main/java/org/apache/struts2/components/Include.java +++ b/core/src/main/java/org/apache/struts2/components/Include.java @@ -47,7 +47,8 @@ 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; /** @@ -203,7 +204,7 @@ public static String getContextRelativePath(ServletRequest request, String relat // .. is illegal in an absolute path according to the Servlet Spec and will cause // known problems on Orion application servers. if (returnValue.contains("..")) { - Stack stack = new Stack<>(); + Deque segments = new ArrayDeque<>(); StringTokenizer pathParts = new StringTokenizer(returnValue.replace('\\', '/'), "/"); while (pathParts.hasMoreTokens()) { @@ -211,20 +212,22 @@ public static String getContextRelativePath(ServletRequest request, String relat 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)); + for (String segment : segments) { + flatPathBuffer.append("/").append(segment); } - returnValue = flatPathBuffer.toString(); + returnValue = flatPathBuffer.length() > 0 ? flatPathBuffer.toString() : "/"; } return returnValue; diff --git a/core/src/test/java/org/apache/struts2/views/jsp/IncludeTagTest.java b/core/src/test/java/org/apache/struts2/views/jsp/IncludeTagTest.java index 7ca1daf127..9ca0587e3e 100644 --- a/core/src/test/java/org/apache/struts2/views/jsp/IncludeTagTest.java +++ b/core/src/test/java/org/apache/struts2/views/jsp/IncludeTagTest.java @@ -220,6 +220,18 @@ public void testIncludeRelative2Dots_clearTagStateSet() throws Exception { 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