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

rmaucher pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit ba03a7c02941fe265c4ed507102d26c2c7a0bbf1
Author: opencode <[email protected]>
AuthorDate: Sun Oct 18 22:16:49 2026 +0200

    Fix the jsp:attribute trim loop skipping nodes after removal
    
    When removing white-space only template text nodes from the body of a
    jsp:attribute action while parsing a JSP document, the loop in
    JspDocumentParser.endElement() did not re-examine the node that was
    shifted into the vacated position after a removal.
    
    Adjacent template text siblings occur in practice when the attribute
    body contains CDATA sections, because the startCDATA/endCDATA handling
    flushes the buffered characters into a template text node without
    creating a separating node. For such bodies the skipped node could be a
    second white-space only text node that then remained in the body, or the
    final text node which consequently was not trimmed when trim was true.
    
    Re-examine the shifted node after each removal so every text node
    receives the treatment required by the specification. Add a regression
    test using CDATA sections in jsp:attribute bodies.
    
    Found by code review.
---
 .../apache/jasper/compiler/JspDocumentParser.java  |  3 ++
 .../jasper/compiler/TestJspDocumentParser.java     | 23 ++++++++++++++
 test/webapp/jsp/document/named-attribute-trim.jspx | 37 ++++++++++++++++++++++
 webapps/docs/changelog.xml                         | 10 ++++++
 4 files changed, 73 insertions(+)

diff --git a/java/org/apache/jasper/compiler/JspDocumentParser.java 
b/java/org/apache/jasper/compiler/JspDocumentParser.java
index ffebbd3dab..3cda1a7379 100644
--- a/java/org/apache/jasper/compiler/JspDocumentParser.java
+++ b/java/org/apache/jasper/compiler/JspDocumentParser.java
@@ -548,6 +548,9 @@ class JspDocumentParser extends DefaultHandler2 implements 
TagConstants {
                 } else {
                     if (((Node.TemplateText) subElem).isAllSpace()) {
                         subElems.remove(subElem);
+                        // Re-examine the element that was shifted into this
+                        // position by the removal above
+                        i--;
                     }
                 }
             }
diff --git a/test/org/apache/jasper/compiler/TestJspDocumentParser.java 
b/test/org/apache/jasper/compiler/TestJspDocumentParser.java
index 591a5eebab..21275d3186 100644
--- a/test/org/apache/jasper/compiler/TestJspDocumentParser.java
+++ b/test/org/apache/jasper/compiler/TestJspDocumentParser.java
@@ -84,6 +84,29 @@ public class TestJspDocumentParser extends TomcatBaseTest {
         Assert.assertEquals(HttpServletResponse.SC_OK, rc);
     }
 
+    @Test
+    public void testNamedAttributeTrimCdata() throws Exception {
+        getTomcatInstanceTestWebapp(false, true);
+
+        ByteChunk bc = new ByteChunk();
+        int rc = getUrl("http://localhost:"; + getPort() + 
"/test/jsp/document/named-attribute-trim.jspx", bc, null);
+
+        Assert.assertEquals(HttpServletResponse.SC_OK, rc);
+
+        String result = bc.toString();
+
+        // White-space only text nodes between the other body elements must be
+        // removed and the final text node must be trimmed
+        Assert.assertTrue(result, result.contains("<e1 a=\"x tail\"/>"));
+        Assert.assertTrue(result, result.contains("<e2 a=\"x\"/>"));
+        Assert.assertTrue(result, result.contains("<e3 a=\"xy\"/>"));
+        Assert.assertTrue(result, result.contains("<e4 a=\"x y\"/>"));
+        // With trim set to false, leading and trailing white-space only text
+        // nodes must be kept verbatim
+        Assert.assertTrue(result, result.contains("<t5 a=\"  \"/>"));
+        Assert.assertTrue(result, result.contains("<t6 a=\"x tail \"/>"));
+    }
+
     @Test
     public void testSchemaValidation() throws Exception {
         getTomcatInstanceTestWebapp(false, true);
diff --git a/test/webapp/jsp/document/named-attribute-trim.jspx 
b/test/webapp/jsp/document/named-attribute-trim.jspx
new file mode 100644
index 0000000000..5422dc524e
--- /dev/null
+++ b/test/webapp/jsp/document/named-attribute-trim.jspx
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"; version="2.0">
+  <!--
+    Regression tests for the handling of adjacent template text nodes -
+    produced by CDATA sections - in the body of a jsp:attribute action.
+    Expected results are given in comments below. Each element name encodes
+    the trim setting: e* for trim="true" (the default), t* for trim="false".
+  -->
+  <!-- Middle white-space only node removed, trailing node trimmed: "x tail" 
-->
+  <jsp:element name="e1"><jsp:attribute 
name="a"><jsp:text>x</jsp:text><![CDATA[ ]]><![CDATA[ tail 
]]></jsp:attribute></jsp:element>
+  <!-- Both consecutive white-space only middle nodes removed: "x" -->
+  <jsp:element name="e2"><jsp:attribute 
name="a"><jsp:text>x</jsp:text><![CDATA[ ]]><![CDATA[ 
]]></jsp:attribute></jsp:element>
+  <!-- Second of two consecutive white-space only middle nodes removed: "xy" 
-->
+  <jsp:element name="e3"><jsp:attribute 
name="a"><jsp:text>x</jsp:text><![CDATA[ ]]><![CDATA[ 
]]><jsp:text>y</jsp:text></jsp:attribute></jsp:element>
+  <!-- Single CDATA between text nodes, trailing node trimmed: "x y" -->
+  <jsp:element name="e4"><jsp:attribute name="a">x<![CDATA[ ]]> y 
</jsp:attribute></jsp:element>
+  <!-- Leading and trailing white-space only nodes kept verbatim: "  " -->
+  <jsp:element name="t5"><jsp:attribute name="a" trim="false"><![CDATA[ 
]]><![CDATA[ ]]></jsp:attribute></jsp:element>
+  <!-- Middle node removed, trailing node kept verbatim: "x tail " -->
+  <jsp:element name="t6"><jsp:attribute name="a" trim="false">x<![CDATA[ 
]]><![CDATA[ tail ]]></jsp:attribute></jsp:element>
+</jsp:root>
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 71c9b266ee..a056f84ac1 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -400,6 +400,16 @@
         silently omitted from processing. Option parsing now stops at the
         first full stop argument wherever it appears. (remm)
       </fix>
+      <fix>
+        When removing white-space only template text nodes from the body of a
+        <code>jsp:attribute</code> action while parsing a JSP document, the
+        node that was shifted into the vacated position is now re-examined.
+        Previously it was skipped, which, for bodies containing adjacent
+        template text nodes such as those produced by CDATA sections, could
+        leave a white-space only text node in the body or prevent the final
+        text node from being trimmed when <code>trim</code> was
+        <code>true</code>. (remm)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="WebSocket">


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to