You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by lu...@apache.org on 2013/06/06 07:29:49 UTC

svn commit: r1490149 - in /struts/struts2/trunk: ./ core/src/main/java/org/apache/struts2/ core/src/main/java/org/apache/struts2/dispatcher/mapper/ core/src/test/java/org/apache/struts2/dispatcher/mapper/ xwork-core/src/main/java/com/opensymphony/xwork...

Author: lukaszlenart
Date: Thu Jun  6 05:29:48 2013
New Revision: 1490149

URL: http://svn.apache.org/r1490149
Log:
Merged from STRUTS_2_3_14_2_X
WW-4090 Itroduces actions names' whitelisting [from revision 1488895]
WW-4090 Removes double evaluation of parsed expression [from revision 1488897]
WW-4090 Add some logging [from revision 1488899]
WW-4090 Uses warn level instead of debug [from revision 1488900]

Modified:
    struts/struts2/trunk/   (props changed)
    struts/struts2/trunk/core/src/main/java/org/apache/struts2/StrutsConstants.java
    struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapper.java
    struts/struts2/trunk/core/src/test/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapperTest.java
    struts/struts2/trunk/xwork-core/src/main/java/com/opensymphony/xwork2/util/OgnlTextParser.java
    struts/struts2/trunk/xwork-core/src/test/java/com/opensymphony/xwork2/util/TextParseUtilTest.java

Propchange: struts/struts2/trunk/
------------------------------------------------------------------------------
  Merged /struts/struts2/branches/STRUTS_2_3_14_2_X:r1488895,1488897,1488899-1488900

Modified: struts/struts2/trunk/core/src/main/java/org/apache/struts2/StrutsConstants.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/StrutsConstants.java?rev=1490149&r1=1490148&r2=1490149&view=diff
==============================================================================
--- struts/struts2/trunk/core/src/main/java/org/apache/struts2/StrutsConstants.java (original)
+++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/StrutsConstants.java Thu Jun  6 05:29:48 2013
@@ -252,4 +252,7 @@ public final class StrutsConstants {
 
     public static final String STRUTS_EXPRESSION_PARSER = "struts.expression.parser";
 
+    /** actions names' whitelist **/
+    public static final String STRUTS_ALLOWED_ACTION_NAMES = "struts.allowed.action.names";
+
 }

Modified: struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapper.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapper.java?rev=1490149&r1=1490148&r2=1490149&view=diff
==============================================================================
--- struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapper.java (original)
+++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapper.java Thu Jun  6 05:29:48 2013
@@ -27,6 +27,8 @@ import com.opensymphony.xwork2.config.Co
 import com.opensymphony.xwork2.config.entities.PackageConfig;
 import com.opensymphony.xwork2.inject.Container;
 import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.struts2.RequestUtils;
 import org.apache.struts2.ServletActionContext;
