This is an automated email from the ASF dual-hosted git repository.

lukaszlenart pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/struts.git


The following commit(s) were added to refs/heads/main by this push:
     new de599c91d WW-5646 Modernize path normalization in Include component 
(#1780)
de599c91d is described below

commit de599c91d3519d216c4e048fc207717881a6144c
Author: Arun <[email protected]>
AuthorDate: Fri Jul 17 15:48:08 2026 +0530

    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.
---
 .../java/org/apache/struts2/components/Include.java   | 19 ++++++++++++-------
 .../org/apache/struts2/views/jsp/IncludeTagTest.java  | 12 ++++++++++++
 2 files changed, 24 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 f8decac25..55f0baaf8 100644
--- a/core/src/main/java/org/apache/struts2/components/Include.java
+++ b/core/src/main/java/org/apache/struts2/components/Include.java
@@ -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;
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 7ca1daf12..9ca0587e3 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 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

Reply via email to