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

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

commit ee07777d1d30258ae877e7f88381e5988d4a0460
Author: opencode <[email protected]>
AuthorDate: Thu Oct 8 13:59:26 2026 +0200

    Give && higher precedence than || in SSI conditional expressions
    
    The SSI conditional expression parser assigned the same precedence to
    the logical operators, so mixed chains were evaluated strictly left to
    right and "$a || $b && $c" grouped as "($a || $b) && $c" instead of
    the conventionally expected "$a || ($b && $c)", silently inverting the
    logic of such expressions.
    
    Give AndNode and OrNode distinct precedences, with && binding more
    tightly than ||, consistent with the convention used by the C-like
    languages and with Apache HTTP Server expressions. Chains of the same
    operator keep their left-to-right associativity. Note that this changes
    the evaluation of existing documents using unparenthesized mixed && and
    || chains. Document the precedence order in the SSI howto.
---
 java/org/apache/catalina/ssi/ExpressionParseTree.java |  7 ++++---
 .../apache/catalina/ssi/TestExpressionParseTree.java  | 19 +++++++++++++++++++
 webapps/docs/changelog.xml                            | 12 ++++++++++++
 webapps/docs/ssi-howto.xml                            |  6 ++++++
 4 files changed, 41 insertions(+), 3 deletions(-)

diff --git a/java/org/apache/catalina/ssi/ExpressionParseTree.java 
b/java/org/apache/catalina/ssi/ExpressionParseTree.java
index 652d8f9e24..53c7af88f8 100644
--- a/java/org/apache/catalina/ssi/ExpressionParseTree.java
+++ b/java/org/apache/catalina/ssi/ExpressionParseTree.java
@@ -310,7 +310,8 @@ public class ExpressionParseTree {
 
     private static final int PRECEDENCE_NOT = 5;
     private static final int PRECEDENCE_COMPARE = 4;
-    private static final int PRECEDENCE_LOGICAL = 1;
+    private static final int PRECEDENCE_AND = 2;
+    private static final int PRECEDENCE_OR = 1;
 
     /**
      * A node implementation that represents an operation.
@@ -397,7 +398,7 @@ public class ExpressionParseTree {
 
         @Override
         public int getPrecedence() {
-            return PRECEDENCE_LOGICAL;
+            return PRECEDENCE_AND;
         }
 
 
@@ -419,7 +420,7 @@ public class ExpressionParseTree {
 
         @Override
         public int getPrecedence() {
-            return PRECEDENCE_LOGICAL;
+            return PRECEDENCE_OR;
         }
 
 
diff --git a/test/org/apache/catalina/ssi/TestExpressionParseTree.java 
b/test/org/apache/catalina/ssi/TestExpressionParseTree.java
index a635c8258d..4865305a3c 100644
--- a/test/org/apache/catalina/ssi/TestExpressionParseTree.java
+++ b/test/org/apache/catalina/ssi/TestExpressionParseTree.java
@@ -137,6 +137,25 @@ public class TestExpressionParseTree {
     }
 
 
+    @Test
+    public void testAndHasHigherPrecedenceThanOr() throws Exception {
+        SSIExternalResolver r = new TesterSSIExternalResolver();
+        r.setVariableValue("a", "x");
+        r.setVariableValue("b", "");
+        r.setVariableValue("c", "");
+        SSIMediator mediator = new SSIMediator(r, LAST_MODIFIED);
+        // With conventional precedence, this is true: $a || ($b && $c)
+        ExpressionParseTree ept = new ExpressionParseTree("$a || $b && $c", 
mediator);
+        Assert.assertTrue(ept.evaluateTree());
+        // Explicit parentheses give the same result
+        ept = new ExpressionParseTree("$a || ($b && $c)", mediator);
+        Assert.assertTrue(ept.evaluateTree());
+        // The left-to-right grouping evaluates to false: ($a || $b) && $c
+        ept = new ExpressionParseTree("($a || $b) && $c", mediator);
+        Assert.assertFalse(ept.evaluateTree());
+    }
+
+
     @Test
     public void testMissingOperand() throws Exception {
         // Operators missing an operand must produce a parse error rather than
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index fbde229783..970690ded5 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -284,6 +284,18 @@
         now passed to the <code>LoginModule</code> unchanged, consistent with
         the non-JAAS realms. (remm)
       </fix>
+      <fix>
+        Give the SSI conditional expression operators <code>&amp;&amp;</code>
+        and <code>||</code> distinct precedences, <code>&amp;&amp;</code>
+        binding more tightly than <code>||</code>, consistent with the
+        convention used by the C-like languages and by Apache HTTP Server
+        expressions. Previously, mixed chains were evaluated strictly left to
+        right, so <code>$a || $b &amp;&amp; $c</code> was evaluated as
+        <code>($a || $b) &amp;&amp; $c</code> rather than the expected
+        <code>$a || ($b &amp;&amp; $c)</code>. Note that this changes the
+        evaluation of existing documents using unparenthesized mixed
+        <code>&amp;&amp;</code> and <code>||</code> chains. (remm)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Coyote">
diff --git a/webapps/docs/ssi-howto.xml b/webapps/docs/ssi-howto.xml
index 7790307726..e121af40f4 100644
--- a/webapps/docs/ssi-howto.xml
+++ b/webapps/docs/ssi-howto.xml
@@ -219,6 +219,12 @@ is used to assign a value to a user-defined variable.
 <!--#else -->
 <p>Yoga class at noon.</p>
 <!--#endif -->]]></source>
+Operators in conditional expressions are evaluated from highest to lowest
+precedence: parentheses, the <code>!</code> operator, the comparison operators
+(<code>=</code>, <code>!=</code>, <code>&lt;</code>, <code>&gt;</code>,
+<code>&lt;=</code>, <code>&gt;=</code>), <code>&amp;&amp;</code> and finally
+<code>||</code>. For example, <code>$a || $b &amp;&amp; $c</code> is evaluated 
as
+<code>$a || ($b &amp;&amp; $c)</code>.
 </li>
 </ul>
 <p>


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

Reply via email to