@@ -35,12 +37,7 @@ import org.apache.struts2.dispatcher.Ser
 import org.apache.struts2.util.PrefixTrie;
 
 import javax.servlet.http.HttpServletRequest;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 
 /**
  * <!-- START SNIPPET: javadoc -->
@@ -162,6 +159,8 @@ import java.util.Set;
  */
 public class DefaultActionMapper implements ActionMapper {
 
+    private static final Logger LOG = LoggerFactory.getLogger(DefaultActionMapper.class);
+
     protected static final String METHOD_PREFIX = "method:";
     protected static final String ACTION_PREFIX = "action:";
     protected static final String REDIRECT_PREFIX = "redirect:";
@@ -171,6 +170,7 @@ public class DefaultActionMapper impleme
     protected boolean allowSlashesInActionNames = false;
     protected boolean alwaysSelectFullNamespace = false;
     protected PrefixTrie prefixTrie = null;
+    protected String allowedActionNames = "[a-z]*[A-Z]*[0-9]*[.\\-_!/]*";
 
     protected List<String> extensions = new ArrayList<String>() {{
         add("action");
@@ -260,6 +260,11 @@ public class DefaultActionMapper impleme
         this.alwaysSelectFullNamespace = "true".equals(val);
     }
 
+    @Inject(value = StrutsConstants.STRUTS_ALLOWED_ACTION_NAMES, required = false)
+    public void setAllowedActionNames(String allowedActionNames) {
+        this.allowedActionNames = allowedActionNames;
+    }
+
     @Inject
     public void setContainer(Container container) {
         this.container = container;
@@ -417,7 +422,32 @@ public class DefaultActionMapper impleme
         }
 
         mapping.setNamespace(namespace);
-        mapping.setName(name);
+        mapping.setName(cleanupActionName(name));
+    }
+
+    /**
+     * Cleans up action name from suspicious characters
+     *
+     * @param rawActionName action name extracted from URI
+     * @return safe action name
+     */
+    protected String cleanupActionName(final String rawActionName) {
+        if (rawActionName.matches(allowedActionNames)) {
+            return rawActionName;
+        } else {
+            if (LOG.isWarnEnabled()) {
+                LOG.warn("Action [#0] do not match allowed action names pattern [#1], cleaning it up!",
+                        rawActionName, allowedActionNames);
+            }
+            String cleanActionName = rawActionName;
+            for(String chunk : rawActionName.split(allowedActionNames)) {
+                cleanActionName = cleanActionName.replace(chunk, "");
+            }
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Cleaned action name [#0]", cleanActionName);
+            }
+            return cleanActionName;
+        }
     }
 
     /**

Modified: struts/struts2/trunk/core/src/test/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapperTest.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/test/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapperTest.java?rev=1490149&r1=1490148&r2=1490149&view=diff
==============================================================================
--- struts/struts2/trunk/core/src/test/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapperTest.java (original)
+++ struts/struts2/trunk/core/src/test/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapperTest.java Thu Jun  6 05:29:48 2013
@@ -747,4 +747,23 @@ public class DefaultActionMapperTest ext
 
     }
 
+    public void testAllowedActionNames() throws Exception {
+        DefaultActionMapper mapper = new DefaultActionMapper();
+
+        String actionName = "action";
+        assertEquals(actionName, mapper.cleanupActionName(actionName));
+
+        actionName = "${action}";
+        assertEquals("action", mapper.cleanupActionName(actionName));
+
+        actionName = "${${%{action}}}";
+        assertEquals("action", mapper.cleanupActionName(actionName));
+
+        actionName = "${#foo='action',#foo}";
+        assertEquals("fooactionfoo", mapper.cleanupActionName(actionName));
+
+        actionName = "test-action";
+        assertEquals("test-action", mapper.cleanupActionName(actionName));
+    }
+
 }

Modified: struts/struts2/trunk/xwork-core/src/main/java/com/opensymphony/xwork2/util/OgnlTextParser.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/xwork-core/src/main/java/com/opensymphony/xwork2/util/OgnlTextParser.java?rev=1490149&r1=1490148&r2=1490149&view=diff
==============================================================================
--- struts/struts2/trunk/xwork-core/src/main/java/com/opensymphony/xwork2/util/OgnlTextParser.java (original)
+++ struts/struts2/trunk/xwork-core/src/main/java/com/opensymphony/xwork2/util/OgnlTextParser.java Thu Jun  6 05:29:48 2013
@@ -11,17 +11,16 @@ public class OgnlTextParser implements T
         // deal with the "pure" expressions first!
         //expression = expression.trim();
         Object result = expression;
+        int pos = 0;
+
         for (char open : openChars) {
             int loopCount = 1;
-            int pos = 0;
-
             //this creates an implicit StringBuffer and shouldn't be used in the inner loop
             final String lookupChars = open + "{";
 
             while (true) {
                 int start = expression.indexOf(lookupChars, pos);
                 if (start == -1) {
-                    pos = 0;
                     loopCount++;
                     start = expression.indexOf(lookupChars);
                 }

Modified: struts/struts2/trunk/xwork-core/src/test/java/com/opensymphony/xwork2/util/TextParseUtilTest.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/xwork-core/src/test/java/com/opensymphony/xwork2/util/TextParseUtilTest.java?rev=1490149&r1=1490148&r2=1490149&view=diff
==============================================================================
--- struts/struts2/trunk/xwork-core/src/test/java/com/opensymphony/xwork2/util/TextParseUtilTest.java (original)
+++ struts/struts2/trunk/xwork-core/src/test/java/com/opensymphony/xwork2/util/TextParseUtilTest.java Thu Jun  6 05:29:48 2013
@@ -97,6 +97,24 @@ public class TextParseUtilTest extends X
         assertEquals("count must be between 123 and 456, current value is 98765.", s);
     }
 
+    public void testNestedExpression() throws Exception {
+        ValueStack stack = ActionContext.getContext().getValueStack();
+        stack.push(new HashMap<String, Object>() {{ put("foo", "${%{1+1}}"); }});
+        String s = TextParseUtil.translateVariables("${foo}", stack);
+        assertEquals("${%{1+1}}", s);
+        stack.pop();
+    }
+
+    public void testMixedOpenChars() throws Exception {
+        ValueStack stack = ActionContext.getContext().getValueStack();
+        stack.push(new HashMap<String, Object>() {{ put("foo", "bar"); }});
+        String s = TextParseUtil.translateVariables("${foo}-%{foo}", stack);
+        assertEquals("bar-bar", s);
+        s = TextParseUtil.translateVariables("%{foo}-${foo}", stack);
+        assertEquals("%{foo}-bar", s); // this is bad, but it is the only way not to double evaluate passed expression
+        stack.pop();
+    }
+
     public void testCommaDelimitedStringToSet() {
         assertEquals(0, TextParseUtil.commaDelimitedStringToSet("").size());
         assertEquals(new HashSet<String>(Arrays.asList("foo", "bar", "tee")),
@@ -132,10 +150,13 @@ public class TextParseUtilTest extends X
 
     public void testTranslateVariablesRecursive() {
         ValueStack stack = ActionContext.getContext().getValueStack();
-        stack.push(new HashMap<String, Object>() {{ put("foo", "${1+1}"); }});
+        stack.push(new HashMap<String, Object>() {{ put("foo", "${1+1}"); put("bar", "${${1+2}}"); }});
 
         Object s = TextParseUtil.translateVariables('$', "foo: ${foo}", stack, String.class, null, 2);
         assertEquals("foo: 2", s);
+
+        s = TextParseUtil.translateVariables('$', "foo: ${bar}", stack, String.class, null, 1);
+        assertEquals("foo: ${${1+2}}", s);
     }
 
     public void testTranslateVariablesWithNull() {