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

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

commit fed76df83198b41730b3938a8ea65654fd84df97
Author: opencode <[email protected]>
AuthorDate: Thu Oct 8 13:44:22 2026 +0200

    Fix StringIndexOutOfBoundsException for unterminated regular expression in 
SSI expressions
    
    When an SSI conditional expression contained a regular expression literal
    without a closing slash, ExpressionTokenizer.nextToken() computed an end
    offset one past the end of the expression, and building the token string
    threw a StringIndexOutOfBoundsException. Since this is not a ParseException,
    it bypassed the error handling of SSIConditional and SSIProcessor and
    surfaced as a 500 error for the whole request.
    
    Cap the token at the end of the expression when the closing slash is
    missing, mirroring the pre-existing behavior for unterminated quoted
    strings. The resulting token (e.g. "/x") is not recognized as a regular
    expression by ExpressionParseTree and is compared as a plain string. Also
    harden the end-of-input check against the index past the end that both
    unterminated cases leave behind, and add a tokenizer test case for the
    unterminated regular expression.
---
 java/org/apache/catalina/ssi/ExpressionTokenizer.java     | 6 ++++--
 test/org/apache/catalina/ssi/TestExpressionTokenizer.java | 5 +++++
 2 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/java/org/apache/catalina/ssi/ExpressionTokenizer.java 
b/java/org/apache/catalina/ssi/ExpressionTokenizer.java
index 36c0d519cd..825b82c30a 100644
--- a/java/org/apache/catalina/ssi/ExpressionTokenizer.java
+++ b/java/org/apache/catalina/ssi/ExpressionTokenizer.java
@@ -109,7 +109,7 @@ public class ExpressionTokenizer {
         }
         // Clear the current token val
         tokenVal = null;
-        if (index == length) {
+        if (index >= length) {
             return TOKEN_END; // End of string
         }
         int start = index;
@@ -191,7 +191,9 @@ public class ExpressionTokenizer {
                 }
                 escaped = false;
             }
-            end = ++index;
+            // If the regular expression was not terminated, the token is the
+            // remainder of the expression, otherwise it includes the closing 
slash
+            end = Math.min(++index, length);
         } else {
             // End is the next whitespace character
             for (; index < length; index++) {
diff --git a/test/org/apache/catalina/ssi/TestExpressionTokenizer.java 
b/test/org/apache/catalina/ssi/TestExpressionTokenizer.java
index eafa517b49..e4f78cdb55 100644
--- a/test/org/apache/catalina/ssi/TestExpressionTokenizer.java
+++ b/test/org/apache/catalina/ssi/TestExpressionTokenizer.java
@@ -101,6 +101,11 @@ public class TestExpressionTokenizer {
         // Escaped slash inside a regular expression
         parameterSets.add(new Object[] { "/a\\/b/",
                 new int[] { ExpressionTokenizer.TOKEN_STRING }, new String[] { 
"/a\\/b/" } });
+        // Unterminated regular expression: the token is the remainder
+        parameterSets.add(new Object[] { "a = /x",
+                new int[] { ExpressionTokenizer.TOKEN_STRING, 
ExpressionTokenizer.TOKEN_EQ,
+                        ExpressionTokenizer.TOKEN_STRING },
+                new String[] { "a", null, "/x" } });
 
         // Operators mixed with strings
         parameterSets.add(new Object[] { "a && b",


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

Reply via email to