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 e966ef0cdf1dbe197bb4616f5eb78fca25ebb6a1
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]

Reply via email to