This is an automated email from the ASF dual-hosted git repository. rmaucher pushed a commit to branch 11.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 925493100c1271d4347d1d533837d985bdbaa5ee Author: opencode <[email protected]> AuthorDate: Thu Oct 8 14:28:57 2026 +0200 Accept the var and value attributes of SSI #set in any order SSISet paired a value attribute with a var attribute seen earlier in the directive, so "<!--#set value=\"bar\" var=\"foo\" -->" which is valid for Apache hit the missing variable error path, wrote the error message and stopped processing, silently dropping the rest of the document. Collect the attributes in a first pass so they can be given in any order as Apache does, with later occurrences of the same attribute winning, and reject unknown attributes during that pass. The other attribute combinations keep their existing behavior. --- java/org/apache/catalina/ssi/SSISet.java | 24 ++-- test/org/apache/catalina/ssi/TestSSISet.java | 158 +++++++++++++++++++++++++++ 2 files changed, 173 insertions(+), 9 deletions(-) diff --git a/java/org/apache/catalina/ssi/SSISet.java b/java/org/apache/catalina/ssi/SSISet.java index f74b4cddf8..5b364d82c0 100644 --- a/java/org/apache/catalina/ssi/SSISet.java +++ b/java/org/apache/catalina/ssi/SSISet.java @@ -53,27 +53,33 @@ public class SSISet implements SSICommand { long lastModified = 0; String errorMessage = ssiMediator.getConfigErrMsg(); String variableName = null; + String variableValue = null; + // Collect the attributes first so that they can be given in any order, as + // Apache does for (int i = 0; i < paramNames.length; i++) { String paramName = paramNames[i]; String paramValue = paramValues[i]; if (paramName.equalsIgnoreCase("var")) { variableName = paramValue; } else if (paramName.equalsIgnoreCase("value")) { - if (variableName != null) { - String substitutedValue = ssiMediator.substituteVariables(paramValue); - ssiMediator.setVariableValue(variableName, substitutedValue); - lastModified = System.currentTimeMillis(); - } else { - ssiMediator.log(sm.getString("ssiSet.noVariable")); - writer.write(errorMessage); - throw new SSIStopProcessingException(); - } + variableValue = paramValue; } else { ssiMediator.log(sm.getString("ssiCommand.invalidAttribute", paramName)); writer.write(errorMessage); throw new SSIStopProcessingException(); } } + if (variableName == null) { + if (variableValue != null) { + ssiMediator.log(sm.getString("ssiSet.noVariable")); + writer.write(errorMessage); + throw new SSIStopProcessingException(); + } + } else if (variableValue != null) { + String substitutedValue = ssiMediator.substituteVariables(variableValue); + ssiMediator.setVariableValue(variableName, substitutedValue); + lastModified = System.currentTimeMillis(); + } return lastModified; } } \ No newline at end of file diff --git a/test/org/apache/catalina/ssi/TestSSISet.java b/test/org/apache/catalina/ssi/TestSSISet.java new file mode 100644 index 0000000000..77e351bf3f --- /dev/null +++ b/test/org/apache/catalina/ssi/TestSSISet.java @@ -0,0 +1,158 @@ +/* + * 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. + */ +package org.apache.catalina.ssi; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import org.junit.Assert; +import org.junit.Test; + +public class TestSSISet { + + @Test + public void testValueBeforeVar() throws Exception { + // The attributes may be given in any order, as Apache does + SSIMediator mediator = newMediator(); + String output = process(mediator, new String[] { "value", "var" }, new String[] { "bar", "foo" }); + Assert.assertEquals("", output); + Assert.assertEquals("bar", mediator.getVariableValue("foo")); + } + + + @Test + public void testVarBeforeValue() throws Exception { + SSIMediator mediator = newMediator(); + String output = process(mediator, new String[] { "var", "value" }, new String[] { "foo", "bar" }); + Assert.assertEquals("", output); + Assert.assertEquals("bar", mediator.getVariableValue("foo")); + } + + + @Test + public void testVarOnly() throws Exception { + // Setting only the variable name does nothing and is not an error + SSIMediator mediator = newMediator(); + String output = process(mediator, new String[] { "var" }, new String[] { "foo" }); + Assert.assertEquals("", output); + Assert.assertNull(mediator.getVariableValue("foo")); + } + + + @Test + public void testValueWithoutVar() throws Exception { + SSIMediator mediator = newMediator(); + String output = process(mediator, new String[] { "value" }, new String[] { "bar" }); + Assert.assertEquals(mediator.getConfigErrMsg(), output); + Assert.assertNull(mediator.getVariableValue("foo")); + } + + + @Test + public void testDuplicateAttributes() throws Exception { + // Later occurrences of an attribute win, regardless of order + SSIMediator mediator = newMediator(); + String output = process(mediator, new String[] { "value", "value", "var" }, + new String[] { "bar1", "bar2", "foo" }); + Assert.assertEquals("", output); + Assert.assertEquals("bar2", mediator.getVariableValue("foo")); + + mediator = newMediator(); + output = process(mediator, new String[] { "var", "value", "value" }, + new String[] { "foo", "bar1", "bar2" }); + Assert.assertEquals("", output); + Assert.assertEquals("bar2", mediator.getVariableValue("foo")); + } + + + @Test + public void testInvalidAttribute() throws Exception { + SSIMediator mediator = newMediator(); + String output = process(mediator, new String[] { "name" }, new String[] { "foo" }); + Assert.assertEquals(mediator.getConfigErrMsg(), output); + } + + + private SSIMediator newMediator() { + return new SSIMediator(new TesterSSIExternalResolver(), 0); + } + + + private String process(SSIMediator mediator, String[] paramNames, String[] paramValues) throws Exception { + StringWriter stringWriter = new StringWriter(); + PrintWriter writer = new PrintWriter(stringWriter); + try { + new SSISet().process(mediator, "set", paramNames, paramValues, writer); + } catch (SSIStopProcessingException e) { + // Expected for the error cases + } + writer.flush(); + return stringWriter.toString(); + } + + /** + * Minimal implementation that provides the bare essentials required for the unit tests. + */ + private static class TesterSSIExternalResolver implements SSIExternalResolver { + + private final Map<String,String> variables = new HashMap<>(); + + @Override + public void addVariableNames(Collection<String> variableNames) { + // NO-OP + } + + @Override + public String getVariableValue(String name) { + return variables.get(name); + } + + @Override + public void setVariableValue(String name, String value) { + variables.put(name, value); + } + + @Override + public Date getCurrentDate() { + return null; + } + + @Override + public long getFileSize(String path, boolean virtual) { + return 0; + } + + @Override + public long getFileLastModified(String path, boolean virtual) { + return 0; + } + + @Override + public String getFileText(String path, boolean virtual) { + return null; + } + + @Override + public void log(String message, Throwable throwable) { + // NO-OP + } + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
