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 678c2091328264bbda465e821565cf54ffc78cde Author: opencode <[email protected]> AuthorDate: Thu Oct 8 14:08:04 2026 +0200 Report a parse error for unterminated literals in SSI expressions An unterminated quoted string in an SSI conditional expression was silently accepted: the scan ran to the end of the expression and the remainder of the expression became the literal value, silently changing the semantics of the expression compared to what the document author intended. Signal this with a new TOKEN_ERROR token type when the closing quote is missing, and convert it into a ParseException in ExpressionParseTree so the failure takes the existing parse error path, stopping the processing of the document at the failing directive. Apply the same treatment to an unterminated regular expression for consistency, replacing the previous remainder-as-literal behavior. --- java/org/apache/catalina/ssi/ExpressionParseTree.java | 3 +++ java/org/apache/catalina/ssi/ExpressionTokenizer.java | 15 ++++++++++++--- java/org/apache/catalina/ssi/LocalStrings.properties | 1 + .../apache/catalina/ssi/TestExpressionParseTree.java | 17 +++++++++++++++++ .../apache/catalina/ssi/TestExpressionTokenizer.java | 14 +++++++++++--- 5 files changed, 44 insertions(+), 6 deletions(-) diff --git a/java/org/apache/catalina/ssi/ExpressionParseTree.java b/java/org/apache/catalina/ssi/ExpressionParseTree.java index cf6dc1afa6..7ba512c1b5 100644 --- a/java/org/apache/catalina/ssi/ExpressionParseTree.java +++ b/java/org/apache/catalina/ssi/ExpressionParseTree.java @@ -241,6 +241,9 @@ public class ExpressionParseTree { break; case ExpressionTokenizer.TOKEN_END: break; + case ExpressionTokenizer.TOKEN_ERROR: + throw new ParseException(sm.getString("expressionParseTree.unterminatedLiteral"), + et.getIndex()); } } // Finish off the rest of the opps diff --git a/java/org/apache/catalina/ssi/ExpressionTokenizer.java b/java/org/apache/catalina/ssi/ExpressionTokenizer.java index 825b82c30a..981a85fe4b 100644 --- a/java/org/apache/catalina/ssi/ExpressionTokenizer.java +++ b/java/org/apache/catalina/ssi/ExpressionTokenizer.java @@ -48,6 +48,8 @@ public class ExpressionTokenizer { public static final int TOKEN_LT = 11; /** Token type indicating end of expression. */ public static final int TOKEN_END = 12; + /** Token type indicating an unterminated string or regular expression. */ + public static final int TOKEN_ERROR = 13; private final char[] expr; private String tokenVal = null; private int index; @@ -176,6 +178,10 @@ public class ExpressionTokenizer { } escaped = false; } + if (index == length) { + // The closing quote is missing + return TOKEN_ERROR; + } end = index; index++; // Skip the end quote } else if (currentChar == '/') { @@ -191,9 +197,12 @@ public class ExpressionTokenizer { } escaped = false; } - // 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); + if (index == length) { + // The closing slash is missing + return TOKEN_ERROR; + } + // The token includes the closing slash + end = ++index; } else { // End is the next whitespace character for (; index < length; index++) { diff --git a/java/org/apache/catalina/ssi/LocalStrings.properties b/java/org/apache/catalina/ssi/LocalStrings.properties index 5cebaec4d0..0fbbc0d9e4 100644 --- a/java/org/apache/catalina/ssi/LocalStrings.properties +++ b/java/org/apache/catalina/ssi/LocalStrings.properties @@ -20,6 +20,7 @@ expressionParseTree.extraNodes=Extra nodes created expressionParseTree.invalidExpression=Invalid expression [{0}] expressionParseTree.missingOperand=Missing operand expressionParseTree.noNodes=No nodes created +expressionParseTree.unterminatedLiteral=Unterminated string or regular expression expressionParseTree.unusedOpCodes=Unused nodes exist ssiCommand.invalidAttribute=Invalid attribute [{0}] diff --git a/test/org/apache/catalina/ssi/TestExpressionParseTree.java b/test/org/apache/catalina/ssi/TestExpressionParseTree.java index 4865305a3c..0a3cbd5bf5 100644 --- a/test/org/apache/catalina/ssi/TestExpressionParseTree.java +++ b/test/org/apache/catalina/ssi/TestExpressionParseTree.java @@ -156,6 +156,23 @@ public class TestExpressionParseTree { } + @Test + public void testUnterminatedLiteral() throws Exception { + // Unterminated quoted strings and regular expressions must produce a + // parse error rather than silently changing the expression semantics + String[] expressions = { "a = \"x", "a = 'x", "a = /x", "\"", "/", "'x = y" }; + for (String expression : expressions) { + SSIMediator mediator = new SSIMediator(new TesterSSIExternalResolver(), LAST_MODIFIED); + try { + new ExpressionParseTree(expression, mediator); + Assert.fail("Expected a parse error for [" + expression + "]"); + } catch (ParseException pe) { + // Expected + } + } + } + + @Test public void testMissingOperand() throws Exception { // Operators missing an operand must produce a parse error rather than diff --git a/test/org/apache/catalina/ssi/TestExpressionTokenizer.java b/test/org/apache/catalina/ssi/TestExpressionTokenizer.java index e4f78cdb55..85bd512612 100644 --- a/test/org/apache/catalina/ssi/TestExpressionTokenizer.java +++ b/test/org/apache/catalina/ssi/TestExpressionTokenizer.java @@ -101,11 +101,19 @@ 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 + // Unterminated regular expression yields an error token parameterSets.add(new Object[] { "a = /x", new int[] { ExpressionTokenizer.TOKEN_STRING, ExpressionTokenizer.TOKEN_EQ, - ExpressionTokenizer.TOKEN_STRING }, - new String[] { "a", null, "/x" } }); + ExpressionTokenizer.TOKEN_ERROR }, + new String[] { "a", null, null } }); + + // Unterminated quoted strings yield an error token + parameterSets.add(new Object[] { "a = \"x", + new int[] { ExpressionTokenizer.TOKEN_STRING, ExpressionTokenizer.TOKEN_EQ, + ExpressionTokenizer.TOKEN_ERROR }, + new String[] { "a", null, null } }); + parameterSets.add(new Object[] { "'x", + new int[] { ExpressionTokenizer.TOKEN_ERROR }, new String[] { null } }); // Operators mixed with strings parameterSets.add(new Object[] { "a && b", --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
