This is an automated email from the ASF dual-hosted git repository.
markt-asf pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/main by this push:
new 48a5927b86 Follow up to SSI fixes
48a5927b86 is described below
commit 48a5927b86d150aa2bccf377419028342efb3f00
Author: Mark Thomas <[email protected]>
AuthorDate: Mon Jun 29 14:16:26 2026 +0100
Follow up to SSI fixes
- Avoid AIOOB exception if no command is provided for exec
- Log if the maximum substitutions limit is exceeded
- Reduce chance of deadlock when reading stdout/stderr with exec
- Handle entities from the supplementary plane - fix and test - Opus 4.8
---
java/org/apache/catalina/ssi/LocalStrings.properties | 2 ++
java/org/apache/catalina/ssi/SSIExec.java | 5 ++++-
java/org/apache/catalina/ssi/SSIMediator.java | 7 ++++---
.../org/apache/catalina/ssi/TestExpressionParseTree.java | 16 ++++++++++++++++
4 files changed, 26 insertions(+), 4 deletions(-)
diff --git a/java/org/apache/catalina/ssi/LocalStrings.properties
b/java/org/apache/catalina/ssi/LocalStrings.properties
index 7d54faa2c7..599fa44f20 100644
--- a/java/org/apache/catalina/ssi/LocalStrings.properties
+++ b/java/org/apache/catalina/ssi/LocalStrings.properties
@@ -23,6 +23,7 @@ ssiCommand.invalidAttribute=Invalid attribute [{0}]
ssiEcho.invalidEncoding=Invalid encoding [{0}]
ssiExec.executeFailed=Cannot execute file [{0}]
+ssiExec.noCommand=No command was provided for SSI exec
ssiFlastmod.noLastModified=Cannot get last modification date for file [{0}]
@@ -31,6 +32,7 @@ ssiFsize.noSize=Cannot get size for file [{0}]
ssiInclude.includeFailed=Cannot include file [{0}]
+ssiMediator.maxSubstitutionsExceeded=The limit of [{0}] substitutions per
expression has been exceeded
ssiMediator.unknownEncoding=Unknown encoding [{0}]
ssiServletExternalResolver.absoluteNonVirtualPath=Non virtual [{0}] path
cannot be absolute
diff --git a/java/org/apache/catalina/ssi/SSIExec.java
b/java/org/apache/catalina/ssi/SSIExec.java
index feec1c7fe7..9090ece48b 100644
--- a/java/org/apache/catalina/ssi/SSIExec.java
+++ b/java/org/apache/catalina/ssi/SSIExec.java
@@ -90,6 +90,9 @@ public class SSIExec implements SSICommand {
tokens.add(current.toString());
}
String[] cmdArray = tokens.toArray(new String[0]);
+ if (cmdArray.length == 0) {
+ throw new IOException(sm.getString("ssiExec.noCommand"));
+ }
Process proc = rt.exec(cmdArray);
foundProgram = true;
char[] buf = new char[BUFFER_SIZE];
@@ -99,8 +102,8 @@ public class SSIExec implements SSICommand {
new
InputStreamReader(proc.getErrorStream()))) {
// We don't spawn a thread here, since this is costly.
stderr would be usually written
// right away and the amount written would be small
- IOTools.flow(stdErrReader, writer, buf);
IOTools.flow(stdOutReader, writer, buf);
+ IOTools.flow(stdErrReader, writer, buf);
}
proc.waitFor();
} finally {
diff --git a/java/org/apache/catalina/ssi/SSIMediator.java
b/java/org/apache/catalina/ssi/SSIMediator.java
index 7d0310fe88..bac7bb4a48 100644
--- a/java/org/apache/catalina/ssi/SSIMediator.java
+++ b/java/org/apache/catalina/ssi/SSIMediator.java
@@ -346,10 +346,10 @@ public class SSIMediator {
boolean processed = false;
try {
int codePoint = Integer.parseInt(sb.substring(charStart +
2, charEnd));
- if (codePoint >= 0 && codePoint <= 0xFFFF) {
- char c = (char) codePoint;
+ if (Character.isValidCodePoint(codePoint)) {
+ // Use toChars() so code points outside the BMP are
inserted as a surrogate pair
sb.delete(charStart, charEnd + 1);
- sb.insert(charStart, c);
+ sb.insert(charStart, Character.toChars(codePoint));
processed = true;
}
} catch (NumberFormatException e) {
@@ -440,6 +440,7 @@ public class SSIMediator {
// count prevents infinite loops from circular variable references
// (e.g. X="$Y" and Y="$X")
if (++substitutionCount > maxSubstitutions) {
+ log(sm.getString("ssiMediator.maxSubstitutionsExceeded",
Integer.toString(maxSubstitutions)));
break;
}
i = start;
diff --git a/test/org/apache/catalina/ssi/TestExpressionParseTree.java
b/test/org/apache/catalina/ssi/TestExpressionParseTree.java
index 18ccebb9a2..14b63233da 100644
--- a/test/org/apache/catalina/ssi/TestExpressionParseTree.java
+++ b/test/org/apache/catalina/ssi/TestExpressionParseTree.java
@@ -208,6 +208,22 @@ public class TestExpressionParseTree {
Assert.assertEquals("$Yresult", mediator.substituteVariables(input));
}
+
+ @Test
+ public void testSubstituteVariablesNumericEntityBmp() throws Exception {
+ // A numeric character reference in the BMP is decoded
+ SSIMediator mediator = new SSIMediator(new
TesterSSIExternalResolver(), LAST_MODIFIED);
+ Assert.assertEquals("A", mediator.substituteVariables("A"));
+ }
+
+
+ @Test
+ public void testSubstituteVariablesNumericEntitySupplementary() throws
Exception {
+ // A numeric character reference outside the BMP (U+1F600 = surrogate
pair \uD83D\uDE00) must be decoded
+ SSIMediator mediator = new SSIMediator(new
TesterSSIExternalResolver(), LAST_MODIFIED);
+ Assert.assertEquals("\uD83D\uDE00",
mediator.substituteVariables("😀"));
+ }
+
/**
* Minimal implementation that provides the bare essentials require for
the unit tests.
*/
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]