From b292ca59c0b0e54c1d661b67ed99008458a5478b Mon Sep 17 00:00:00 2001 From: Arun Date: Sat, 11 Jul 2026 05:36:42 +0530 Subject: [PATCH 1/3] Modernize path normalization in Include component and handle edge cases --- .../org/apache/struts2/components/Include.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) 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; From 6b33fdcc56d2ea9ad5d4767f8209139a23f79e12 Mon Sep 17 00:00:00 2001 From: Arun Date: Sat, 11 Jul 2026 05:42:44 +0530 Subject: [PATCH 2/3] Add tests for edge case dot-dot handling in Include path normalization --- .../org/apache/struts2/views/jsp/IncludeTagTest.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) 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 From 09b2470ac3fd591aade8960b06d8d47765533e99 Mon Sep 17 00:00:00 2001 From: Arun Date: Sun, 12 Jul 2026 18:02:23 +0530 Subject: [PATCH 3/3] 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. --- .../main/java/org/apache/struts2/components/Include.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 039b4b03eb..55f0baaf87 100644 --- a/core/src/main/java/org/apache/struts2/components/Include.java +++ b/core/src/main/java/org/apache/struts2/components/Include.java @@ -50,6 +50,7 @@ import java.util.ArrayDeque; import java.util.Deque; import java.util.StringTokenizer; +import java.util.Iterator; /** * @@ -223,8 +224,9 @@ public static String getContextRelativePath(ServletRequest request, String relat StringBuilder flatPathBuffer = new StringBuilder(); - for (String segment : segments) { - flatPathBuffer.append("/").append(segment); + Iterator it = segments.descendingIterator(); + while (it.hasNext()) { + flatPathBuffer.append("/").append(it.next()); } returnValue = flatPathBuffer.length() > 0 ? flatPathBuffer.toString() : "/";