You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by ya...@apache.org on 2021/07/09 06:16:12 UTC

[struts] 01/01: fix double evaluations

This is an automated email from the ASF dual-hosted git repository.

yasserzamani pushed a commit to branch fix/double_evaluations_2_5
in repository https://gitbox.apache.org/repos/asf/struts.git

commit 8d6e26e0feb8cb1669f45a66e458860534b94571
Author: Yasser Zamani <ya...@apache.org>
AuthorDate: Fri Jul 9 10:45:22 2021 +0430

    fix double evaluations
    
    address known issues reported at https://securitylab.github.com/research/apache-struts-double-evaluation/
---
 .../providers/XWorkConfigurationProvider.java      |   4 +
 .../xwork2/interceptor/AliasInterceptor.java       |  85 ++++++-
 .../com/opensymphony/xwork2/mock/MockResult.java   |   7 +-
 .../xwork2/security/AcceptedPatternsChecker.java   |   2 +-
 .../DefaultNotExcludedAcceptedPatternsChecker.java | 105 +++++++++
 .../NotExcludedAcceptedPatternsChecker.java        |  70 ++++++
 .../java/org/apache/struts2/StrutsConstants.java   |   1 +
 .../org/apache/struts2/components/Component.java   |  42 +++-
 .../org/apache/struts2/components/FormButton.java  |   1 +
 .../java/org/apache/struts2/components/Param.java  |  29 ++-
 .../struts2/components/ServletUrlRenderer.java     |   8 +-
 .../java/org/apache/struts2/components/UIBean.java | 123 +++++-----
 .../config/DefaultBeanSelectionProvider.java       |   3 +
 .../org/apache/struts2/result/StreamResult.java    |  51 ++++-
 .../java/org/apache/struts2/util/StrutsUtil.java   |   4 +-
 core/src/main/resources/struts-default.xml         |   1 +
 .../resources/template/css_xhtml/form-validate.ftl |   4 +-
 .../main/resources/template/simple/combobox.ftl    |   4 +-
 .../resources/template/simple/doubleselect.ftl     |  44 ++--
 .../main/resources/template/simple/form-close.ftl  |  24 +-
 .../template/xhtml/form-close-validate.ftl         |  10 +-
 .../main/resources/template/xhtml/form-close.ftl   |   2 +-
 .../resources/template/xhtml/form-validate.ftl     |   4 +-
 .../com/opensymphony/xwork2/ChainResultTest.java   |  14 +-
 .../xwork2/interceptor/AliasInterceptorTest.java   |  90 ++++++++
 .../DefaultAcceptedPatternsCheckerTest.java        |  28 +++
 .../DefaultExcludedPatternsCheckerTest.java        |  29 +++
 ...aultNotExcludedAcceptedPatternsCheckerTest.java |  91 ++++++++
 .../apache/struts2/TestConfigurationProvider.java  |  12 +-
 .../org/apache/struts2/components/UIBeanTest.java  |  94 ++++++--
 .../apache/struts2/result/PostbackResultTest.java  | 135 +++++++++++
 .../result/ServletActionRedirectResultTest.java    | 135 +++++++++++
 .../apache/struts2/result/StreamResultTest.java    |  42 +++-
 .../org/apache/struts2/util/StrutsUtilTest.java    |  55 +++--
 .../freemarker/FreemarkerResultMockedTest.java     |  65 +++++-
 .../org/apache/struts2/views/jsp/BeanTagTest.java  |  61 +++++
 .../apache/struts2/views/jsp/ui/ComboBoxTest.java  |   2 +-
 .../apache/struts2/views/jsp/ui/TextfieldTest.java |   3 +-
 .../apache/struts2/views/freemarker/iterator.ftl   |   8 +-
 .../org/apache/struts2/views/jsp/ui/ComboBox-4.txt |   8 +-
 .../org/apache/struts2/views/jsp/ui/Radio-6.txt    |  12 +-
 .../apache/struts2/views/jsp/ui/Textfield-5.txt    |   2 +-
 core/src/test/resources/struts.xml                 |   5 +
 core/src/test/resources/xwork-sample.xml           |  24 +-
 .../views/jasperreports/JasperReportsResult.java   |  59 ++++-
 .../jasperreports/JasperReportsResultTest.java     | 251 +++++++++++++++++++--
 .../struts2/views/jasperreports/simple.jrxml       |  45 ++++
 .../struts2/views/java/simple/AnchorTest.java      |   2 +
 .../struts2/views/java/simple/CheckboxTest.java    |   2 +
 .../struts2/components/PortletUrlRenderer.java     |   4 +-
 50 files changed, 1657 insertions(+), 249 deletions(-)

diff --git a/core/src/main/java/com/opensymphony/xwork2/config/providers/XWorkConfigurationProvider.java b/core/src/main/java/com/opensymphony/xwork2/config/providers/XWorkConfigurationProvider.java
index f1d4f11..f677a3e 100644
--- a/core/src/main/java/com/opensymphony/xwork2/config/providers/XWorkConfigurationProvider.java
+++ b/core/src/main/java/com/opensymphony/xwork2/config/providers/XWorkConfigurationProvider.java
@@ -33,6 +33,7 @@ import com.opensymphony.xwork2.security.DefaultAcceptedPatternsChecker;
 import com.opensymphony.xwork2.security.DefaultExcludedPatternsChecker;
 import com.opensymphony.xwork2.DefaultTextProvider;
 import com.opensymphony.xwork2.DefaultUnknownHandlerManager;
+import com.opensymphony.xwork2.security.DefaultNotExcludedAcceptedPatternsChecker;
 import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
 import com.opensymphony.xwork2.FileManager;
 import com.opensymphony.xwork2.FileManagerFactory;
@@ -88,6 +89,7 @@ import com.opensymphony.xwork2.ognl.accessor.XWorkIteratorPropertyAccessor;
 import com.opensymphony.xwork2.ognl.accessor.XWorkListPropertyAccessor;
 import com.opensymphony.xwork2.ognl.accessor.XWorkMapPropertyAccessor;
 import com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor;
+import com.opensymphony.xwork2.security.NotExcludedAcceptedPatternsChecker;
 import com.opensymphony.xwork2.util.CompoundRoot;
 import com.opensymphony.xwork2.LocalizedTextProvider;
 import com.opensymphony.xwork2.util.StrutsLocalizedTextProvider;
@@ -215,6 +217,8 @@ public class XWorkConfigurationProvider implements ConfigurationProvider {
 
                 .factory(ExcludedPatternsChecker.class, DefaultExcludedPatternsChecker.class, Scope.PROTOTYPE)
                 .factory(AcceptedPatternsChecker.class, DefaultAcceptedPatternsChecker.class, Scope.PROTOTYPE)
+                .factory(NotExcludedAcceptedPatternsChecker.class, DefaultNotExcludedAcceptedPatternsChecker.class
+                        , Scope.SINGLETON)
 
                 .factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON)
         ;
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/AliasInterceptor.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/AliasInterceptor.java
index d5135bf..2cb1a4e 100644
--- a/core/src/main/java/com/opensymphony/xwork2/interceptor/AliasInterceptor.java
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/AliasInterceptor.java
@@ -23,6 +23,8 @@ import com.opensymphony.xwork2.ActionInvocation;
 import com.opensymphony.xwork2.XWorkConstants;
 import com.opensymphony.xwork2.config.entities.ActionConfig;
 import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
+import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
 import com.opensymphony.xwork2.util.ClearableValueStack;
 import com.opensymphony.xwork2.util.Evaluated;
 import com.opensymphony.xwork2.LocalizedTextProvider;
@@ -100,6 +102,9 @@ public class AliasInterceptor extends AbstractInterceptor {
     protected LocalizedTextProvider localizedTextProvider;
     protected boolean devMode = false;
 
+    private ExcludedPatternsChecker excludedPatterns;
+    private AcceptedPatternsChecker acceptedPatterns;
+
     @Inject(XWorkConstants.DEV_MODE)
     public void setDevMode(String mode) {
         this.devMode = Boolean.parseBoolean(mode);
@@ -115,6 +120,16 @@ public class AliasInterceptor extends AbstractInterceptor {
         this.localizedTextProvider = localizedTextProvider;
     }
 
+    @Inject
+    public void setExcludedPatterns(ExcludedPatternsChecker excludedPatterns) {
+        this.excludedPatterns = excludedPatterns;
+    }
+
+    @Inject
+    public void setAcceptedPatterns(AcceptedPatternsChecker acceptedPatterns) {
+        this.acceptedPatterns = acceptedPatterns;
+    }
+
     /**
      * <p>
      * Sets the name of the action parameter to look for the alias map.
@@ -145,7 +160,7 @@ public class AliasInterceptor extends AbstractInterceptor {
             ValueStack stack = ac.getValueStack();
             Object obj = stack.findValue(aliasExpression);
 
-            if (obj != null && obj instanceof Map) {
+            if (obj instanceof Map) {
                 //get secure stack
                 ValueStack newStack = valueStackFactory.createValueStack(stack);
                 boolean clearableStack = newStack instanceof ClearableValueStack;
@@ -167,7 +182,13 @@ public class AliasInterceptor extends AbstractInterceptor {
                 for (Object o : aliases.entrySet()) {
                     Map.Entry entry = (Map.Entry) o;
                     String name = entry.getKey().toString();
+                    if (isNotAcceptableExpression(name)) {
+                        continue;
+                    }
                     String alias = (String) entry.getValue();
+                    if (isNotAcceptableExpression(alias)) {
+                        continue;
+                    }
                     Evaluated value = new Evaluated(stack.findValue(name));
                     if (!value.isDefined()) {
                         // workaround
@@ -206,5 +227,65 @@ public class AliasInterceptor extends AbstractInterceptor {
         
         return invocation.invoke();
     }
-    
+
+    protected boolean isAccepted(String paramName) {
+        AcceptedPatternsChecker.IsAccepted result = acceptedPatterns.isAccepted(paramName);
+        if (result.isAccepted()) {
+            return true;
+        }
+
+        LOG.warn("Parameter [{}] didn't match accepted pattern [{}]! See Accepted / Excluded patterns at\n" +
+                        "https://struts.apache.org/security/#accepted--excluded-patterns",
+                paramName, result.getAcceptedPattern());
+
+        return false;
+    }
+
+    protected boolean isExcluded(String paramName) {
+        ExcludedPatternsChecker.IsExcluded result = excludedPatterns.isExcluded(paramName);
+        if (!result.isExcluded()) {
+            return false;
+        }
+
+        LOG.warn("Parameter [{}] matches excluded pattern [{}]! See Accepted / Excluded patterns at\n" +
+                        "https://struts.apache.org/security/#accepted--excluded-patterns",
+                paramName, result.getExcludedPattern());
+
+        return true;
+    }
+
+    /**
+     * Checks if expression contains vulnerable code
+     *
+     * @param expression of interceptor
+     * @return true|false
+     */
+    protected boolean isNotAcceptableExpression(String expression) {
+        return isExcluded(expression) || !isAccepted(expression);
+    }
+
+    /**
+     * Sets a comma-delimited list of regular expressions to match
+     * parameters that are allowed in the parameter map (aka whitelist).
+     * <p>
+     * Don't change the default unless you know what you are doing in terms
+     * of security implications.
+     * </p>
+     *
+     * @param commaDelim A comma-delimited list of regular expressions
+     */
+    public void setAcceptParamNames(String commaDelim) {
+        acceptedPatterns.setAcceptedPatterns(commaDelim);
+    }
+
+    /**
+     * Sets a comma-delimited list of regular expressions to match
+     * parameters that should be removed from the parameter map.
+     *
+     * @param commaDelim A comma-delimited list of regular expressions
+     */
+    public void setExcludeParams(String commaDelim) {
+        excludedPatterns.setExcludedPatterns(commaDelim);
+    }
+
 }
diff --git a/core/src/main/java/com/opensymphony/xwork2/mock/MockResult.java b/core/src/main/java/com/opensymphony/xwork2/mock/MockResult.java
index f85cae4..6d3debe 100644
--- a/core/src/main/java/com/opensymphony/xwork2/mock/MockResult.java
+++ b/core/src/main/java/com/opensymphony/xwork2/mock/MockResult.java
@@ -31,6 +31,8 @@ public class MockResult implements Result {
 
     public static final String DEFAULT_PARAM = "foo";
 
+    private ActionInvocation invocation;
+
     @Override
     public boolean equals(Object o) {
         if (this == o) {
@@ -41,7 +43,7 @@ public class MockResult implements Result {
     }
 
     public void execute(ActionInvocation invocation) throws Exception {
-        // no op
+        this.invocation = invocation;
     }
 
     @Override
@@ -53,4 +55,7 @@ public class MockResult implements Result {
         // no op
     }
 
+    public ActionInvocation getInvocation() {
+        return invocation;
+    }
 }
diff --git a/core/src/main/java/com/opensymphony/xwork2/security/AcceptedPatternsChecker.java b/core/src/main/java/com/opensymphony/xwork2/security/AcceptedPatternsChecker.java
index fa92e0b..f5a3294 100644
--- a/core/src/main/java/com/opensymphony/xwork2/security/AcceptedPatternsChecker.java
+++ b/core/src/main/java/com/opensymphony/xwork2/security/AcceptedPatternsChecker.java
@@ -22,7 +22,7 @@ import java.util.Set;
 import java.util.regex.Pattern;
 
 /**
- * Used across different interceptors to check if given string matches one of the excluded patterns.
+ * Used across different interceptors to check if given string matches one of the accepted patterns.
  */
 public interface AcceptedPatternsChecker {
 
diff --git a/core/src/main/java/com/opensymphony/xwork2/security/DefaultNotExcludedAcceptedPatternsChecker.java b/core/src/main/java/com/opensymphony/xwork2/security/DefaultNotExcludedAcceptedPatternsChecker.java
new file mode 100644
index 0000000..b475da1
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/security/DefaultNotExcludedAcceptedPatternsChecker.java
@@ -0,0 +1,105 @@
+/*
+ * 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 com.opensymphony.xwork2.security;
+
+import com.opensymphony.xwork2.inject.Inject;
+
+import java.util.Set;
+import java.util.regex.Pattern;
+
+public class DefaultNotExcludedAcceptedPatternsChecker implements NotExcludedAcceptedPatternsChecker {
+    private ExcludedPatternsChecker excludedPatterns;
+    private AcceptedPatternsChecker acceptedPatterns;
+
+
+    @Inject
+    public void setExcludedPatterns(ExcludedPatternsChecker excludedPatterns) {
+        this.excludedPatterns = excludedPatterns;
+    }
+
+    @Inject
+    public void setAcceptedPatterns(AcceptedPatternsChecker acceptedPatterns) {
+        this.acceptedPatterns = acceptedPatterns;
+    }
+
+    @Override
+    public IsAllowed isAllowed(String value) {
+        IsExcluded isExcluded = isExcluded(value);
+        if (isExcluded.isExcluded()) {
+            return IsAllowed.no(isExcluded.getExcludedPattern());
+        }
+
+        IsAccepted isAccepted = isAccepted(value);
+        if (!isAccepted.isAccepted()) {
+            return IsAllowed.no(isAccepted.getAcceptedPattern());
+        }
+
+        return IsAllowed.yes(isAccepted.getAcceptedPattern());
+    }
+
+    @Override
+    public IsAccepted isAccepted(String value) {
+        return acceptedPatterns.isAccepted(value);
+    }
+
+    @Override
+    public void setAcceptedPatterns(String commaDelimitedPatterns) {
+        acceptedPatterns.setAcceptedPatterns(commaDelimitedPatterns);
+    }
+
+    @Override
+    public void setAcceptedPatterns(String[] patterns) {
+        acceptedPatterns.setAcceptedPatterns(patterns);
+    }
+
+    @Override
+    public void setAcceptedPatterns(Set<String> patterns) {
+        acceptedPatterns.setAcceptedPatterns(patterns);
+    }
+
+    @Override
+    public Set<Pattern> getAcceptedPatterns() {
+        return acceptedPatterns.getAcceptedPatterns();
+    }
+
+    @Override
+    public IsExcluded isExcluded(String value) {
+        return excludedPatterns.isExcluded(value);
+    }
+
+    @Override
+    public void setExcludedPatterns(String commaDelimitedPatterns) {
+        excludedPatterns.setExcludedPatterns(commaDelimitedPatterns);
+    }
+
+    @Override
+    public void setExcludedPatterns(String[] patterns) {
+        excludedPatterns.setExcludedPatterns(patterns);
+    }
+
+    @Override
+    public void setExcludedPatterns(Set<String> patterns) {
+        excludedPatterns.setExcludedPatterns(patterns);
+    }
+
+    @Override
+    public Set<Pattern> getExcludedPatterns() {
+        return excludedPatterns.getExcludedPatterns();
+    }
+}
diff --git a/core/src/main/java/com/opensymphony/xwork2/security/NotExcludedAcceptedPatternsChecker.java b/core/src/main/java/com/opensymphony/xwork2/security/NotExcludedAcceptedPatternsChecker.java
new file mode 100644
index 0000000..3a1f2d8
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/security/NotExcludedAcceptedPatternsChecker.java
@@ -0,0 +1,70 @@
+/*
+ * 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 com.opensymphony.xwork2.security;
+
+/**
+ * Used across different places to check if given string is not excluded and is accepted
+ * @see <a href="https://securitylab.github.com/research/apache-struts-double-evaluation/">here</a>
+ * @since 2.5.27
+ */
+public interface NotExcludedAcceptedPatternsChecker extends ExcludedPatternsChecker, AcceptedPatternsChecker {
+
+    /**
+     * Checks if value doesn't match excluded pattern and matches accepted pattern
+     *
+     * @param value to check
+     * @return object containing result of matched pattern and pattern itself
+     */
+    IsAllowed isAllowed(String value);
+
+    final class IsAllowed {
+
+        private final boolean allowed;
+        private final String allowedPattern;
+
+        public static IsAllowed yes(String allowedPattern) {
+            return new IsAllowed(true, allowedPattern);
+        }
+
+        public static IsAllowed no(String allowedPattern) {
+            return new IsAllowed(false, allowedPattern);
+        }
+
+        private IsAllowed(boolean allowed, String allowedPattern) {
+            this.allowed = allowed;
+            this.allowedPattern = allowedPattern;
+        }
+
+        public boolean isAllowed() {
+            return allowed;
+        }
+
+        public String getAllowedPattern() {
+            return allowedPattern;
+        }
+
+        @Override
+        public String toString() {
+            return "IsAllowed { " +
+                    "allowed=" + allowed +
+                    ", allowedPattern=" + allowedPattern +
+                    " }";
+        }
+    }
+}
diff --git a/core/src/main/java/org/apache/struts2/StrutsConstants.java b/core/src/main/java/org/apache/struts2/StrutsConstants.java
index ae4c278..8c076d2 100644
--- a/core/src/main/java/org/apache/struts2/StrutsConstants.java
+++ b/core/src/main/java/org/apache/struts2/StrutsConstants.java
@@ -322,6 +322,7 @@ public final class StrutsConstants {
     /** Dedicated services to check if passed string is excluded/accepted */
     public static final String STRUTS_EXCLUDED_PATTERNS_CHECKER = "struts.excludedPatterns.checker";
     public static final String STRUTS_ACCEPTED_PATTERNS_CHECKER = "struts.acceptedPatterns.checker";
+    public static final String STRUTS_NOT_EXCLUDED_ACCEPTED_PATTERNS_CHECKER = "struts.notExcludedAcceptedPatterns.checker";
 
     /** Constant is used to override framework's default excluded patterns */
     public static final String STRUTS_OVERRIDE_EXCLUDED_PATTERNS = "struts.override.excludedPatterns";
diff --git a/core/src/main/java/org/apache/struts2/components/Component.java b/core/src/main/java/org/apache/struts2/components/Component.java
index 2612ea0..3e25a52 100644
--- a/core/src/main/java/org/apache/struts2/components/Component.java
+++ b/core/src/main/java/org/apache/struts2/components/Component.java
@@ -19,6 +19,7 @@
 package org.apache.struts2.components;
 
 import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.security.NotExcludedAcceptedPatternsChecker;
 import com.opensymphony.xwork2.util.TextParseUtil;
 import com.opensymphony.xwork2.util.ValueStack;
 import org.apache.commons.lang3.BooleanUtils;
@@ -70,6 +71,8 @@ public class Component {
     protected boolean throwExceptionOnELFailure;
     private UrlHelper urlHelper;
 
+    private NotExcludedAcceptedPatternsChecker notExcludedAcceptedPatterns;
+
     /**
      * Constructor.
      *
@@ -112,6 +115,12 @@ public class Component {
     public void setUrlHelper(UrlHelper urlHelper) {
         this.urlHelper = urlHelper;
     }
+
+    @Inject
+    public void setNotExcludedAcceptedPatterns(NotExcludedAcceptedPatternsChecker notExcludedAcceptedPatterns) {
+        this.notExcludedAcceptedPatterns = notExcludedAcceptedPatterns;
+    }
+
     /**
      * Gets the OGNL value stack associated with this component.
      * @return the OGNL value stack associated with this component.
@@ -276,7 +285,7 @@ public class Component {
     }
 
     /**
-     * If altsyntax (%{...}) is applied, simply strip the "%{" and "}" off. 
+     * If altsyntax (%{...}) is applied, simply strip the "%{" and "}" off.
      * @param expr the expression (must be not null)
      * @return the stripped expression if altSyntax is enabled. Otherwise
      * the parameter expression is returned as is.
@@ -296,7 +305,7 @@ public class Component {
     /**
      * Adds the surrounding %{ } to the expression for proper processing.
      * @param expr the expression.
-     * @return the modified expression if altSyntax is enabled, or the parameter 
+     * @return the modified expression if altSyntax is enabled, or the parameter
      * expression otherwise.
      */
 	protected String completeExpressionIfAltSyntax(String expr) {
@@ -383,15 +392,6 @@ public class Component {
     }
 
     /**
-     * Detects if altSyntax is enabled and then checks if expression contains %{...}
-     * @param expr a string to examined
-     * @return true if altSyntax is enabled and expr contains %{...}
-     */
-    protected boolean recursion(String expr) {
-        return ComponentUtils.altSyntax(stack) && ComponentUtils.containsExpression(expr);
-    }
-
-    /**
      * Renders an action URL by consulting the {@link org.apache.struts2.dispatcher.mapper.ActionMapper}.
      * @param action      the action
      * @param namespace   the namespace
@@ -407,7 +407,7 @@ public class Component {
      * @return the action url.
      */
     protected String determineActionURL(String action, String namespace, String method,
-                                        HttpServletRequest req, HttpServletResponse res, Map parameters, String scheme,
+                                        HttpServletRequest req, HttpServletResponse res, Map<String, Object> parameters, String scheme,
                                         boolean includeContext, boolean encodeResult, boolean forceAddSchemeHostAndPort,
                                         boolean escapeAmp) {
         String finalAction = findString(action);
@@ -560,4 +560,22 @@ public class Component {
         return standardAttributes;
     }
 
+    /**
+     * Checks if expression doesn't contain vulnerable code
+     *
+     * @param expression of the component
+     * @return true|false
+     * @since 2.5.27
+     */
+    protected boolean isAcceptableExpression(String expression) {
+        NotExcludedAcceptedPatternsChecker.IsAllowed isAllowed = notExcludedAcceptedPatterns.isAllowed(expression);
+        if (isAllowed.isAllowed()) {
+            return true;
+        }
+
+        LOG.warn("Expression [{}] isn't allowed by pattern [{}]! See Accepted / Excluded patterns at\n" +
+                "https://struts.apache.org/security/", expression, isAllowed.getAllowedPattern());
+
+        return false;
+    }
 }
diff --git a/core/src/main/java/org/apache/struts2/components/FormButton.java b/core/src/main/java/org/apache/struts2/components/FormButton.java
index 7dcc7ae..e61ed5d 100644
--- a/core/src/main/java/org/apache/struts2/components/FormButton.java
+++ b/core/src/main/java/org/apache/struts2/components/FormButton.java
@@ -125,6 +125,7 @@ public abstract class FormButton extends ClosingUIBean {
             }
         }
         addParameter("id", _tmp_id);
+        addParameter("escapedId", escape(_tmp_id));
     }
 
     /**
diff --git a/core/src/main/java/org/apache/struts2/components/Param.java b/core/src/main/java/org/apache/struts2/components/Param.java
index 023761a..c2c299e 100644
--- a/core/src/main/java/org/apache/struts2/components/Param.java
+++ b/core/src/main/java/org/apache/struts2/components/Param.java
@@ -125,23 +125,29 @@ public class Param extends Component {
             if (component instanceof UnnamedParametric) {
                 ((UnnamedParametric) component).addParameter(findValue(value));
             } else {
-                String name = findString(this.name);
+                String translatedName = findString(this.name);
 
-                if (name == null) {
+                if (translatedName == null) {
                     throw new StrutsException("No name found for following expression: " + this.name);
                 }
 
-                Object value = findValue(this.value);
+                boolean evaluated = !translatedName.equals(this.name);
+                boolean reevaluate = !evaluated || isAcceptableExpression(translatedName);
+                if (!reevaluate) {
+                    throw new StrutsException("Excluded or not accepted name found: " + translatedName);
+                }
+
+                Object foundValue = findValue(this.value);
                 if (suppressEmptyParameters) {
-                    if (value != null && StringUtils.isNotBlank(value.toString())) {
-                        component.addParameter(name, value);
+                    if (foundValue != null && StringUtils.isNotBlank(foundValue.toString())) {
+                        component.addParameter(translatedName, foundValue);
                     } else {
-                        component.addParameter(name, null);
+                        component.addParameter(translatedName, null);
                     }
-                } else if (value == null || StringUtils.isBlank(value.toString())) {
-                    component.addParameter(name, "");
+                } else if (foundValue == null || StringUtils.isBlank(foundValue.toString())) {
+                    component.addParameter(translatedName, "");
                 } else {
-                    component.addParameter(name, value);
+                    component.addParameter(translatedName, foundValue);
                 }
             }
         } else {
@@ -158,7 +164,8 @@ public class Param extends Component {
 
         return super.end(writer, "");
     }
-    
+
+    @Override
     public boolean usesBody() {
         return true;
     }
@@ -193,7 +200,7 @@ public class Param extends Component {
          * Adds the given value as a parameter to the outer tag.
          * @param value  the value
          */
-        public void addParameter(Object value);
+        void addParameter(Object value);
     }
 
 }
diff --git a/core/src/main/java/org/apache/struts2/components/ServletUrlRenderer.java b/core/src/main/java/org/apache/struts2/components/ServletUrlRenderer.java
index b41b4d6..b2704bc 100644
--- a/core/src/main/java/org/apache/struts2/components/ServletUrlRenderer.java
+++ b/core/src/main/java/org/apache/struts2/components/ServletUrlRenderer.java
@@ -188,7 +188,9 @@ public class ServletUrlRenderer implements UrlRenderer {
 
             // if the id isn't specified, use the action name
             if (formComponent.getId() == null && actionName != null) {
-                formComponent.addParameter("id", formComponent.escape(actionName));
+                String escapedId = formComponent.escape(actionName);
+                formComponent.addParameter("id", escapedId);
+                formComponent.addParameter("escapedId", escapedId);
             }
         } else if (action != null) {
             // Since we can't find an action alias in the configuration, we just
@@ -223,7 +225,9 @@ public class ServletUrlRenderer implements UrlRenderer {
                 } else {
                     id = result.substring(slash + 1);
                 }
-                formComponent.addParameter("id", formComponent.escape(id));
+                String escapedId = formComponent.escape(id);
+                formComponent.addParameter("id", escapedId);
+                formComponent.addParameter("escapedId", escapedId);
             }
         }
 
diff --git a/core/src/main/java/org/apache/struts2/components/UIBean.java b/core/src/main/java/org/apache/struts2/components/UIBean.java
index 21ae1d5..9a8ae45 100644
--- a/core/src/main/java/org/apache/struts2/components/UIBean.java
+++ b/core/src/main/java/org/apache/struts2/components/UIBean.java
@@ -555,16 +555,13 @@ public abstract class UIBean extends Component {
     protected abstract String getDefaultTemplate();
 
     protected Template buildTemplateName(String myTemplate, String myDefaultTemplate) {
-        String template = myDefaultTemplate;
+        String templateName = myDefaultTemplate;
 
         if (myTemplate != null) {
-            template = findString(myTemplate);
+            templateName = findString(myTemplate);
         }
 
-        String templateDir = getTemplateDir();
-        String theme = getTheme();
-
-        return new Template(templateDir, theme, template);
+        return new Template(getTemplateDir(), getTheme(), templateName);
 
     }
 
@@ -581,71 +578,70 @@ public abstract class UIBean extends Component {
     }
 
     public String getTemplateDir() {
-        String templateDir = null;
+        String result = null;
 
         if (this.templateDir != null) {
-            templateDir = findString(this.templateDir);
+            result = findString(this.templateDir);
         }
 
         // If templateDir is not explicitly given,
         // try to find attribute which states the dir set to use
-        if (StringUtils.isBlank(templateDir)) {
-            templateDir = stack.findString("#attr.templateDir");
+        if (StringUtils.isBlank(result)) {
+            result = stack.findString("#attr.templateDir");
         }
 
         // Default template set
-        if (StringUtils.isBlank(templateDir)) {
-            templateDir = defaultTemplateDir;
+        if (StringUtils.isBlank(result)) {
+            result = defaultTemplateDir;
         }
 
         // Defaults to 'template'
-        if (StringUtils.isBlank(templateDir)) {
-            templateDir = "template";
+        if (StringUtils.isBlank(result)) {
+            result = "template";
         }
 
-        return templateDir;
+        return result;
     }
 
     public String getTheme() {
-        String theme = null;
+        String result = null;
 
         if (this.theme != null) {
-            theme = findString(this.theme);
+            result = findString(this.theme);
         }
 
-        if (StringUtils.isBlank(theme)) {
+        if (StringUtils.isBlank(result)) {
             Form form = (Form) findAncestor(Form.class);
             if (form != null) {
-                theme = form.getTheme();
+                result = form.getTheme();
             }
         }
 
         // If theme set is not explicitly given,
         // try to find attribute which states the theme set to use
-        if (StringUtils.isBlank(theme)) {
-            theme = stack.findString("#attr.theme");
+        if (StringUtils.isBlank(result)) {
+            result = stack.findString("#attr.theme");
         }
 
         // Default theme set
-        if (StringUtils.isBlank(theme)) {
-            theme = defaultUITheme;
+        if (StringUtils.isBlank(result)) {
+            result = defaultUITheme;
         }
 
-        return theme;
+        return result;
     }
 
     public void evaluateParams() {
-        String templateDir = getTemplateDir();
-        String theme = getTheme();
+        String gotTheme = getTheme();
 
-        addParameter("templateDir", templateDir);
-        addParameter("theme", theme);
+        addParameter("templateDir", getTemplateDir());
+        addParameter("theme", gotTheme);
         addParameter("template", template != null ? findString(template) : getDefaultTemplate());
         addParameter("dynamicAttributes", dynamicAttributes);
         addParameter("themeExpansionToken", uiThemeExpansionToken);
-        addParameter("expandTheme", uiThemeExpansionToken + theme);
+        addParameter("expandTheme", uiThemeExpansionToken + gotTheme);
 
-        String name = null;
+        String translatedName = null;
         String providedLabel = null;
 
         if (this.key != null) {
@@ -661,8 +657,8 @@ public abstract class UIBean extends Component {
         }
 
         if (this.name != null) {
-            name = findString(this.name);
-            addParameter("name", name);
+            translatedName = findString(this.name);
+            addParameter("name", translatedName);
         }
 
         if (label != null) {
@@ -786,28 +782,31 @@ public abstract class UIBean extends Component {
 
 
         // see if the value was specified as a parameter already
+        final String NAME_VALUE = "nameValue";
         if (parameters.containsKey("value")) {
-            parameters.put("nameValue", parameters.get("value"));
+            parameters.put(NAME_VALUE, parameters.get("value"));
         } else {
             if (evaluateNameValue()) {
                 final Class valueClazz = getValueClassType();
 
                 if (valueClazz != null) {
                     if (value != null) {
-                        addParameter("nameValue", findValue(value, valueClazz));
-                    } else if (name != null) {
-                        String expr = completeExpressionIfAltSyntax(name);
-                        if (recursion(name)) {
-                            addParameter("nameValue", expr);
+                        addParameter(NAME_VALUE, findValue(value, valueClazz));
+                    } else if (translatedName != null) {
+                        boolean evaluated = !translatedName.equals(this.name);
+                        boolean reevaluate = !evaluated || isAcceptableExpression(translatedName);
+                        if (!reevaluate) {
+                            addParameter(NAME_VALUE, translatedName);
                         } else {
-                            addParameter("nameValue", findValue(expr, valueClazz));
+                            String expr = completeExpressionIfAltSyntax(translatedName);
+                            addParameter(NAME_VALUE, findValue(expr, valueClazz));
                         }
                     }
                 } else {
                     if (value != null) {
-                        addParameter("nameValue", findValue(value));
-                    } else if (name != null) {
-                        addParameter("nameValue", findValue(name));
+                        addParameter(NAME_VALUE, findValue(value));
+                    } else if (translatedName != null) {
+                        addParameter(NAME_VALUE, findValue(translatedName));
                     }
                 }
             }
@@ -821,10 +820,10 @@ public abstract class UIBean extends Component {
         if (form != null ) {
             addParameter("form", form.getParameters());
 
-            if ( name != null ) {
+            if ( translatedName != null ) {
                 // list should have been created by the form component
                 List<String> tags = (List<String>) form.getParameters().get("tagNames");
-                tags.add(name);
+                tags.add(translatedName);
             }
         }
 
@@ -892,7 +891,7 @@ public abstract class UIBean extends Component {
     protected String escape(String name) {
         // escape any possible values that can make the ID painful to work with in JavaScript
         if (name != null) {
-            return name.replaceAll("[\\/\\.\\[\\]]", "_");
+            return name.replaceAll("[^a-zA-Z0-9_]", "_");
         } else {
             return null;
         }
@@ -943,14 +942,14 @@ public abstract class UIBean extends Component {
 
     protected Map getTooltipConfig(UIBean component) {
         Object tooltipConfigObj = component.getParameters().get("tooltipConfig");
-        Map<String, String> tooltipConfig = new LinkedHashMap<>();
+        Map<String, String> result = new LinkedHashMap<>();
 
         if (tooltipConfigObj instanceof Map) {
             // we get this if its configured using
             // 1] UI component's tooltipConfig attribute  OR
             // 2] <param name="tooltip" value="" /> param tag value attribute
 
-            tooltipConfig = new LinkedHashMap<>((Map) tooltipConfigObj);
+            result = new LinkedHashMap<>((Map) tooltipConfigObj);
         } else if (tooltipConfigObj instanceof String) {
 
             // we get this if its configured using
@@ -960,23 +959,23 @@ public abstract class UIBean extends Component {
 
             for (String aTooltipConfigArray : tooltipConfigArray) {
                 String[] configEntry = aTooltipConfigArray.trim().split("=");
-                String key = configEntry[0].trim();
-                String value;
+                String configKey = configEntry[0].trim();
+                String configValue;
                 if (configEntry.length > 1) {
-                    value = configEntry[1].trim();
-                    tooltipConfig.put(key, value);
+                    configValue = configEntry[1].trim();
+                    result.put(configKey, configValue);
                 } else {
-                    LOG.warn("component {} tooltip config param {} has no value defined, skipped", component, key);
+                    LOG.warn("component {} tooltip config param {} has no value defined, skipped", component, configKey);
                 }
             }
         }
         if (component.javascriptTooltip != null)
-            tooltipConfig.put("jsTooltipEnabled", component.javascriptTooltip);
+            result.put("jsTooltipEnabled", component.javascriptTooltip);
         if (component.tooltipIconPath != null)
-            tooltipConfig.put("tooltipIcon", component.tooltipIconPath);
+            result.put("tooltipIcon", component.tooltipIconPath);
         if (component.tooltipDelay != null)
-            tooltipConfig.put("tooltipDelay", component.tooltipDelay);
-        return tooltipConfig;
+            result.put("tooltipDelay", component.tooltipDelay);
+        return result;
     }
 
     /**
@@ -1257,10 +1256,10 @@ public abstract class UIBean extends Component {
 
     public void setDynamicAttributes(Map<String, String> tagDynamicAttributes) {
         for (Map.Entry<String, String> entry : tagDynamicAttributes.entrySet()) {
-            String key = entry.getKey();
+            String entryKey = entry.getKey();
 
-            if (!isValidTagAttribute(key)) {
-                dynamicAttributes.put(key, entry.getValue());
+            if (!isValidTagAttribute(entryKey)) {
+                dynamicAttributes.put(entryKey, entry.getValue());
             }
         }
     }
@@ -1275,9 +1274,9 @@ public abstract class UIBean extends Component {
         super.copyParams(params);
         for (Object o : params.entrySet()) {
             Map.Entry entry = (Map.Entry) o;
-            String key = (String) entry.getKey();
-            if (!isValidTagAttribute(key) && !key.equals("dynamicAttributes")) {
-                dynamicAttributes.put(key, entry.getValue());
+            String entryKey = (String) entry.getKey();
+            if (!isValidTagAttribute(entryKey) && !entryKey.equals("dynamicAttributes")) {
+                dynamicAttributes.put(entryKey, entry.getValue());
             }
         }
     }
diff --git a/core/src/main/java/org/apache/struts2/config/DefaultBeanSelectionProvider.java b/core/src/main/java/org/apache/struts2/config/DefaultBeanSelectionProvider.java
index fb78003..cb39ac7 100644
--- a/core/src/main/java/org/apache/struts2/config/DefaultBeanSelectionProvider.java
+++ b/core/src/main/java/org/apache/struts2/config/DefaultBeanSelectionProvider.java
@@ -50,6 +50,7 @@ import com.opensymphony.xwork2.factory.ResultFactory;
 import com.opensymphony.xwork2.factory.ValidatorFactory;
 import com.opensymphony.xwork2.inject.ContainerBuilder;
 import com.opensymphony.xwork2.inject.Scope;
+import com.opensymphony.xwork2.security.NotExcludedAcceptedPatternsChecker;
 import com.opensymphony.xwork2.util.PatternMatcher;
 import com.opensymphony.xwork2.util.TextParser;
 import com.opensymphony.xwork2.util.ValueStackFactory;
@@ -424,6 +425,8 @@ public class DefaultBeanSelectionProvider extends AbstractBeanSelectionProvider
         /** Checker is used mostly in interceptors, so there be one instance of checker per interceptor with Scope.PROTOTYPE **/
         alias(ExcludedPatternsChecker.class, StrutsConstants.STRUTS_EXCLUDED_PATTERNS_CHECKER, builder, props, Scope.PROTOTYPE);
         alias(AcceptedPatternsChecker.class, StrutsConstants.STRUTS_ACCEPTED_PATTERNS_CHECKER, builder, props, Scope.PROTOTYPE);
+        alias(NotExcludedAcceptedPatternsChecker.class, StrutsConstants.STRUTS_NOT_EXCLUDED_ACCEPTED_PATTERNS_CHECKER
+                , builder, props, Scope.SINGLETON);
 
         switchDevMode(props);
 
diff --git a/core/src/main/java/org/apache/struts2/result/StreamResult.java b/core/src/main/java/org/apache/struts2/result/StreamResult.java
index 554b1a9..3b53408 100644
--- a/core/src/main/java/org/apache/struts2/result/StreamResult.java
+++ b/core/src/main/java/org/apache/struts2/result/StreamResult.java
@@ -19,6 +19,8 @@
 package org.apache.struts2.result;
 
 import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.security.NotExcludedAcceptedPatternsChecker;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
@@ -100,6 +102,8 @@ public class StreamResult extends StrutsResultSupport {
     protected int bufferSize = 1024;
     protected boolean allowCaching = true;
 
+    private NotExcludedAcceptedPatternsChecker notExcludedAcceptedPatterns;
+
     public StreamResult() {
         super();
     }
@@ -115,6 +119,11 @@ public class StreamResult extends StrutsResultSupport {
         return allowCaching;
     }
 
+    @Inject
+    public void setNotExcludedAcceptedPatterns(NotExcludedAcceptedPatternsChecker notExcludedAcceptedPatterns) {
+        this.notExcludedAcceptedPatterns = notExcludedAcceptedPatterns;
+    }
+
     /**
      * Set allowCaching to <tt>false</tt> to indicate that the client should be requested not to cache the data stream.
      * This is set to <tt>false</tt> by default
@@ -219,14 +228,17 @@ public class StreamResult extends StrutsResultSupport {
         OutputStream oOutput = null;
 
         try {
-            if (inputStream == null) {
+            String parsedInputName = conditionalParse(inputName, invocation);
+            boolean evaluated = parsedInputName != null && !parsedInputName.equals(inputName);
+            boolean reevaluate = !evaluated || isAcceptableExpression(parsedInputName);
+            if (inputStream == null && reevaluate) {
                 LOG.debug("Find the inputstream from the invocation variable stack");
-                inputStream = (InputStream) invocation.getStack().findValue(conditionalParse(inputName, invocation));
+                inputStream = (InputStream) invocation.getStack().findValue(parsedInputName);
             }
 
             if (inputStream == null) {
-                String msg = ("Can not find a java.io.InputStream with the name [" + inputName + "] in the invocation stack. " +
-                    "Check the <param name=\"inputName\"> tag specified for this action.");
+                String msg = ("Can not find a java.io.InputStream with the name [" + parsedInputName + "] in the invocation stack. " +
+                    "Check the <param name=\"inputName\"> tag specified for this action is correct, not excluded and accepted.");
                 LOG.error(msg);
                 throw new IllegalArgumentException(msg);
             }
@@ -243,15 +255,16 @@ public class StreamResult extends StrutsResultSupport {
 
             LOG.debug("Set the content length: {}", contentLength);
             if (contentLength != null) {
-                String _contentLength = conditionalParse(contentLength, invocation);
-                int _contentLengthAsInt;
+                String translatedContentLength = conditionalParse(contentLength, invocation);
+                int contentLengthAsInt;
                 try {
-                    _contentLengthAsInt = Integer.parseInt(_contentLength);
-                    if (_contentLengthAsInt >= 0) {
-                        oResponse.setContentLength(_contentLengthAsInt);
+                    contentLengthAsInt = Integer.parseInt(translatedContentLength);
+                    if (contentLengthAsInt >= 0) {
+                        oResponse.setContentLength(contentLengthAsInt);
                     }
                 } catch(NumberFormatException e) {
-                    LOG.warn("failed to recognize {} as a number, contentLength header will not be set", _contentLength, e);
+                    LOG.warn("failed to recognize {} as a number, contentLength header will not be set",
+                            translatedContentLength, e);
                 }
             }
 
@@ -292,4 +305,22 @@ public class StreamResult extends StrutsResultSupport {
         }
     }
 
+    /**
+     * Checks if expression doesn't contain vulnerable code
+     *
+     * @param expression of result
+     * @return true|false
+     * @since 2.5.27
+     */
+    protected boolean isAcceptableExpression(String expression) {
+        NotExcludedAcceptedPatternsChecker.IsAllowed isAllowed = notExcludedAcceptedPatterns.isAllowed(expression);
+        if (isAllowed.isAllowed()) {
+            return true;
+        }
+
+        LOG.warn("Expression [{}] isn't allowed by pattern [{}]! See Accepted / Excluded patterns at\n" +
+                "https://struts.apache.org/security/", expression, isAllowed.getAllowedPattern());
+
+        return false;
+    }
 }
diff --git a/core/src/main/java/org/apache/struts2/util/StrutsUtil.java b/core/src/main/java/org/apache/struts2/util/StrutsUtil.java
index 647a743..499652e 100644
--- a/core/src/main/java/org/apache/struts2/util/StrutsUtil.java
+++ b/core/src/main/java/org/apache/struts2/util/StrutsUtil.java
@@ -102,7 +102,7 @@ public class StrutsUtil {
             return responseWrapper.getData();
         }
         catch (Exception e) {
-            LOG.debug("Cannot include {}", aName.toString(), e);
+            LOG.debug("Cannot include {}", aName, e);
             throw e;
         }
     }
@@ -125,7 +125,7 @@ public class StrutsUtil {
     }
 
     public String getText(String text) {
-        return (String) stack.findValue("getText('" + text + "')");
+        return (String) stack.findValue("getText('" + text.replace('\'', '"') + "')");
     }
 
     /*
diff --git a/core/src/main/resources/struts-default.xml b/core/src/main/resources/struts-default.xml
index 2b305d1..f05109d 100644
--- a/core/src/main/resources/struts-default.xml
+++ b/core/src/main/resources/struts-default.xml
@@ -202,6 +202,7 @@
 
     <bean type="com.opensymphony.xwork2.security.ExcludedPatternsChecker" name="struts" class="com.opensymphony.xwork2.security.DefaultExcludedPatternsChecker" scope="prototype" />
     <bean type="com.opensymphony.xwork2.security.AcceptedPatternsChecker" name="struts" class="com.opensymphony.xwork2.security.DefaultAcceptedPatternsChecker" scope="prototype" />
+    <bean type="com.opensymphony.xwork2.security.NotExcludedAcceptedPatternsChecker" name="struts" class="com.opensymphony.xwork2.security.DefaultNotExcludedAcceptedPatternsChecker" scope="singleton" />
 
     <bean type="com.opensymphony.xwork2.config.providers.ValueSubstitutor" class="com.opensymphony.xwork2.config.providers.EnvsValueSubstitutor" scope="singleton"/>
 
diff --git a/core/src/main/resources/template/css_xhtml/form-validate.ftl b/core/src/main/resources/template/css_xhtml/form-validate.ftl
index f1b2053..04f1a45 100644
--- a/core/src/main/resources/template/css_xhtml/form-validate.ftl
+++ b/core/src/main/resources/template/css_xhtml/form-validate.ftl
@@ -21,8 +21,8 @@
 <#if parameters.validate!false == true>
 <script type="text/javascript" src="${base}/struts/css_xhtml/validation.js"></script>
     <#if parameters.onsubmit??>
-        ${tag.addParameter('onsubmit', "${parameters.onsubmit}; return validateForm_${parameters.id}();")}
+        ${tag.addParameter('onsubmit', "${parameters.onsubmit}; return validateForm_${parameters.escapedId}();")}
     <#else>
-        ${tag.addParameter('onsubmit', "return validateForm_${parameters.id}();")}
+        ${tag.addParameter('onsubmit', "return validateForm_${parameters.escapedId}();")}
     </#if>
 </#if>
diff --git a/core/src/main/resources/template/simple/combobox.ftl b/core/src/main/resources/template/simple/combobox.ftl
index 3d953c5..87e5b6a 100644
--- a/core/src/main/resources/template/simple/combobox.ftl
+++ b/core/src/main/resources/template/simple/combobox.ftl
@@ -21,7 +21,7 @@
 <script type="text/javascript">
 	function autoPopulate_${parameters.escapedId?html}(targetElement) {
 		<#if parameters.headerKey?? && parameters.headerValue??>
-		if (targetElement.options[targetElement.selectedIndex].value == '${parameters.headerKey?html}') {
+		if (targetElement.options[targetElement.selectedIndex].value == '${parameters.headerKey?js_string}') {
 			return;
 		}
 		</#if>
@@ -30,7 +30,7 @@
 		    return;
 		}
 		</#if>
-		targetElement.form.elements['${parameters.name?html}'].value=targetElement.options[targetElement.selectedIndex].value;
+		targetElement.form.elements['${parameters.name?js_string}'].value=targetElement.options[targetElement.selectedIndex].value;
 	}
 </script>
 <#include "/${parameters.templateDir}/simple/text.ftl" />
diff --git a/core/src/main/resources/template/simple/doubleselect.ftl b/core/src/main/resources/template/simple/doubleselect.ftl
index f1adf8f..40e700d 100644
--- a/core/src/main/resources/template/simple/doubleselect.ftl
+++ b/core/src/main/resources/template/simple/doubleselect.ftl
@@ -72,9 +72,9 @@
 </#if>
 <script type="text/javascript">
     <#assign itemCount = startCount/>
-    var ${parameters.id}Group = new Array(${parameters.listSize} + ${startCount});
-    for (var i = 0; i < (${parameters.listSize} + ${startCount}); i++) {
-        ${parameters.id}Group[i] = [];
+    var ${parameters.escapedId}Group = new Array(${parameters.listSize?number?c} + ${startCount});
+    for (var i = 0; i < (${parameters.listSize?number?c} + ${startCount}); i++) {
+        ${parameters.escapedId}Group[i] = [];
     }
 
     <@s.iterator value="parameters.list">
@@ -90,11 +90,11 @@
         </#if>
         <#assign doubleItemCount = 0/>
         <#if parameters.doubleHeaderKey?? && parameters.doubleHeaderValue??>
-        ${parameters.id}Group[${itemCount}][${doubleItemCount}] = new Option("${parameters.doubleHeaderValue?js_string}", "${parameters.doubleHeaderKey?js_string}");
+        ${parameters.escapedId}Group[${itemCount}][${doubleItemCount}] = new Option("${parameters.doubleHeaderValue?js_string}", "${parameters.doubleHeaderKey?js_string}");
             <#assign doubleItemCount = doubleItemCount + 1/>
         </#if>
         <#if parameters.doubleEmptyOption??>
-        ${parameters.id}Group[${itemCount}][${doubleItemCount}] = new Option("", "");
+        ${parameters.escapedId}Group[${itemCount}][${doubleItemCount}] = new Option("", "");
             <#assign doubleItemCount = doubleItemCount + 1/>
         </#if>
     <@s.iterator value="${parameters.doubleList}">
@@ -130,15 +130,15 @@
               <#assign itemDoubleTitle = ''/>
             </#if>
         </#if>
-    ${parameters.id}Group[${itemCount}][${doubleItemCount}] = new Option("${doubleItemValue?js_string}", "${doubleItemKeyStr?js_string}");
+    ${parameters.escapedId}Group[${itemCount}][${doubleItemCount}] = new Option("${doubleItemValue?js_string}", "${doubleItemKeyStr?js_string}");
         <#if itemDoubleCssClass??>
-    ${parameters.id}Group[${itemCount}][${doubleItemCount}].setAttribute("class","${itemDoubleCssClass?html}");
+    ${parameters.escapedId}Group[${itemCount}][${doubleItemCount}].setAttribute("class","${itemDoubleCssClass}");
         </#if>
         <#if itemDoubleCssStyle??>
-        ${parameters.id}Group[${itemCount}][${doubleItemCount}].setAttribute("style","${itemDoubleCssStyle?html}");
+        ${parameters.escapedId}Group[${itemCount}][${doubleItemCount}].setAttribute("style","${itemDoubleCssStyle}");
         </#if>
         <#if itemDoubleTitle??>
-        ${parameters.id}Group[${itemCount}][${doubleItemCount}].setAttribute("title","${itemDoubleTitle?html}");
+        ${parameters.escapedId}Group[${itemCount}][${doubleItemCount}].setAttribute("title","${itemDoubleTitle}");
         </#if>
 
         <#assign doubleItemCount = doubleItemCount + 1/>
@@ -146,7 +146,7 @@
         <#assign itemCount = itemCount + 1/>
     </...@s.iterator>
 
-    var ${parameters.id}Temp = document.${parameters.formName}.${parameters.doubleId};
+    var ${parameters.escapedId}Temp = document.${parameters.formName}.${parameters.doubleId};
     <#assign itemCount = startCount/>
     <#assign redirectTo = 0/>
     <@s.iterator value="parameters.list">
@@ -160,34 +160,34 @@
         </#if>
         <#assign itemCount = itemCount + 1/>
     </...@s.iterator>
-    ${parameters.id}Redirect(${redirectTo});
-    function ${parameters.id}Redirect(x) {
+    ${parameters.escapedId}Redirect(${redirectTo});
+    function ${parameters.escapedId}Redirect(x) {
         var selected = false;
-        for (var m = ${parameters.id}Temp.options.length - 1; m >= 0; m--) {
-            ${parameters.id}Temp.remove(m);
+        for (var m = ${parameters.escapedId}Temp.options.length - 1; m >= 0; m--) {
+            ${parameters.escapedId}Temp.remove(m);
         }
 
-        for (var i = 0; i < ${parameters.id}Group[x].length; i++) {
-            ${parameters.id}Temp.options[i] = new Option(${parameters.id}Group[x][i].text, ${parameters.id}Group[x][i].value);
+        for (var i = 0; i < ${parameters.escapedId}Group[x].length; i++) {
+            ${parameters.escapedId}Temp.options[i] = new Option(${parameters.escapedId}Group[x][i].text, ${parameters.escapedId}Group[x][i].value);
         <#if parameters.doubleNameValue??>
             <#if parameters.doubleMultiple??>
                 for (var j = 0; j < ${parameters.doubleNameValue}.length; j++) {
-                    if (${parameters.id}Temp.options[i].value == ${parameters.doubleNameValue?js_string}[j]) {
-                        ${parameters.id}Temp.options[i].selected = true;
+                    if (${parameters.escapedId}Temp.options[i].value == ${parameters.doubleNameValue?js_string}[j]) {
+                        ${parameters.escapedId}Temp.options[i].selected = true;
                         selected = true;
                     }
                 }
                 <#else>
-                    if (${parameters.id}Temp.options[i].value == '${parameters.doubleNameValue?js_string}') {
-                        ${parameters.id}Temp.options[i].selected = true;
+                    if (${parameters.escapedId}Temp.options[i].value == '${parameters.doubleNameValue?js_string}') {
+                        ${parameters.escapedId}Temp.options[i].selected = true;
                         selected = true;
                     }
             </#if>
         </#if>
         }
 
-        if ((${parameters.id}Temp.options.length > 0) && (! selected)) {
-            ${parameters.id}Temp.options[0].selected = true;
+        if ((${parameters.escapedId}Temp.options.length > 0) && (! selected)) {
+            ${parameters.escapedId}Temp.options[0].selected = true;
         }
     }
 </script>
\ No newline at end of file
diff --git a/core/src/main/resources/template/simple/form-close.ftl b/core/src/main/resources/template/simple/form-close.ftl
index 0efea0c..c454f07 100644
--- a/core/src/main/resources/template/simple/form-close.ftl
+++ b/core/src/main/resources/template/simple/form-close.ftl
@@ -27,15 +27,15 @@
   submission.
 -->
 <#if (parameters.optiontransferselectIds!?size > 0)>
-	var containingForm = document.getElementById("${parameters.id}");
+	var containingForm = document.getElementById("${parameters.id?js_string}");
 	<#assign selectObjIds = parameters.optiontransferselectIds.keySet() />
 	<#list selectObjIds as selectObjectId>
 		StrutsUtils.addEventListener(containingForm, "submit", 
 			function(evt) {
-				var selectObj = document.getElementById("${selectObjectId}");
+				var selectObj = document.getElementById("${selectObjectId?js_string}");
 				<#if parameters.optiontransferselectIds.get(selectObjectId)??>
 					<#assign selectTagHeaderKey = parameters.optiontransferselectIds.get(selectObjectId)/>
-					selectAllOptionsExceptSome(selectObj, "key", "${selectTagHeaderKey}");
+					selectAllOptionsExceptSome(selectObj, "key", "${selectTagHeaderKey?js_string}");
 				<#else>
 					selectAllOptionsExceptSome(selectObj, "key", "");
 				</#if>
@@ -43,15 +43,15 @@
 	</#list>
 </#if>
 <#if (parameters.inputtransferselectIds!?size > 0)>
-	var containingForm = document.getElementById("${parameters.id}");
+	var containingForm = document.getElementById("${parameters.id?js_string}");
 	<#assign selectObjIds = parameters.inputtransferselectIds.keySet() />
 	<#list selectObjIds as selectObjectId>
 		StrutsUtils.addEventListener(containingForm, "submit",
 			function(evt) {
-				var selectObj = document.getElementById("${selectObjectId}");
+				var selectObj = document.getElementById("${selectObjectId?js_string}");
 				<#if parameters.inputtransferselectIds.get(selectObjectId)??>
 					<#assign selectTagHeaderKey = parameters.inputtransferselectIds.get(selectObjectId)/>
-					selectAllOptionsExceptSome(selectObj, "key", "${selectTagHeaderKey}");
+					selectAllOptionsExceptSome(selectObj, "key", "${selectTagHeaderKey?js_string}");
 				<#else>
 					selectAllOptionsExceptSome(selectObj, "key", "");
 				</#if>
@@ -59,15 +59,15 @@
 	</#list>
 </#if>
 <#if (parameters.optiontransferselectDoubleIds!?size > 0)>
-	var containingForm = document.getElementById("${parameters.id}");
+	var containingForm = document.getElementById("${parameters.id?js_string}");
 	<#assign selectDoubleObjIds = parameters.optiontransferselectDoubleIds.keySet() />
 	<#list selectDoubleObjIds as selectObjId>
 		StrutsUtils.addEventListener(containingForm, "submit", 
 			function(evt) {
-				var selectObj = document.getElementById("${selectObjId}");
+				var selectObj = document.getElementById("${selectObjId?js_string}");
 				<#if parameters.optiontransferselectDoubleIds.get(selectObjId)??>
 					<#assign selectTagHeaderKey = parameters.optiontransferselectDoubleIds.get(selectObjId)/>
-					selectAllOptionsExceptSome(selectObj, "key", "${selectTagHeaderKey}");
+					selectAllOptionsExceptSome(selectObj, "key", "${selectTagHeaderKey?js_string}");
 				<#else>
 					selectAllOptionsExceptSome(selectObj, "key", "");
 				</#if>
@@ -81,15 +81,15 @@
 	submission
 -->
 <#if (parameters.updownselectIds!?size > 0)>
-	var containingForm = document.getElementById("${parameters.id}");
+	var containingForm = document.getElementById("${parameters.id?js_string}");
 	<#assign tmpIds = parameters.updownselectIds.keySet() />
 	<#list tmpIds as tmpId>
 		StrutsUtils.addEventListener(containingForm, "submit", 
 			function(evt) {
-				var updownselectObj = document.getElementById("${tmpId}");
+				var updownselectObj = document.getElementById("${tmpId?js_string}");
 				<#if parameters.updownselectIds.get(tmpId)??>
 					<#assign tmpHeaderKey = parameters.updownselectIds.get(tmpId) />
-					selectAllOptionsExceptSome(updownselectObj, "key", "${tmpHeaderKey}");
+					selectAllOptionsExceptSome(updownselectObj, "key", "${tmpHeaderKey?js_string}");
 				<#else>
 					selectAllOptionsExceptSome(updownselectObj, "key", "");
 				</#if>
diff --git a/core/src/main/resources/template/xhtml/form-close-validate.ftl b/core/src/main/resources/template/xhtml/form-close-validate.ftl
index 2cbb174..d6033f7 100644
--- a/core/src/main/resources/template/xhtml/form-close-validate.ftl
+++ b/core/src/main/resources/template/xhtml/form-close-validate.ftl
@@ -33,7 +33,7 @@ END SNIPPET: supported-validators
 -->
 <#if ((parameters.validate!false == true) && (parameters.performValidation!false == true))>
 <script type="text/javascript">
-    function validateForm_${parameters.id?replace('[^a-zA-Z0-9_]', '_', 'r')}() {
+    function validateForm_${parameters.escapedId}() {
         <#--
             In case of multiselect fields return only the first value.
         -->
@@ -54,7 +54,7 @@ END SNIPPET: supported-validators
             }
             return field.value;
         }
-        form = document.getElementById("${parameters.id}");
+        form = document.getElementById("${parameters.id?js_string}");
         clearErrorMessages(form);
         clearErrorLabels(form);
 
@@ -62,10 +62,10 @@ END SNIPPET: supported-validators
         var continueValidation = true;
     <#list parameters.tagNames as tagName>
         <#list tag.getValidators("${tagName}") as aValidator>
-        // field name: ${aValidator.fieldName}
+        // field name: ${aValidator.fieldName?js_string}
         // validator name: ${aValidator.validatorType}
-        if (form.elements['${aValidator.fieldName}']) {
-            field = form.elements['${aValidator.fieldName}'];
+        if (form.elements['${aValidator.fieldName?js_string}']) {
+            field = form.elements['${aValidator.fieldName?js_string}'];
             <#if aValidator.validatorType = "field-visitor">
                 <#assign validator = aValidator.fieldValidator >
                 //visitor validator switched to: ${validator.validatorType}
diff --git a/core/src/main/resources/template/xhtml/form-close.ftl b/core/src/main/resources/template/xhtml/form-close.ftl
index 3e0e2a7..2d39010 100644
--- a/core/src/main/resources/template/xhtml/form-close.ftl
+++ b/core/src/main/resources/template/xhtml/form-close.ftl
@@ -24,7 +24,7 @@
 <#if parameters.focusElement??>
 <script type="text/javascript">
     StrutsUtils.addOnLoad(function() {
-        var element = document.getElementById("${parameters.focusElement?html}");
+        var element = document.getElementById("${parameters.focusElement?js_string}");
         if(element) {
             element.focus();
         }
diff --git a/core/src/main/resources/template/xhtml/form-validate.ftl b/core/src/main/resources/template/xhtml/form-validate.ftl
index 2d140b7..474470c 100644
--- a/core/src/main/resources/template/xhtml/form-validate.ftl
+++ b/core/src/main/resources/template/xhtml/form-validate.ftl
@@ -22,8 +22,8 @@
 	<script type="text/javascript" src="${base}/struts/xhtml/validation.js"></script>
 	<script type="text/javascript" src="${base}/struts/utils.js"></script>
 	<#if parameters.onsubmit??>
-		${tag.addParameter('onsubmit', "${parameters.onsubmit}; return validateForm_${parameters.id?replace('[^a-zA-Z0-9_]', '_', 'r')}();")}
+		${tag.addParameter('onsubmit', "${parameters.onsubmit}; return validateForm_${parameters.escapedId}();")}
 	<#else>
-		${tag.addParameter('onsubmit', "return validateForm_${parameters.id?replace('[^a-zA-Z0-9_]', '_', 'r')}();")}
+		${tag.addParameter('onsubmit', "return validateForm_${parameters.escapedId}();")}
 	</#if>
 </#if>
diff --git a/core/src/test/java/com/opensymphony/xwork2/ChainResultTest.java b/core/src/test/java/com/opensymphony/xwork2/ChainResultTest.java
index 68def20..c091f0f 100644
--- a/core/src/test/java/com/opensymphony/xwork2/ChainResultTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/ChainResultTest.java
@@ -20,6 +20,7 @@ package com.opensymphony.xwork2;
 
 import com.mockobjects.dynamic.Mock;
 import com.opensymphony.xwork2.config.providers.XmlConfigurationProvider;
+import com.opensymphony.xwork2.mock.MockResult;
 import com.opensymphony.xwork2.util.ValueStack;
 import junit.framework.TestCase;
 
@@ -83,7 +84,18 @@ public class ChainResultTest extends XWorkTestCase {
         }
     }
 
-    private class NamespaceActionNameTestActionProxyFactory implements ActionProxyFactory {
+    public void testNamespaceChain() throws Exception {
+        ActionProxy proxy = actionProxyFactory.createActionProxy(null, "chain_with_namespace", null, null);
+        ((SimpleAction)proxy.getAction()).setBlah("%{foo}");
+
+        proxy.execute();
+
+        assertTrue(proxy.getInvocation().getResult() instanceof MockResult);
+        MockResult result = (MockResult) proxy.getInvocation().getResult();
+        assertEquals("%{foo}", result.getInvocation().getProxy().getNamespace());
+    }
+
+    private static class NamespaceActionNameTestActionProxyFactory implements ActionProxyFactory {
         private ActionProxy returnVal;
         private String expectedActionName;
         private String expectedNamespace;
diff --git a/core/src/test/java/com/opensymphony/xwork2/interceptor/AliasInterceptorTest.java b/core/src/test/java/com/opensymphony/xwork2/interceptor/AliasInterceptorTest.java
index 9014125..12ffd86 100644
--- a/core/src/test/java/com/opensymphony/xwork2/interceptor/AliasInterceptorTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/interceptor/AliasInterceptorTest.java
@@ -28,6 +28,10 @@ import org.apache.struts2.dispatcher.HttpParameters;
 import java.util.HashMap;
 import java.util.Map;
 
+import static com.opensymphony.xwork2.security.DefaultAcceptedPatternsCheckerTest.ACCEPT_ALL_PATTERNS_CHECKER;
+import static com.opensymphony.xwork2.security.DefaultExcludedPatternsCheckerTest.NO_EXCLUSION_PATTERNS_CHECKER;
+import static org.junit.Assert.assertNotEquals;
+
 
 /**
  * AliasInterceptorTest
@@ -66,6 +70,92 @@ public class AliasInterceptorTest extends XWorkTestCase {
         assertNull(actionOne.getBlah());    //  WW-5087
     }
 
+    public void testNameNotAccepted() throws Exception {
+        Map<String, Object> params = new HashMap<>();
+        params.put("aliasSource", "source here");
+
+        Map<String, String> httpParams = new HashMap<>();
+        httpParams.put("name", "getAliasSource()");
+        httpParams.put("value", "aliasDest");
+        params.put("parameters", HttpParameters.create(httpParams).build());
+
+
+        XmlConfigurationProvider provider = new XmlConfigurationProvider("xwork-sample.xml");
+        container.inject(provider);
+        loadConfigurationProviders(provider);
+        ActionProxy proxy = actionProxyFactory.createActionProxy("", "dynamicAliasTest", null, params);
+        SimpleAction actionOne = (SimpleAction) proxy.getAction();
+        actionOne.setAliasSource("name to be copied");
+
+        // prevent ERROR result
+        actionOne.setFoo(-1);
+        actionOne.setBar(1);
+
+        proxy.execute();
+        assertEquals("name to be copied", actionOne.getAliasSource());
+        assertNotEquals(actionOne.getAliasSource(), actionOne.getAliasDest());
+
+        proxy = actionProxyFactory.createActionProxy("", "dynamicAliasTest", null, params);
+        ((AliasInterceptor)proxy.getConfig().getInterceptors().get(1).getInterceptor())
+                .setExcludedPatterns(NO_EXCLUSION_PATTERNS_CHECKER);
+        ((AliasInterceptor)proxy.getConfig().getInterceptors().get(1).getInterceptor())
+                .setAcceptedPatterns(ACCEPT_ALL_PATTERNS_CHECKER);
+
+        actionOne = (SimpleAction) proxy.getAction();
+        actionOne.setAliasSource("name to be copied");
+
+        // prevent ERROR result
+        actionOne.setFoo(-1);
+        actionOne.setBar(1);
+
+        proxy.execute();
+        assertEquals("name to be copied", actionOne.getAliasSource());
+        assertEquals(actionOne.getAliasSource(), actionOne.getAliasDest());
+    }
+
+    public void testValueNotAccepted() throws Exception {
+        Map<String, Object> params = new HashMap<>();
+        params.put("aliasSource", "source here");
+
+        Map<String, String> httpParams = new HashMap<>();
+        httpParams.put("name", "aliasSource");
+        httpParams.put("value", "[0].aliasDest");
+        params.put("parameters", HttpParameters.create(httpParams).build());
+
+
+        XmlConfigurationProvider provider = new XmlConfigurationProvider("xwork-sample.xml");
+        container.inject(provider);
+        loadConfigurationProviders(provider);
+        ActionProxy proxy = actionProxyFactory.createActionProxy("", "dynamicAliasTest", null, params);
+        SimpleAction actionOne = (SimpleAction) proxy.getAction();
+        actionOne.setAliasSource("name to be copied");
+
+        // prevent ERROR result
+        actionOne.setFoo(-1);
+        actionOne.setBar(1);
+
+        proxy.execute();
+        assertEquals("name to be copied", actionOne.getAliasSource());
+        assertNotEquals(actionOne.getAliasSource(), actionOne.getAliasDest());
+
+        proxy = actionProxyFactory.createActionProxy("", "dynamicAliasTest", null, params);
+        ((AliasInterceptor) proxy.getConfig().getInterceptors().get(1).getInterceptor())
+                .setExcludedPatterns(NO_EXCLUSION_PATTERNS_CHECKER);
+        ((AliasInterceptor) proxy.getConfig().getInterceptors().get(1).getInterceptor())
+                .setAcceptedPatterns(ACCEPT_ALL_PATTERNS_CHECKER);
+
+        actionOne = (SimpleAction) proxy.getAction();
+        actionOne.setAliasSource("name to be copied");
+
+        // prevent ERROR result
+        actionOne.setFoo(-1);
+        actionOne.setBar(1);
+
+        proxy.execute();
+        assertEquals("name to be copied", actionOne.getAliasSource());
+        assertEquals(actionOne.getAliasSource(), actionOne.getAliasDest());
+    }
+
     public void testNotExisting() throws Exception {
         Map<String, Object> params = new HashMap<>();
         Map<String, Object> httpParams = new HashMap<>();
diff --git a/core/src/test/java/com/opensymphony/xwork2/security/DefaultAcceptedPatternsCheckerTest.java b/core/src/test/java/com/opensymphony/xwork2/security/DefaultAcceptedPatternsCheckerTest.java
index 1dc8d8a..d69ac8f 100644
--- a/core/src/test/java/com/opensymphony/xwork2/security/DefaultAcceptedPatternsCheckerTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/security/DefaultAcceptedPatternsCheckerTest.java
@@ -174,4 +174,32 @@ public class DefaultAcceptedPatternsCheckerTest extends XWorkTestCase {
 
         assertTrue("dmi isn't accepted", accepted.isAccepted());
     }
+
+
+    public static final AcceptedPatternsChecker ACCEPT_ALL_PATTERNS_CHECKER = new AcceptedPatternsChecker() {
+        @Override
+        public IsAccepted isAccepted(String value) {
+            return IsAccepted.yes(".*");
+        }
+
+        @Override
+        public void setAcceptedPatterns(String commaDelimitedPatterns) {
+
+        }
+
+        @Override
+        public void setAcceptedPatterns(String[] patterns) {
+
+        }
+
+        @Override
+        public void setAcceptedPatterns(Set<String> patterns) {
+
+        }
+
+        @Override
+        public Set<Pattern> getAcceptedPatterns() {
+            return null;
+        }
+    };
 }
\ No newline at end of file
diff --git a/core/src/test/java/com/opensymphony/xwork2/security/DefaultExcludedPatternsCheckerTest.java b/core/src/test/java/com/opensymphony/xwork2/security/DefaultExcludedPatternsCheckerTest.java
index cec0c1f..d6e3758 100644
--- a/core/src/test/java/com/opensymphony/xwork2/security/DefaultExcludedPatternsCheckerTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/security/DefaultExcludedPatternsCheckerTest.java
@@ -22,6 +22,7 @@ import com.opensymphony.xwork2.XWorkTestCase;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.regex.Pattern;
@@ -218,4 +219,32 @@ public class DefaultExcludedPatternsCheckerTest extends XWorkTestCase {
             // Expected result
         }
     }
+
+
+    public static final ExcludedPatternsChecker NO_EXCLUSION_PATTERNS_CHECKER = new ExcludedPatternsChecker() {
+        @Override
+        public IsExcluded isExcluded(String value) {
+            return IsExcluded.no(new HashSet<Pattern>());
+        }
+
+        @Override
+        public void setExcludedPatterns(String commaDelimitedPatterns) {
+
+        }
+
+        @Override
+        public void setExcludedPatterns(String[] patterns) {
+
+        }
+
+        @Override
+        public void setExcludedPatterns(Set<String> patterns) {
+
+        }
+
+        @Override
+        public Set<Pattern> getExcludedPatterns() {
+            return null;
+        }
+    };
 }
diff --git a/core/src/test/java/com/opensymphony/xwork2/security/DefaultNotExcludedAcceptedPatternsCheckerTest.java b/core/src/test/java/com/opensymphony/xwork2/security/DefaultNotExcludedAcceptedPatternsCheckerTest.java
new file mode 100644
index 0000000..85f5b71
--- /dev/null
+++ b/core/src/test/java/com/opensymphony/xwork2/security/DefaultNotExcludedAcceptedPatternsCheckerTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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 com.opensymphony.xwork2.security;
+
+import com.opensymphony.xwork2.XWorkTestCase;
+
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import static org.junit.Assert.*;
+
+public class DefaultNotExcludedAcceptedPatternsCheckerTest extends XWorkTestCase {
+
+    public void testNoExclusionAcceptAllPatternsChecker() {
+        assertTrue(NO_EXCLUSION_ACCEPT_ALL_PATTERNS_CHECKER.isAllowed("%{1+1}").isAllowed());
+    }
+
+    public static final NotExcludedAcceptedPatternsChecker NO_EXCLUSION_ACCEPT_ALL_PATTERNS_CHECKER
+            = new NotExcludedAcceptedPatternsChecker() {
+        @Override
+        public IsAllowed isAllowed(String value) {
+            return IsAllowed.yes("*");
+        }
+
+        @Override
+        public IsAccepted isAccepted(String value) {
+            return null;
+        }
+
+        @Override
+        public void setAcceptedPatterns(String commaDelimitedPatterns) {
+
+        }
+
+        @Override
+        public void setAcceptedPatterns(String[] patterns) {
+
+        }
+
+        @Override
+        public void setAcceptedPatterns(Set<String> patterns) {
+
+        }
+
+        @Override
+        public Set<Pattern> getAcceptedPatterns() {
+            return null;
+        }
+
+        @Override
+        public IsExcluded isExcluded(String value) {
+            return null;
+        }
+
+        @Override
+        public void setExcludedPatterns(String commaDelimitedPatterns) {
+
+        }
+
+        @Override
+        public void setExcludedPatterns(String[] patterns) {
+
+        }
+
+        @Override
+        public void setExcludedPatterns(Set<String> patterns) {
+
+        }
+
+        @Override
+        public Set<Pattern> getExcludedPatterns() {
+            return null;
+        }
+    };
+}
\ No newline at end of file
diff --git a/core/src/test/java/org/apache/struts2/TestConfigurationProvider.java b/core/src/test/java/org/apache/struts2/TestConfigurationProvider.java
index 9e093f8..ce6bcc1 100644
--- a/core/src/test/java/org/apache/struts2/TestConfigurationProvider.java
+++ b/core/src/test/java/org/apache/struts2/TestConfigurationProvider.java
@@ -33,7 +33,9 @@ import com.opensymphony.xwork2.inject.ContainerBuilder;
 import com.opensymphony.xwork2.interceptor.ParametersInterceptor;
 import com.opensymphony.xwork2.mock.MockResult;
 import com.opensymphony.xwork2.security.DefaultExcludedPatternsChecker;
+import com.opensymphony.xwork2.security.DefaultNotExcludedAcceptedPatternsChecker;
 import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
+import com.opensymphony.xwork2.security.NotExcludedAcceptedPatternsChecker;
 import com.opensymphony.xwork2.util.location.LocatableProperties;
 import com.opensymphony.xwork2.validator.ValidationInterceptor;
 import org.apache.struts2.result.ServletDispatcherResult;
@@ -42,6 +44,7 @@ import org.apache.struts2.interceptor.TokenSessionStoreInterceptor;
 import org.apache.struts2.views.jsp.ui.DoubleValidationAction;
 
 import java.util.HashMap;
+import java.util.Map;
 
 
 /**
@@ -74,7 +77,7 @@ public class TestConfigurationProvider implements ConfigurationProvider {
      */
     public void loadPackages() {
 
-        HashMap successParams = new HashMap();
+        Map<String, String> successParams = new HashMap<>();
         successParams.put("propertyName", "executionCount");
         successParams.put("expectedValue", "1");
 
@@ -149,9 +152,7 @@ public class TestConfigurationProvider implements ConfigurationProvider {
     }
 
     /**
-     * Tells whether the ConfigurationProvider should reload its configuration
-     *
-     * @return
+     * @return whether the ConfigurationProvider should reload its configuration
      */
     public boolean needsReload() {
         return false;
@@ -167,5 +168,8 @@ public class TestConfigurationProvider implements ConfigurationProvider {
         if (!builder.contains(ExcludedPatternsChecker.class)) {
             builder.factory(ExcludedPatternsChecker.class, DefaultExcludedPatternsChecker.class);
         }
+        if (!builder.contains(NotExcludedAcceptedPatternsChecker.class)) {
+            builder.factory(NotExcludedAcceptedPatternsChecker.class, DefaultNotExcludedAcceptedPatternsChecker.class);
+        }
     }
 }
diff --git a/core/src/test/java/org/apache/struts2/components/UIBeanTest.java b/core/src/test/java/org/apache/struts2/components/UIBeanTest.java
index ca2ffa2..3b4dca5 100644
--- a/core/src/test/java/org/apache/struts2/components/UIBeanTest.java
+++ b/core/src/test/java/org/apache/struts2/components/UIBeanTest.java
@@ -32,9 +32,11 @@ import org.springframework.mock.web.MockHttpServletResponse;
 import java.util.Collections;
 import java.util.Map;
 
+import static com.opensymphony.xwork2.security.DefaultNotExcludedAcceptedPatternsCheckerTest.NO_EXCLUSION_ACCEPT_ALL_PATTERNS_CHECKER;
+
 public class UIBeanTest extends StrutsInternalTestCase {
 
-    public void testPopulateComponentHtmlId1() throws Exception {
+    public void testPopulateComponentHtmlId1() {
         ValueStack stack = ActionContext.getContext().getValueStack();
         MockHttpServletRequest req = new MockHttpServletRequest();
         MockHttpServletResponse res = new MockHttpServletResponse();
@@ -50,7 +52,7 @@ public class UIBeanTest extends StrutsInternalTestCase {
         assertEquals("txtFldId", txtFld.getParameters().get("id"));
     }
 
-    public void testPopulateComponentHtmlIdWithOgnl() throws Exception {
+    public void testPopulateComponentHtmlIdWithOgnl() {
         ValueStack stack = ActionContext.getContext().getValueStack();
         MockHttpServletRequest req = new MockHttpServletRequest();
         MockHttpServletResponse res = new MockHttpServletResponse();
@@ -66,7 +68,7 @@ public class UIBeanTest extends StrutsInternalTestCase {
         assertEquals("formId_txtFldName1", txtFld.getParameters().get("id"));
     }
 
-    public void testPopulateComponentHtmlId2() throws Exception {
+    public void testPopulateComponentHtmlId2() {
         ValueStack stack = ActionContext.getContext().getValueStack();
         MockHttpServletRequest req = new MockHttpServletRequest();
         MockHttpServletResponse res = new MockHttpServletResponse();
@@ -82,7 +84,7 @@ public class UIBeanTest extends StrutsInternalTestCase {
         assertEquals("formId_txtFldName", txtFld.getParameters().get("id"));
     }
 
-    public void testPopulateComponentHtmlWithoutNameAndId() throws Exception {
+    public void testPopulateComponentHtmlWithoutNameAndId() {
         ValueStack stack = ActionContext.getContext().getValueStack();
         MockHttpServletRequest req = new MockHttpServletRequest();
         MockHttpServletResponse res = new MockHttpServletResponse();
@@ -94,10 +96,10 @@ public class UIBeanTest extends StrutsInternalTestCase {
 
         txtFld.populateComponentHtmlId(form);
 
-        assertEquals(null, txtFld.getParameters().get("id"));
+        assertNull(txtFld.getParameters().get("id"));
     }
 
-    public void testEscape() throws Exception {
+    public void testEscape() {
         ValueStack stack = ActionContext.getContext().getValueStack();
         MockHttpServletRequest req = new MockHttpServletRequest();
         MockHttpServletResponse res = new MockHttpServletResponse();
@@ -110,11 +112,11 @@ public class UIBeanTest extends StrutsInternalTestCase {
         assertEquals(bean.escape("hello[world"), "hello_world");
         assertEquals(bean.escape("hello.world"), "hello_world");
         assertEquals(bean.escape("hello]world"), "hello_world");
-        assertEquals(bean.escape("hello!world"), "hello!world");
-        assertEquals(bean.escape("hello!@#$%^&*()world"), "hello!@#$%^&*()world");
+        assertEquals(bean.escape("hello!world"), "hello_world");
+        assertEquals(bean.escape("hello!@#$%^&*()world"), "hello__________world");
     }
 
-    public void testEscapeId() throws Exception {
+    public void testEscapeId() {
         ValueStack stack = ActionContext.getContext().getValueStack();
         MockHttpServletRequest req = new MockHttpServletRequest();
         MockHttpServletResponse res = new MockHttpServletResponse();
@@ -128,7 +130,7 @@ public class UIBeanTest extends StrutsInternalTestCase {
         assertEquals("formId_foo_bar", txtFld.getParameters().get("id"));
     }
 
-    public void testGetThemeFromForm() throws Exception {
+    public void testGetThemeFromForm() {
         ValueStack stack = ActionContext.getContext().getValueStack();
         MockHttpServletRequest req = new MockHttpServletRequest();
         MockHttpServletResponse res = new MockHttpServletResponse();
@@ -140,29 +142,29 @@ public class UIBeanTest extends StrutsInternalTestCase {
         assertEquals("foo", txtFld.getTheme());
     }
 
-    public void testGetThemeFromContext() throws Exception {
+    public void testGetThemeFromContext() {
         ValueStack stack = ActionContext.getContext().getValueStack();
         MockHttpServletRequest req = new MockHttpServletRequest();
         MockHttpServletResponse res = new MockHttpServletResponse();
-        Map context = Collections.singletonMap("theme", "bar");
+        Map<String, String> context = Collections.singletonMap("theme", "bar");
         ActionContext.getContext().put("attr", context);
 
         TextField txtFld = new TextField(stack, req, res);
         assertEquals("bar", txtFld.getTheme());
     }
 
-    public void testGetThemeFromContextNonString() throws Exception {
+    public void testGetThemeFromContextNonString() {
         ValueStack stack = ActionContext.getContext().getValueStack();
         MockHttpServletRequest req = new MockHttpServletRequest();
         MockHttpServletResponse res = new MockHttpServletResponse();
-        Map context = Collections.singletonMap("theme", 12);
+        Map<String, Integer> context = Collections.singletonMap("theme", 12);
         ActionContext.getContext().put("attr", context);
 
         TextField txtFld = new TextField(stack, req, res);
         assertEquals("12", txtFld.getTheme());
     }
 
-    public void testMergeTemplateNullEngineException() throws Exception {
+    public void testMergeTemplateNullEngineException() {
         ValueStack stack = ActionContext.getContext().getValueStack();
         MockHttpServletRequest req = new MockHttpServletRequest();
         MockHttpServletResponse res = new MockHttpServletResponse();
@@ -184,7 +186,7 @@ public class UIBeanTest extends StrutsInternalTestCase {
         }
     }
 
-    public void testBuildTemplate() throws Exception {
+    public void testBuildTemplate() {
         String defaultTemplateName = "default";
         String customTemplateName = "custom";
         ValueStack stack = ActionContext.getContext().getValueStack();
@@ -200,14 +202,14 @@ public class UIBeanTest extends StrutsInternalTestCase {
         assertEquals(customTemplateName, customTemplate.getName());
     }
 
-    public void testGetTemplateDirExplicit() throws Exception {
+    public void testGetTemplateDirExplicit() {
         String explicitTemplateDir = "explicitTemplateDirectory";
         String attrTemplateDir = "attrTemplateDirectory";
         String defaultTemplateDir = "defaultTemplateDirectory";
         ValueStack stack = ActionContext.getContext().getValueStack();
         MockHttpServletRequest req = new MockHttpServletRequest();
         MockHttpServletResponse res = new MockHttpServletResponse();
-        Map context = Collections.singletonMap("templateDir", attrTemplateDir);
+        Map<String, String> context = Collections.singletonMap("templateDir", attrTemplateDir);
         ActionContext.getContext().put("attr", context);
 
         TextField txtFld = new TextField(stack, req, res);
@@ -217,13 +219,13 @@ public class UIBeanTest extends StrutsInternalTestCase {
         assertEquals(explicitTemplateDir, txtFld.getTemplateDir());
     }
 
-    public void testGetTemplateDirAttr() throws Exception {
+    public void testGetTemplateDirAttr() {
         String attrTemplateDir = "attrTemplateDirectory";
         String defaultTemplateDir = "defaultTemplateDirectory";
         ValueStack stack = ActionContext.getContext().getValueStack();
         MockHttpServletRequest req = new MockHttpServletRequest();
         MockHttpServletResponse res = new MockHttpServletResponse();
-        Map context = Collections.singletonMap("templateDir", attrTemplateDir);
+        Map<String, String> context = Collections.singletonMap("templateDir", attrTemplateDir);
         ActionContext.getContext().put("attr", context);
 
         TextField txtFld = new TextField(stack, req, res);
@@ -232,7 +234,7 @@ public class UIBeanTest extends StrutsInternalTestCase {
         assertEquals(attrTemplateDir, txtFld.getTemplateDir());
     }
 
-    public void testGetTemplateDirDefault() throws Exception {
+    public void testGetTemplateDirDefault() {
         String defaultTemplateDir = "defaultTemplateDirectory";
         ValueStack stack = ActionContext.getContext().getValueStack();
         MockHttpServletRequest req = new MockHttpServletRequest();
@@ -244,7 +246,7 @@ public class UIBeanTest extends StrutsInternalTestCase {
         assertEquals(defaultTemplateDir, txtFld.getTemplateDir());
     }
 
-    public void testGetTemplateDirNoneSet() throws Exception {
+    public void testGetTemplateDirNoneSet() {
         ValueStack stack = ActionContext.getContext().getValueStack();
         MockHttpServletRequest req = new MockHttpServletRequest();
         MockHttpServletResponse res = new MockHttpServletResponse();
@@ -295,10 +297,58 @@ public class UIBeanTest extends StrutsInternalTestCase {
         });
 
         TextField txtFld = new TextField(stack, req, res);
+        container.inject(txtFld);
         txtFld.setName("%{myValue}");
         txtFld.evaluateParams();
 
         assertEquals("%{myBad}", txtFld.getParameters().get("nameValue"));
+        assertEquals("%{myBad}", txtFld.getParameters().get("name"));
+    }
+
+    public void testValueNameParameterNotAccepted() {
+        ValueStack stack = ActionContext.getContext().getValueStack();
+        MockHttpServletRequest req = new MockHttpServletRequest();
+        MockHttpServletResponse res = new MockHttpServletResponse();
+
+        stack.push(new Object() {
+            public String getMyValueName() {
+                return "getMyValue()";
+            }
+            public String getMyValue() {
+                return "value";
+            }
+        });
+
+        TextField txtFld = new TextField(stack, req, res);
+        container.inject(txtFld);
+        txtFld.setName("%{myValueName}");
+        txtFld.evaluateParams();
+        assertEquals("getMyValue()", txtFld.getParameters().get("name"));
+        assertEquals("getMyValue()", txtFld.getParameters().get("nameValue"));
+
+        txtFld.setNotExcludedAcceptedPatterns(NO_EXCLUSION_ACCEPT_ALL_PATTERNS_CHECKER);
+        txtFld.evaluateParams();
+        assertEquals("getMyValue()", txtFld.getParameters().get("name"));
+        assertEquals("value", txtFld.getParameters().get("nameValue"));
+    }
+
+    public void testValueNameParameterGetterAccepted() {
+        ValueStack stack = ActionContext.getContext().getValueStack();
+        MockHttpServletRequest req = new MockHttpServletRequest();
+        MockHttpServletResponse res = new MockHttpServletResponse();
+
+        stack.push(new Object() {
+            public String getMyValue() {
+                return "value";
+            }
+        });
+
+        TextField txtFld = new TextField(stack, req, res);
+        container.inject(txtFld);
+        txtFld.setName("getMyValue()");
+        txtFld.evaluateParams();
+        assertEquals("getMyValue()", txtFld.getParameters().get("name"));
+        assertEquals("value", txtFld.getParameters().get("nameValue"));
     }
 
     public void testSetClass() {
diff --git a/core/src/test/java/org/apache/struts2/result/PostbackResultTest.java b/core/src/test/java/org/apache/struts2/result/PostbackResultTest.java
new file mode 100644
index 0000000..8d64340
--- /dev/null
+++ b/core/src/test/java/org/apache/struts2/result/PostbackResultTest.java
@@ -0,0 +1,135 @@
+/*
+ * 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.struts2.result;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.ActionProxy;
+import com.opensymphony.xwork2.Result;
+import com.opensymphony.xwork2.util.ValueStack;
+import org.apache.struts2.ServletActionContext;
+import org.apache.struts2.StrutsInternalTestCase;
+import org.apache.struts2.dispatcher.mapper.ActionMapper;
+import org.easymock.IMocksControl;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+
+import static org.easymock.EasyMock.createControl;
+import static org.easymock.EasyMock.expect;
+
+public class PostbackResultTest extends StrutsInternalTestCase {
+
+    public void testWithNoNamespace() throws Exception {
+
+        ActionContext context = ActionContext.getContext();
+        ValueStack stack = context.getValueStack();
+        MockHttpServletRequest req = new MockHttpServletRequest();
+        MockHttpServletResponse res = new MockHttpServletResponse();
+        context.put(ServletActionContext.HTTP_REQUEST, req);
+        context.put(ServletActionContext.HTTP_RESPONSE, res);
+
+        PostbackResult result = new PostbackResult();
+        result.setActionName("myAction${1-1}");
+        result.setPrependServletContext(false);
+
+        IMocksControl control = createControl();
+        ActionProxy mockActionProxy = control.createMock(ActionProxy.class);
+        ActionInvocation mockInvocation = control.createMock(ActionInvocation.class);
+        expect(mockInvocation.getInvocationContext()).andReturn(context).anyTimes();
+        expect(mockInvocation.getStack()).andReturn(stack).anyTimes();
+        expect(mockInvocation.getProxy()).andReturn(mockActionProxy);
+        expect(mockActionProxy.getNamespace()).andReturn("${1-1}");
+
+        control.replay();
+        result.setActionMapper(container.getInstance(ActionMapper.class));
+        result.execute(mockInvocation);
+        assertEquals("<!DOCTYPE html><html><body><form action=\"${1-1}/myAction0.action\" method=\"POST\">" +
+                "<script>setTimeout(function(){document.forms[0].submit();},0);</script></html>", res.getContentAsString());
+
+        control.verify();
+    }
+
+    public void testWithNamespace() throws Exception {
+
+        ActionContext context = ActionContext.getContext();
+        ValueStack stack = context.getValueStack();
+        MockHttpServletRequest req = new MockHttpServletRequest();
+        MockHttpServletResponse res = new MockHttpServletResponse();
+        context.put(ServletActionContext.HTTP_REQUEST, req);
+        context.put(ServletActionContext.HTTP_RESPONSE, res);
+
+        PostbackResult result = new PostbackResult();
+        result.setActionName("myAction${1-1}");
+        result.setNamespace("myNamespace${1-1}");
+        result.setPrependServletContext(false);
+
+        IMocksControl control = createControl();
+        ActionInvocation mockInvocation = control.createMock(ActionInvocation.class);
+        expect(mockInvocation.getInvocationContext()).andReturn(context).anyTimes();
+        expect(mockInvocation.getStack()).andReturn(stack).anyTimes();
+
+        control.replay();
+        result.setActionMapper(container.getInstance(ActionMapper.class));
+        result.execute(mockInvocation);
+        assertEquals("<!DOCTYPE html><html><body><form action=\"myNamespace0/myAction0.action\" method=\"POST\">" +
+                "<script>setTimeout(function(){document.forms[0].submit();},0);</script></html>", res.getContentAsString());
+
+        control.verify();
+    }
+
+    public void testExpressionNamespace() throws Exception {
+
+        ActionContext context = ActionContext.getContext();
+        context.getContextMap().put("namespaceName", "${1-1}");
+        context.getContextMap().put("actionName", "${1-1}");
+        context.getContextMap().put("methodName", "${1-1}");
+        ValueStack stack = context.getValueStack();
+        MockHttpServletRequest req = new MockHttpServletRequest();
+        MockHttpServletResponse res = new MockHttpServletResponse();
+        context.put(ServletActionContext.HTTP_REQUEST, req);
+        context.put(ServletActionContext.HTTP_RESPONSE, res);
+
+        PostbackResult result = new PostbackResult();
+        result.setNamespace("/myNamespace${#namespaceName}");
+        result.setActionName("myAction${#actionName}");
+        result.setMethod("myMethod${#methodName}");
+        result.setPrependServletContext(false);
+
+        IMocksControl control = createControl();
+        ActionInvocation mockInvocation = control.createMock(ActionInvocation.class);
+        expect(mockInvocation.getInvocationContext()).andReturn(context).anyTimes();
+        expect(mockInvocation.getStack()).andReturn(stack).anyTimes();
+
+        control.replay();
+        result.setActionMapper(container.getInstance(ActionMapper.class));
+        result.execute(mockInvocation);
+        assertEquals("<!DOCTYPE html><html><body><form action=\"/myNamespace${1-1}/myAction${1-1}!myMethod${1-1}.action\" method=\"POST\">" +
+                "<script>setTimeout(function(){document.forms[0].submit();},0);</script></html>", res.getContentAsString());
+
+        req = new MockHttpServletRequest();
+        res = new MockHttpServletResponse();
+        context.put(ServletActionContext.HTTP_REQUEST, req);
+        context.put(ServletActionContext.HTTP_RESPONSE, res);
+        result.execute(mockInvocation);
+        assertEquals("<!DOCTYPE html><html><body><form action=\"/myNamespace0/myAction0!myMethod0.action\" method=\"POST\">" +
+                "<script>setTimeout(function(){document.forms[0].submit();},0);</script></html>", res.getContentAsString());
+
+        control.verify();
+    }
+}
diff --git a/core/src/test/java/org/apache/struts2/result/ServletActionRedirectResultTest.java b/core/src/test/java/org/apache/struts2/result/ServletActionRedirectResultTest.java
index b7ed61c..acd79c1 100644
--- a/core/src/test/java/org/apache/struts2/result/ServletActionRedirectResultTest.java
+++ b/core/src/test/java/org/apache/struts2/result/ServletActionRedirectResultTest.java
@@ -108,6 +108,141 @@ public class ServletActionRedirectResultTest extends StrutsInternalTestCase {
         control.verify();
     }
 
+    public void testExpressionParameterInResultWithConditionParseOn() throws Exception {
+
+        ResultConfig resultConfig = new ResultConfig.Builder("", "")
+                .addParam("actionName", "someActionName")
+                .addParam("namespace", "someNamespace")
+                .addParam("encode", "true")
+                .addParam("parse", "true")
+                .addParam("location", "someLocation")
+                .addParam("prependServletContext", "true")
+                .addParam("method", "someMethod")
+                .addParam("statusCode", "333")
+                .addParam("param1", "${#value1}")
+                .addParam("param2", "${#value2}")
+                .addParam("param3", "${#value3}")
+                .addParam("anchor", "${#fragment}")
+                .build();
+
+
+
+        ActionContext context = ActionContext.getContext();
+        ValueStack stack = context.getValueStack();
+        context.getContextMap().put("value1", "value 1");
+        context.getContextMap().put("value2", "value 2");
+        context.getContextMap().put("value3", "value 3");
+        context.getContextMap().put("namespaceName", "${1-1}");
+        context.getContextMap().put("actionName", "${1-1}");
+        context.getContextMap().put("methodName", "${1-1}");
+        MockHttpServletRequest req = new MockHttpServletRequest();
+        MockHttpServletResponse res = new MockHttpServletResponse();
+        context.put(ServletActionContext.HTTP_REQUEST, req);
+        context.put(ServletActionContext.HTTP_RESPONSE, res);
+
+
+        Map<String, ResultConfig> results= new HashMap<>();
+        results.put("myResult", resultConfig);
+
+        ActionConfig actionConfig = new ActionConfig.Builder("", "", "")
+                .addResultConfigs(results).build();
+
+        ServletActionRedirectResult result = new ServletActionRedirectResult();
+        result.setNamespace("/myNamespace${#namespaceName}");
+        result.setActionName("myAction${#actionName}");
+        result.setMethod("myMethod${#methodName}");
+        result.setParse(true);
+        result.setEncode(false);
+        result.setPrependServletContext(false);
+        result.setAnchor("fragment");
+        result.setUrlHelper(new DefaultUrlHelper());
+
+        IMocksControl control = createControl();
+        ActionProxy mockActionProxy = control.createMock(ActionProxy.class);
+        ActionInvocation mockInvocation = control.createMock(ActionInvocation.class);
+        expect(mockInvocation.getProxy()).andReturn(mockActionProxy).anyTimes();
+        expect(mockInvocation.getResultCode()).andReturn("myResult").anyTimes();
+        expect(mockActionProxy.getConfig()).andReturn(actionConfig).anyTimes();
+        expect(mockInvocation.getInvocationContext()).andReturn(context).anyTimes();
+        expect(mockInvocation.getStack()).andReturn(stack).anyTimes();
+
+        control.replay();
+        result.setActionMapper(container.getInstance(ActionMapper.class));
+        result.execute(mockInvocation);
+        assertEquals("/myNamespace${1-1}/myAction${1-1}!myMethod${1-1}.action?param1=value+1&param2=value+2&param3=value+3#fragment", res.getRedirectedUrl());
+
+        req = new MockHttpServletRequest();
+        res = new MockHttpServletResponse();
+        context.put(ServletActionContext.HTTP_REQUEST, req);
+        context.put(ServletActionContext.HTTP_RESPONSE, res);
+        result.execute(mockInvocation);
+        assertEquals("/myNamespace0/myAction0!myMethod0.action?param1=value+1&param2=value+2&param3=value+3#fragment", res.getRedirectedUrl());
+
+        control.verify();
+    }
+
+    public void testIncludeParameterInResultWithConditionParseOnWithNoNamespace() throws Exception {
+
+        ResultConfig resultConfig = new ResultConfig.Builder("", "")
+                .addParam("actionName", "someActionName")
+                .addParam("namespace", "someNamespace")
+                .addParam("encode", "true")
+                .addParam("parse", "true")
+                .addParam("location", "someLocation")
+                .addParam("prependServletContext", "true")
+                .addParam("method", "someMethod")
+                .addParam("statusCode", "333")
+                .addParam("param1", "${#value1}")
+                .addParam("param2", "${#value2}")
+                .addParam("param3", "${#value3}")
+                .addParam("anchor", "${#fragment}")
+                .build();
+
+
+
+        ActionContext context = ActionContext.getContext();
+        ValueStack stack = context.getValueStack();
+        context.getContextMap().put("value1", "value 1");
+        context.getContextMap().put("value2", "value 2");
+        context.getContextMap().put("value3", "value 3");
+        MockHttpServletRequest req = new MockHttpServletRequest();
+        MockHttpServletResponse res = new MockHttpServletResponse();
+        context.put(ServletActionContext.HTTP_REQUEST, req);
+        context.put(ServletActionContext.HTTP_RESPONSE, res);
+
+
+        Map<String, ResultConfig> results= new HashMap<>();
+        results.put("myResult", resultConfig);
+
+        ActionConfig actionConfig = new ActionConfig.Builder("", "", "")
+                .addResultConfigs(results).build();
+
+        ServletActionRedirectResult result = new ServletActionRedirectResult();
+        result.setActionName("myAction${1-1}");
+        result.setParse(true);
+        result.setEncode(false);
+        result.setPrependServletContext(false);
+        result.setAnchor("fragment");
+        result.setUrlHelper(new DefaultUrlHelper());
+
+        IMocksControl control = createControl();
+        ActionProxy mockActionProxy = control.createMock(ActionProxy.class);
+        ActionInvocation mockInvocation = control.createMock(ActionInvocation.class);
+        expect(mockInvocation.getProxy()).andReturn(mockActionProxy).times(2);
+        expect(mockInvocation.getResultCode()).andReturn("myResult");
+        expect(mockActionProxy.getConfig()).andReturn(actionConfig);
+        expect(mockActionProxy.getNamespace()).andReturn("${1-1}");
+        expect(mockInvocation.getInvocationContext()).andReturn(context);
+        expect(mockInvocation.getStack()).andReturn(stack).anyTimes();
+
+        control.replay();
+        result.setActionMapper(container.getInstance(ActionMapper.class));
+        result.execute(mockInvocation);
+        assertEquals("/${1-1}/myAction0.action?param1=value+1&param2=value+2&param3=value+3#fragment", res.getRedirectedUrl());
+
+        control.verify();
+    }
+
     public void testIncludeParameterInResult() throws Exception {
 
         ResultConfig resultConfig = new ResultConfig.Builder("", "")
diff --git a/core/src/test/java/org/apache/struts2/result/StreamResultTest.java b/core/src/test/java/org/apache/struts2/result/StreamResultTest.java
index 1b46d0b..5661db5 100644
--- a/core/src/test/java/org/apache/struts2/result/StreamResultTest.java
+++ b/core/src/test/java/org/apache/struts2/result/StreamResultTest.java
@@ -33,6 +33,8 @@ import java.io.InputStream;
 import java.net.URI;
 import java.net.URL;
 
+import static com.opensymphony.xwork2.security.DefaultNotExcludedAcceptedPatternsCheckerTest.NO_EXCLUSION_ACCEPT_ALL_PATTERNS_CHECKER;
+
 /**
  * Unit test for {@link StreamResult}.
  *
@@ -213,11 +215,41 @@ public class StreamResultTest extends StrutsInternalTestCase {
         assertEquals("filename=\"logo.png\"", response.getHeader("Content-disposition"));
     }
 
+    public void testStreamResultParseExpression() throws Exception {
+        result.setParse(true);
+        result.setInputName("${streamForImageAsExpression}");
+
+        try {
+            result.doExecute("helloworld", mai);
+            fail("double evaluation?!");
+        } catch (IllegalArgumentException e) {
+            assertEquals("Can not find a java.io.InputStream with the name [getStreamForImage()] in the " +
+                    "invocation stack. Check the <param name=\"inputName\"> tag specified for this action is correct, " +
+                    "not excluded and accepted.", e.getMessage());
+        }
+
+        // verify that above test has really effect
+        result.setNotExcludedAcceptedPatterns(NO_EXCLUSION_ACCEPT_ALL_PATTERNS_CHECKER);
+        assertNull(result.inputStream);
+        result.doExecute("helloworld", mai);
+        assertNotNull(result.inputStream);
+        container.inject(result);   // roll back pattern checkers
+    }
+
+    public void testStreamResultParseGetter() throws Exception {
+        result.setParse(true);
+        result.setInputName("getStreamForImage()");
+        assertNull(result.inputStream);
+        result.doExecute("helloworld", mai);
+        assertNotNull(result.inputStream);
+    }
+
     protected void setUp() throws Exception {
         super.setUp();
         response = new MockHttpServletResponse();
 
         result = new StreamResult();
+        container.inject(result);
         result.setContentLength("${contentLength}");
         stack = ActionContext.getContext().getValueStack();
 
@@ -244,7 +276,7 @@ public class StreamResultTest extends StrutsInternalTestCase {
         mai = null;
     }
 
-    public class MyImageAction implements Action {
+    public static class MyImageAction implements Action {
 
         FileInputStream streamForImage;
         long contentLength;
@@ -257,7 +289,7 @@ public class StreamResultTest extends StrutsInternalTestCase {
             contentLength = file.length();
         }
 
-        public InputStream getStreamForImage() throws Exception {
+        public InputStream getStreamForImage() {
             return streamForImage;
         }
 
@@ -265,7 +297,7 @@ public class StreamResultTest extends StrutsInternalTestCase {
             return SUCCESS;
         }
 
-        public long getContentLength() throws Exception {
+        public long getContentLength() {
             return contentLength;
         }
 
@@ -273,6 +305,10 @@ public class StreamResultTest extends StrutsInternalTestCase {
             return "streamForImage";
         }
 
+        public String getStreamForImageAsExpression() {
+            return "getStreamForImage()";
+        }
+
         public String getContentCharSetMethod() {
             return "UTF-8";
         }
diff --git a/core/src/test/java/org/apache/struts2/util/StrutsUtilTest.java b/core/src/test/java/org/apache/struts2/util/StrutsUtilTest.java
index 02be0f2..4cf0abb 100644
--- a/core/src/test/java/org/apache/struts2/util/StrutsUtilTest.java
+++ b/core/src/test/java/org/apache/struts2/util/StrutsUtilTest.java
@@ -49,7 +49,7 @@ public class StrutsUtilTest extends StrutsInternalTestCase {
         assertTrue(o instanceof TestAction);
     }
 
-    public void testIsTrueMethod() throws Exception {
+    public void testIsTrueMethod() {
         stack.push(new Object() {
             public String getMyString() {
                 return "myString";
@@ -64,7 +64,7 @@ public class StrutsUtilTest extends StrutsInternalTestCase {
         assertFalse(strutsUtil.isTrue("getMyBoolean(false)"));
     }
 
-    public void testFindStringMethod() throws Exception {
+    public void testFindStringMethod() {
         stack.push(new Object() {
             public String getMyString() {
                 return "myString";
@@ -89,13 +89,13 @@ public class StrutsUtilTest extends StrutsInternalTestCase {
     }
 
 
-    public void testUrlEncodeMethod() throws Exception {
+    public void testUrlEncodeMethod() {
         assertEquals(
                 strutsUtil.urlEncode("http://www.opensymphony.com/action2/index.jsp?param1=value1"),
                 "http%3A%2F%2Fwww.opensymphony.com%2Faction2%2Findex.jsp%3Fparam1%3Dvalue1");
     }
 
-    public void testBuildUrlMethod() throws Exception {
+    public void testBuildUrlMethod() {
         request.setContextPath("/myContextPath");
         assertEquals(strutsUtil.buildUrl("/someUrl?param1=value1"), "/myContextPath/someUrl?param1=value1");
     }
@@ -123,21 +123,29 @@ public class StrutsUtilTest extends StrutsInternalTestCase {
 
 
 
-    public void testGetTextMethod() throws Exception {
+    public void testGetTextMethod() {
         // this should be in xwork-messages.properties (included by default
         // by LocalizedTextUtil
-        assertNotNull(strutsUtil.getText("xwork.error.action.execution"));
-        assertEquals(strutsUtil.getText("xwork.error.action.execution"), "Error during Action invocation");
+        String expression = "xwork.error.action.execution";
+        String text = strutsUtil.getText(expression);
+        assertNotNull(text);
+        assertEquals(text, "Error during Action invocation");
+    }
+
+    public void testGetTextMethodWithSingleQuote() {
+        String expression = "xwork.error.action.execution') + getText('xwork.error.action.execution";
+        String text = strutsUtil.getText(expression);
+        assertNull(text);
     }
 
 
-    public void testGetContextMethod() throws Exception {
+    public void testGetContextMethod() {
         request.setContextPath("/myContext");
         assertEquals(strutsUtil.getContext(), "/myContext");
     }
 
 
-    public void testMakeSelectListMethod() throws Exception {
+    public void testMakeSelectListMethod() {
         String[] selectedList = new String[] { "Car", "Airplane", "Bus" };
         List list = new ArrayList();
         list.add("Lorry");
@@ -152,44 +160,43 @@ public class StrutsUtilTest extends StrutsInternalTestCase {
         assertEquals(listMade.size(), 3);
         assertEquals(((ListEntry)listMade.get(0)).getKey(), "Lorry");
         assertEquals(((ListEntry)listMade.get(0)).getValue(), "Lorry");
-        assertEquals(((ListEntry)listMade.get(0)).getIsSelected(), false);
+        assertFalse(((ListEntry) listMade.get(0)).getIsSelected());
         assertEquals(((ListEntry)listMade.get(1)).getKey(), "Car");
         assertEquals(((ListEntry)listMade.get(1)).getValue(), "Car");
-        assertEquals(((ListEntry)listMade.get(1)).getIsSelected(), true);
+        assertTrue(((ListEntry) listMade.get(1)).getIsSelected());
         assertEquals(((ListEntry)listMade.get(2)).getKey(), "Helicopter");
         assertEquals(((ListEntry)listMade.get(2)).getValue(), "Helicopter");
-        assertEquals(((ListEntry)listMade.get(2)).getIsSelected(), false);
+        assertFalse(((ListEntry) listMade.get(2)).getIsSelected());
     }
 
-    public void testToInt() throws Exception {
-        assertEquals(strutsUtil.toInt(11l), 11);
+    public void testToInt() {
+        assertEquals(strutsUtil.toInt(11L), 11);
     }
 
 
-    public void testToLong() throws Exception {
-        assertEquals(strutsUtil.toLong(11), 11l);
+    public void testToLong() {
+        assertEquals(strutsUtil.toLong(11), 11L);
     }
 
 
-    public void testToString() throws Exception {
+    public void testToString() {
         assertEquals(strutsUtil.toString(1), "1");
-        assertEquals(strutsUtil.toString(11l), "11");
+        assertEquals(strutsUtil.toString(11L), "11");
     }
 
-    public void testTranslateVariables() throws Exception {
+    public void testTranslateVariables() {
         stack.push(new Object() {
             public String getFoo() {
                 return "bar";
             }
         });
-        Object obj1 = strutsUtil.translateVariables("try: %{foo}");
+        String obj1 = strutsUtil.translateVariables("try: %{foo}");
 
         assertNotNull(obj1);
-        assertTrue(obj1 instanceof String);
         assertEquals(obj1, "try: bar");
     }
 
-    public void testTranslateVariablesRecursion() throws Exception {
+    public void testTranslateVariablesRecursion() {
         stack.push(new Object() {
             public String getFoo() {
                 return "%{bar}";
@@ -226,7 +233,7 @@ public class StrutsUtilTest extends StrutsInternalTestCase {
 
     // === internal class to assist in testing
 
-    class InternalMockHttpServletRequest extends MockHttpServletRequest {
+    static class InternalMockHttpServletRequest extends MockHttpServletRequest {
         InternalMockRequestDispatcher dispatcher = null;
         public RequestDispatcher getRequestDispatcher(String path) {
             dispatcher = new InternalMockRequestDispatcher(path);
@@ -238,7 +245,7 @@ public class StrutsUtilTest extends StrutsInternalTestCase {
         }
     }
 
-    class InternalMockRequestDispatcher extends MockRequestDispatcher {
+    static class InternalMockRequestDispatcher extends MockRequestDispatcher {
         private String url;
         boolean included = false;
         public InternalMockRequestDispatcher(String url) {
diff --git a/core/src/test/java/org/apache/struts2/views/freemarker/FreemarkerResultMockedTest.java b/core/src/test/java/org/apache/struts2/views/freemarker/FreemarkerResultMockedTest.java
index 1067ddd..7fd439a 100644
--- a/core/src/test/java/org/apache/struts2/views/freemarker/FreemarkerResultMockedTest.java
+++ b/core/src/test/java/org/apache/struts2/views/freemarker/FreemarkerResultMockedTest.java
@@ -22,9 +22,7 @@ import com.opensymphony.xwork2.ActionContext;
 import com.opensymphony.xwork2.mock.MockActionInvocation;
 import com.opensymphony.xwork2.util.ClassLoaderUtil;
 import com.opensymphony.xwork2.util.ValueStack;
-import com.opensymphony.xwork2.util.fs.DefaultFileManagerFactory;
 import freemarker.template.Configuration;
-import freemarker.template.TemplateException;
 import freemarker.template.TemplateExceptionHandler;
 import org.apache.struts2.ServletActionContext;
 import org.apache.struts2.StrutsInternalTestCase;
@@ -39,8 +37,8 @@ import javax.servlet.ServletContext;
 import java.io.File;
 import java.io.PrintWriter;
 import java.io.StringWriter;
-import java.net.MalformedURLException;
-import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
 
 import static org.apache.struts2.views.jsp.AbstractUITagTest.normalize;
 
@@ -194,7 +192,64 @@ public class FreemarkerResultMockedTest extends StrutsInternalTestCase {
         assertEquals(expected, stringWriter.toString());
     }
 
-    private void init() throws MalformedURLException, URISyntaxException {
+    public void testIterator() throws Exception {
+        File file = new File(FreeMarkerResultTest.class.getResource("iterator.ftl").toURI());
+        EasyMock.expect(servletContext.getRealPath("/tutorial/org/apache/struts2/views/freemarker/iterator.ftl")).andReturn(file.getAbsolutePath());
+
+        file = new File(ClassLoaderUtil.getResource("template/simple/text.ftl", getClass()).toURI());
+        EasyMock.expect(servletContext.getRealPath("/template/simple/text.ftl")).andReturn(file.getAbsolutePath());
+
+        file = new File(ClassLoaderUtil.getResource("template/simple/css.ftl", getClass()).toURI());
+        EasyMock.expect(servletContext.getRealPath("/template/simple/css.ftl")).andReturn(file.getAbsolutePath());
+        EasyMock.expect(servletContext.getRealPath("/template/~~~simple/css.ftl")).andReturn(file.getAbsolutePath());
+
+        file = new File(ClassLoaderUtil.getResource("template/simple/scripting-events.ftl", getClass()).toURI());
+        EasyMock.expect(servletContext.getRealPath("/template/simple/scripting-events.ftl")).andReturn(file.getAbsolutePath());
+        EasyMock.expect(servletContext.getRealPath("/template/~~~simple/scripting-events.ftl")).andReturn(file.getAbsolutePath());
+
+        file = new File(ClassLoaderUtil.getResource("template/simple/common-attributes.ftl", getClass()).toURI());
+        EasyMock.expect(servletContext.getRealPath("/template/simple/common-attributes.ftl")).andReturn(file.getAbsolutePath());
+        EasyMock.expect(servletContext.getRealPath("/template/~~~simple/common-attributes.ftl")).andReturn(file.getAbsolutePath());
+
+        file = new File(ClassLoaderUtil.getResource("template/simple/dynamic-attributes.ftl", getClass()).toURI());
+        EasyMock.expect(servletContext.getRealPath("/template/simple/dynamic-attributes.ftl")).andReturn(file.getAbsolutePath());
+        EasyMock.expect(servletContext.getRealPath("/template/~~~simple/dynamic-attributes.ftl")).andReturn(file.getAbsolutePath());
+
+        EasyMock.replay(servletContext);
+
+        init();
+
+        stack.push(new Object() {
+            List<Object> items = null;
+
+            public List<Object> getItems() {
+                if (items == null) {
+                    items = new ArrayList<>(3);
+                    for (int i = 0; i < 3; i++) {
+                        final int finalI = i;
+                        items.add(new Object() {
+                            public String getName() {
+                                return "value" + finalI;
+                            }
+                        });
+                    }
+                }
+                return items;
+            }
+        });
+
+        request.setRequestURI("/tutorial/test11.action");
+        ActionMapping mapping = container.getInstance(ActionMapper.class).getMapping(request, configurationManager);
+        dispatcher.serviceAction(request, response, mapping);
+        String result = stringWriter.toString();
+        for (int i = 0; i < 3; i++) {
+            assertTrue(result.contains("id=\"itemId" + i + "\""));
+            assertTrue(result.contains("name=\"items[" + i + "].name\""));
+            assertTrue(result.contains("value=\"value" + i + "\""));
+        }
+    }
+
+    private void init() {
         stringWriter = new StringWriter();
         writer = new PrintWriter(stringWriter);
         response = new StrutsMockHttpServletResponse();
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/BeanTagTest.java b/core/src/test/java/org/apache/struts2/views/jsp/BeanTagTest.java
index 168ffe5..4b72ee0 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/BeanTagTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/BeanTagTest.java
@@ -18,7 +18,14 @@
  */
 package org.apache.struts2.views.jsp;
 
+import org.apache.struts2.StrutsException;
+import org.apache.struts2.dispatcher.HttpParameters;
+
 import javax.servlet.jsp.JspException;
+import java.util.HashMap;
+import java.util.Map;
+
+import static com.opensymphony.xwork2.security.DefaultNotExcludedAcceptedPatternsCheckerTest.NO_EXCLUSION_ACCEPT_ALL_PATTERNS_CHECKER;
 
 
 /**
@@ -47,4 +54,58 @@ public class BeanTagTest extends AbstractUITagTest {
         request.verify();
         pageContext.verify();
     }
+
+    public void testNotAccepted() throws Exception {
+        BeanTag tag = new BeanTag();
+        tag.setPageContext(pageContext);
+        tag.setName("org.apache.struts2.TestAction");
+
+        Map<String, String> tmp = new HashMap<>();
+        tmp.put("paramName", "getArray()[0]");
+        context.put("parameters", HttpParameters.create(tmp).build());
+        ParamTag param1 = new ParamTag();
+        param1.setPageContext(pageContext);
+        param1.setName("%{#parameters['paramName']}");
+        param1.setValue("'success'");
+
+        tag.doStartTag();
+        param1.doStartTag();
+
+        try {
+            param1.doEndTag();
+            fail("an excluded or not accepted is evaluated?!");
+        } catch (StrutsException e) {
+            assertEquals("Excluded or not accepted name found: getArray()[0]", e.getMessage());
+            assertNull(stack.findValue("result"));
+        }
+
+        param1.component.setNotExcludedAcceptedPatterns(NO_EXCLUSION_ACCEPT_ALL_PATTERNS_CHECKER);
+        tag.component.addParameter("array", "just to instantiate array to avoid null for getArray()");
+
+        param1.doEndTag();
+        assertEquals("success", stack.findValue("array[0]"));
+
+        tag.doEndTag();
+    }
+
+    public void testGetterAccepted() throws Exception {
+        BeanTag tag = new BeanTag();
+        tag.setPageContext(pageContext);
+        tag.setName("org.apache.struts2.TestAction");
+
+        ParamTag param1 = new ParamTag();
+        param1.setPageContext(pageContext);
+        param1.setName("getArray()[0]");
+        param1.setValue("'success'");
+
+        tag.doStartTag();
+        param1.doStartTag();
+
+        tag.component.addParameter("array", "just to instantiate array to avoid null for getArray()");
+
+        param1.doEndTag();
+        assertEquals("success", stack.findValue("array[0]"));
+
+        tag.doEndTag();
+    }
 }
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/ComboBoxTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/ComboBoxTest.java
index f7ef72a..18c8e97 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/ui/ComboBoxTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/ComboBoxTest.java
@@ -145,7 +145,7 @@ public class ComboBoxTest extends AbstractUITagTest {
         tag.setPageContext(pageContext);
         tag.setLabel("mylabel");
         tag.setName("foo");
-        tag.setId("cb.bc");
+        tag.setId("cb['\".\"'] = bc(){};//");
         tag.setList("collection");
 
         tag.doStartTag();
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/TextfieldTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/TextfieldTest.java
index bd52b8a..f1701a8 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/ui/TextfieldTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/TextfieldTest.java
@@ -117,7 +117,7 @@ public class TextfieldTest extends AbstractUITagTest {
         testAction.setFoo("bar");
 
         TextFieldModel model = new TextFieldModel(stack, request, response);
-        Map<String, String> params = new HashMap<String, String>();
+        Map<String, String> params = new HashMap<>();
         params.put("name", "myname");
         params.put("value", "%{foo}");
         params.put("size", "10");
@@ -172,6 +172,7 @@ public class TextfieldTest extends AbstractUITagTest {
         tag.setName("myname");
         tag.setValue("%{foo}");
         tag.setSize("10");
+        tag.setDynamicAttribute(null, "anotherAttr", "%{foo}");
 
         tag.doStartTag();
         tag.doEndTag();
diff --git a/plugins/jasperreports/src/test/resources/org/apache/struts2/views/jasperreports/empty.jrxml b/core/src/test/resources/org/apache/struts2/views/freemarker/iterator.ftl
similarity index 61%
rename from plugins/jasperreports/src/test/resources/org/apache/struts2/views/jasperreports/empty.jrxml
rename to core/src/test/resources/org/apache/struts2/views/freemarker/iterator.ftl
index 816d860..5114f40 100644
--- a/plugins/jasperreports/src/test/resources/org/apache/struts2/views/jasperreports/empty.jrxml
+++ b/core/src/test/resources/org/apache/struts2/views/freemarker/iterator.ftl
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
+<#--
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -19,5 +18,6 @@
  * under the License.
  */
 -->
-<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="test" pageWidth="842" pageHeight="595" orientation="Landscape" columnWidth="802" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" uuid="e65f69ca-7c62-4b4b-abc6-2e1a8710f23e">
-</jasperReport>
\ No newline at end of file
+<@s.iterator value="{'a','b','c'}" status="stat">
+    <@s.textfield id="itemId%{#stat.index}" name="items[%{#stat.index}].name" theme="simple"/>
+</...@s.iterator>
diff --git a/core/src/test/resources/org/apache/struts2/views/jsp/ui/ComboBox-4.txt b/core/src/test/resources/org/apache/struts2/views/jsp/ui/ComboBox-4.txt
index cea7465..5fdcaf8 100644
--- a/core/src/test/resources/org/apache/struts2/views/jsp/ui/ComboBox-4.txt
+++ b/core/src/test/resources/org/apache/struts2/views/jsp/ui/ComboBox-4.txt
@@ -1,13 +1,13 @@
  <tr>
-     <td class="tdLabel"><label for="cb.bc" class="label">mylabel:</label></td>
+     <td class="tdLabel"><label for="cb['&quot;.&quot;']=bc(){};//" class="label">mylabel:</label></td>
      <td class="tdInput">
  <script type="text/javascript">
-    function autoPopulate_cb_bc(targetElement) {
+    function autoPopulate_cb__________bc_______(targetElement) {
         targetElement.form.elements['foo'].value=targetElement.options[targetElement.selectedIndex].value;
     }
  </script>
- <input type="text" name="foo" value="hello" id="cb.bc"/><br/>
- <select onChange="autoPopulate_cb_bc(this);">
+ <input type="text" name="foo" value="hello" id="cb['&quot;.&quot;']=bc(){};//"/><br/>
+ <select onChange="autoPopulate_cb__________bc_______(this);">
      <option value="foo">foo</option>
  </select>
      </td>
diff --git a/core/src/test/resources/org/apache/struts2/views/jsp/ui/Radio-6.txt b/core/src/test/resources/org/apache/struts2/views/jsp/ui/Radio-6.txt
index 6bc0381..146cb0a 100644
--- a/core/src/test/resources/org/apache/struts2/views/jsp/ui/Radio-6.txt
+++ b/core/src/test/resources/org/apache/struts2/views/jsp/ui/Radio-6.txt
@@ -1,11 +1,11 @@
 <tr>
     <td class="tdLabel"></td>
     <td class="tdInput">
-        <input type="radio" name="myMap['name']" id="myMap_'name'_"value=""/>
-        <label for="myMap_'name'_">N/A</label>
-        <input type="radio" name="myMap['name']" id="myMap_'name'_Opt." value="Opt."/>
-        <label for="myMap_'name'_Opt.">Opt.</label>
-        <input type="radio" name="myMap['name']" id="myMap_'name'_Std." checked="checked" value="Std."/>
-        <label for="myMap_'name'_Std.">Std.</label>
+        <input type="radio" name="myMap['name']" id="myMap__name__"value=""/>
+        <label for="myMap__name__">N/A</label>
+        <input type="radio" name="myMap['name']" id="myMap__name__Opt." value="Opt."/>
+        <label for="myMap__name__Opt.">Opt.</label>
+        <input type="radio" name="myMap['name']" id="myMap__name__Std." checked="checked" value="Std."/>
+        <label for="myMap__name__Std.">Std.</label>
     </td>
 </tr>
diff --git a/core/src/test/resources/org/apache/struts2/views/jsp/ui/Textfield-5.txt b/core/src/test/resources/org/apache/struts2/views/jsp/ui/Textfield-5.txt
index 08326c7..2818476 100644
--- a/core/src/test/resources/org/apache/struts2/views/jsp/ui/Textfield-5.txt
+++ b/core/src/test/resources/org/apache/struts2/views/jsp/ui/Textfield-5.txt
@@ -1,4 +1,4 @@
 <tr>
     <td class="tdLabel"><label for="myname" class="label">mylabel:</label></td>
-    <td class="tdInput"><input type="text" name="myname" size="10" value="%{1+1}" id="myname"/></td>
+    <td class="tdInput"><input type="text" name="myname" size="10" value="%{1+1}" id="myname" anotherAttr="%{1+1}"/></td>
 </tr>
diff --git a/core/src/test/resources/struts.xml b/core/src/test/resources/struts.xml
index 5b29c1e..6415ba3 100644
--- a/core/src/test/resources/struts.xml
+++ b/core/src/test/resources/struts.xml
@@ -80,6 +80,11 @@
             </result>
         </action>
 
+        <action name="test11" class="com.opensymphony.xwork2.ActionSupport">
+            <result type="freemarker">
+                <param name="location">org/apache/struts2/views/freemarker/iterator.ftl</param>
+            </result>
+        </action>
     </package>
 
     <package name="sitegraph" namespace="/tutorial/sitegraph" extends="struts-default">
diff --git a/core/src/test/resources/xwork-sample.xml b/core/src/test/resources/xwork-sample.xml
index 5ff1e39..63c32b1 100644
--- a/core/src/test/resources/xwork-sample.xml
+++ b/core/src/test/resources/xwork-sample.xml
@@ -102,7 +102,14 @@
          	<interceptor-ref name="alias"/>
          	<result name="success" type="mock" />
         </action>
-      
+
+        <action name="dynamicAliasTest" class="com.opensymphony.xwork2.SimpleAction">
+            <param name="aliases">#{ #parameters['name'] : #parameters['value'] }</param>
+            <interceptor-ref name="params"/>
+            <interceptor-ref name="alias"/>
+            <result name="success" type="mock" />
+        </action>
+
         <action name="packagelessAction" class="PackagelessAction">
         </action>
 
@@ -143,6 +150,21 @@
         <action name="InfiniteRecursionChain" class="com.opensymphony.xwork2.ActionSupport">
                 <result name="success" type="chain">InfiniteRecursionChain</result>
         </action>
+        <action name="chain_with_namespace" class="com.opensymphony.xwork2.SimpleAction">
+            <result name="error" type="chain">
+                <param name="actionName">chain_without_namespace</param>
+                <param name="namespace">%{blah}</param>
+            </result>
+            <interceptor-ref name="debugStack"/>
+            <interceptor-ref name="defaultStack"/>
+        </action>
+        <action name="chain_without_namespace" class="com.opensymphony.xwork2.SimpleAction">
+            <result name="error" type="chain">
+                <param name="actionName">Foo</param>
+            </result>
+            <interceptor-ref name="debugStack"/>
+            <interceptor-ref name="defaultStack"/>
+        </action>
 
    </package>
 
diff --git a/plugins/jasperreports/src/main/java/org/apache/struts2/views/jasperreports/JasperReportsResult.java b/plugins/jasperreports/src/main/java/org/apache/struts2/views/jasperreports/JasperReportsResult.java
index c79e1b6..05fff2e 100644
--- a/plugins/jasperreports/src/main/java/org/apache/struts2/views/jasperreports/JasperReportsResult.java
+++ b/plugins/jasperreports/src/main/java/org/apache/struts2/views/jasperreports/JasperReportsResult.java
@@ -19,6 +19,8 @@
 package org.apache.struts2.views.jasperreports;
 
 import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.security.NotExcludedAcceptedPatternsChecker;
 import com.opensymphony.xwork2.util.ValueStack;
 
 import net.sf.jasperreports.engine.*;
@@ -131,6 +133,7 @@ public class JasperReportsResult extends StrutsResultSupport implements JasperRe
     private final static Logger LOG = LogManager.getLogger(JasperReportsResult.class);
 
     protected String dataSource;
+    private String parsedDataSource;
     protected String format;
     protected String documentName;
     protected String contentDisposition;
@@ -150,12 +153,16 @@ public class JasperReportsResult extends StrutsResultSupport implements JasperRe
      * additional report parameters from the action.
      */
     protected String reportParameters;
+    private String parsedReportParameters;
 
     /**
      * Names an exporter parameters map stack value,
      * allowing the use of custom export parameters.
      */
     protected String exportParameters;
+    private String parsedExportParameters;
+
+    private NotExcludedAcceptedPatternsChecker notExcludedAcceptedPatterns;
 
     /**
      * Default ctor.
@@ -173,6 +180,11 @@ public class JasperReportsResult extends StrutsResultSupport implements JasperRe
         super(location);
     }
 
+    @Inject
+    public void setNotExcludedAcceptedPatterns(NotExcludedAcceptedPatternsChecker notExcludedAcceptedPatterns) {
+        this.notExcludedAcceptedPatterns = notExcludedAcceptedPatterns;
+    }
+
     public String getImageServletUrl() {
         return imageServletUrl;
     }
@@ -265,8 +277,16 @@ public class JasperReportsResult extends StrutsResultSupport implements JasperRe
         ValueStackDataSource stackDataSource = null;
 
         Connection conn = (Connection) stack.findValue(connection);
-        if (conn == null)
-            stackDataSource = new ValueStackDataSource(stack, dataSource, wrapField);
+        if (conn == null) {
+            boolean evaluated = parsedDataSource != null && !parsedDataSource.equals(dataSource);
+            boolean reevaluate = !evaluated || isAcceptableExpression(parsedDataSource);
+            if (reevaluate) {
+                stackDataSource = new ValueStackDataSource(stack, parsedDataSource, wrapField);
+            } else {
+                throw new ServletException(String.format("Error building dataSource for excluded or not accepted [%s]",
+                        parsedDataSource));
+            }
+        }
 
         if ("https".equalsIgnoreCase(request.getScheme())) {
             // set the the HTTP Header to work around IE SSL weirdness
@@ -297,7 +317,9 @@ public class JasperReportsResult extends StrutsResultSupport implements JasperRe
         }
 
         // Add any report parameters from action to param map.
-        Map reportParams = (Map) stack.findValue(reportParameters);
+        boolean evaluated = parsedReportParameters != null && !parsedReportParameters.equals(reportParameters);
+        boolean reevaluate = !evaluated || isAcceptableExpression(parsedReportParameters);
+        Map reportParams = reevaluate ? (Map) stack.findValue(parsedReportParameters) : null;
         if (reportParams != null) {
         	LOG.debug("Found report parameters; adding to parameters...");
             parameters.putAll(reportParams);
@@ -372,7 +394,9 @@ public class JasperReportsResult extends StrutsResultSupport implements JasperRe
                 throw new ServletException("Unknown report format: " + format);
             }
 
-            Map exportParams = (Map) stack.findValue(exportParameters);
+            evaluated = parsedExportParameters != null && !parsedExportParameters.equals(exportParameters);
+            reevaluate = !evaluated || isAcceptableExpression(parsedExportParameters);
+            Map exportParams = reevaluate ? (Map) stack.findValue(parsedExportParameters) : null;
             if (exportParams != null) {
                 LOG.debug("Found export parameters; adding to exporter parameters...");
                 exporter.getParameters().putAll(exportParams);
@@ -427,8 +451,9 @@ public class JasperReportsResult extends StrutsResultSupport implements JasperRe
             LOG.error(message);
             throw new RuntimeException(message);
         }
-        if (dataSource != null)
-            dataSource = conditionalParse(dataSource, invocation);
+        if (dataSource != null) {
+            parsedDataSource = conditionalParse(dataSource, invocation);
+        }
 
         format = conditionalParse(format, invocation);
         if (StringUtils.isEmpty(format)) {
@@ -443,8 +468,8 @@ public class JasperReportsResult extends StrutsResultSupport implements JasperRe
             documentName = conditionalParse(documentName, invocation);
         }
 
-        reportParameters = conditionalParse(reportParameters, invocation);
-        exportParameters = conditionalParse(exportParameters, invocation);
+        parsedReportParameters = conditionalParse(reportParameters, invocation);
+        parsedExportParameters = conditionalParse(exportParameters, invocation);
     }
 
     /**
@@ -469,4 +494,22 @@ public class JasperReportsResult extends StrutsResultSupport implements JasperRe
         return baos;
     }
 
+    /**
+     * Checks if expression doesn't contain vulnerable code
+     *
+     * @param expression of result
+     * @return true|false
+     * @since 2.5.27
+     */
+    protected boolean isAcceptableExpression(String expression) {
+        NotExcludedAcceptedPatternsChecker.IsAllowed isAllowed = notExcludedAcceptedPatterns.isAllowed(expression);
+        if (isAllowed.isAllowed()) {
+            return true;
+        }
+
+        LOG.warn("Expression [{}] isn't allowed by pattern [{}]! See Accepted / Excluded patterns at\n" +
+                "https://struts.apache.org/security/", expression, isAllowed.getAllowedPattern());
+
+        return false;
+    }
 }
diff --git a/plugins/jasperreports/src/test/java/org/apache/struts2/views/jasperreports/JasperReportsResultTest.java b/plugins/jasperreports/src/test/java/org/apache/struts2/views/jasperreports/JasperReportsResultTest.java
index 1bf55ac..0d87be5 100644
--- a/plugins/jasperreports/src/test/java/org/apache/struts2/views/jasperreports/JasperReportsResultTest.java
+++ b/plugins/jasperreports/src/test/java/org/apache/struts2/views/jasperreports/JasperReportsResultTest.java
@@ -20,35 +20,35 @@ package org.apache.struts2.views.jasperreports;
 
 import com.opensymphony.xwork2.ActionContext;
 import com.opensymphony.xwork2.mock.MockActionInvocation;
+import com.opensymphony.xwork2.security.NotExcludedAcceptedPatternsChecker;
 import com.opensymphony.xwork2.util.ClassLoaderUtil;
 import com.opensymphony.xwork2.util.ValueStack;
 import net.sf.jasperreports.engine.JasperCompileManager;
 import org.apache.struts2.StrutsStatics;
 import org.apache.struts2.StrutsTestCase;
 import org.easymock.IAnswer;
-import org.springframework.mock.web.MockHttpServletRequest;
-import org.springframework.mock.web.MockHttpServletResponse;
-import org.springframework.mock.web.MockServletContext;
 
+import javax.servlet.ServletException;
 import java.net.URL;
 import java.sql.Connection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
 
+import static net.sf.jasperreports.engine.JRExporterParameter.OUTPUT_STRING_BUFFER;
 import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.expectLastCall;
 import static org.easymock.EasyMock.replay;
 import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertNotEquals;
 
 public class JasperReportsResultTest extends StrutsTestCase {
     private MockActionInvocation invocation;
     private ValueStack stack;
+    private JasperReportsResult result;
 
     public void testConnClose() throws Exception {
-        JasperReportsResult result = new JasperReportsResult();
-        URL url = ClassLoaderUtil.getResource("org/apache/struts2/views/jasperreports/empty.jrxml", this.getClass());
-        JasperCompileManager.compileReportToFile(url.getFile(), url.getFile() + ".jasper");
-        result.setLocation("org/apache/struts2/views/jasperreports/empty.jrxml.jasper");
-        result.setFormat(JasperReportConstants.FORMAT_XML);
-
         Connection connection = createMock(Connection.class);
         final Boolean[] closed = {false};
         connection.close();
@@ -70,20 +70,243 @@ public class JasperReportsResultTest extends StrutsTestCase {
         assertTrue(closed[0]);
     }
 
+    public void testDataSourceNotAccepted() throws Exception {
+        stack.push(new Object() {
+            public String getDatasourceName() {
+                return "getDatasource()";
+            }
+
+            public Map<String, String>[] getDatasource() {
+                return JR_MAP_ARRAY_DATA_SOURCE;
+            }
+        });
+        result.setDataSource("${datasourceName}");
+
+        try {
+            result.execute(this.invocation);
+        } catch (ServletException e) {
+            assertEquals("Error building dataSource for excluded or not accepted [getDatasource()]",
+                    e.getMessage());
+        }
+
+        // verify that above test has really effect
+        result.setNotExcludedAcceptedPatterns(NO_EXCLUSION_ACCEPT_ALL_PATTERNS_CHECKER);
+        result.execute(this.invocation);
+        assertTrue(response.getContentAsString().contains("Hello Foo Bar!"));
+    }
+
+    public void testDataSourceAccepted() throws Exception {
+        stack.push(new Object() {
+            public String getDatasourceName() {
+                return "datasource";
+            }
+
+            public Map<String, String>[] getDatasource() {
+                return JR_MAP_ARRAY_DATA_SOURCE;
+            }
+        });
+        result.setDataSource("${datasourceName}");
+
+        result.execute(this.invocation);
+        assertTrue(response.getContentAsString().contains("Hello Foo Bar!"));
+    }
+
+    public void testDataSourceExpressionAccepted() throws Exception {
+        result.setDataSource("{#{'firstName':'Qux', 'lastName':'Quux'}}");
+
+        result.execute(this.invocation);
+        assertTrue(response.getContentAsString().contains("Hello Qux Quux!"));
+    }
+
+    public void testReportParametersNotAccepted() throws Exception {
+        result.setDataSource("{#{'firstName':'ignore', 'lastName':'ignore'}}");
+
+        stack.push(new Object() {
+            public String getReportParametersName() {
+                return "getReportParameters()";
+            }
+
+            public Map<String, String> getReportParameters() {
+                return new HashMap<String, String>() {{
+                    put("title", "Baz");
+                }};
+            }
+        });
+
+        result.setReportParameters("${reportParametersName}");
+        result.execute(this.invocation);
+        assertTrue(response.getContentAsString().contains("null Report"));
+
+        // verify that above test has really effect
+        response.setCommitted(false);
+        response.reset();
+        result.setNotExcludedAcceptedPatterns(NO_EXCLUSION_ACCEPT_ALL_PATTERNS_CHECKER);
+        result.execute(this.invocation);
+        assertTrue(response.getContentAsString().contains("Baz Report"));
+    }
+
+    public void testReportParametersAccepted() throws Exception {
+        result.setDataSource("{#{'firstName':'ignore', 'lastName':'ignore'}}");
+
+        stack.push(new Object() {
+            public String getReportParametersName() {
+                return "reportParameters";
+            }
+
+            public Map<String, String> getReportParameters() {
+                return new HashMap<String, String>() {{
+                    put("title", "Baz");
+                }};
+            }
+        });
+
+        result.setReportParameters("${reportParametersName}");
+        result.execute(this.invocation);
+        assertTrue(response.getContentAsString().contains("Baz Report"));
+    }
+
+    public void testReportParametersExpressionAccepted() throws Exception {
+        result.setDataSource("{#{'firstName':'ignore', 'lastName':'ignore'}}");
+
+        result.setReportParameters("#{'title':'Qux'}");
+        result.execute(this.invocation);
+        assertTrue(response.getContentAsString().contains("Qux Report"));
+    }
+
+    public void testExportParametersNotAccepted() throws Exception {
+        result.setDataSource("{#{'firstName':'ignore', 'lastName':'ignore'}}");
+
+        final StringBuffer sb = new StringBuffer();
+        stack.push(new Object() {
+            public String getExportParametersName() {
+                return "getExportParameters()";
+            }
+
+            public Map<Object, Object> getExportParameters() {
+                return new HashMap<Object, Object>() {{
+                    put(OUTPUT_STRING_BUFFER, sb);
+                }};
+            }
+        });
+
+        result.setExportParameters("${exportParametersName}");
+        result.execute(this.invocation);
+        assertEquals(0, sb.length());
+
+        // verify that above test has really effect
+        response.setCommitted(false);
+        response.reset();
+        result.setNotExcludedAcceptedPatterns(NO_EXCLUSION_ACCEPT_ALL_PATTERNS_CHECKER);
+        result.execute(this.invocation);
+        assertNotEquals(0, sb.length());
+    }
+
+    public void testExportParametersAccepted() throws Exception {
+        result.setDataSource("{#{'firstName':'Qux', 'lastName':'Quux'}}");
+
+        final StringBuffer sb = new StringBuffer();
+        stack.push(new Object() {
+            public String getExportParametersName() {
+                return "exportParameters";
+            }
+
+            public Map<Object, Object> getExportParameters() {
+                return new HashMap<Object, Object>() {{
+                    put(OUTPUT_STRING_BUFFER, sb);
+                }};
+            }
+        });
+
+        result.setExportParameters("${exportParametersName}");
+        result.execute(this.invocation);
+        assertTrue(sb.toString().contains("Hello Qux Quux!"));
+    }
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        MockHttpServletResponse response = new MockHttpServletResponse();
-        MockHttpServletRequest request = new MockHttpServletRequest();
-        request.setRequestURI("http://sumeruri");
+
+        request.setRequestURI("http://someuri");
         ActionContext context = ActionContext.getContext();
         context.put(StrutsStatics.HTTP_RESPONSE, response);
         context.put(StrutsStatics.HTTP_REQUEST, request);
-        this.stack = context.getValueStack();
-        MockServletContext servletContext = new MockServletContext();
         context.put(StrutsStatics.SERVLET_CONTEXT, servletContext);
+        this.stack = context.getValueStack();
         this.invocation = new MockActionInvocation();
         this.invocation.setInvocationContext(context);
         this.invocation.setStack(this.stack);
+
+        result = new JasperReportsResult();
+        container.inject(result);
+        URL url = ClassLoaderUtil.getResource("org/apache/struts2/views/jasperreports/simple.jrxml", this.getClass());
+        JasperCompileManager.compileReportToFile(url.getFile(), url.getFile() + ".jasper");
+        result.setLocation("org/apache/struts2/views/jasperreports/simple.jrxml.jasper");
+        result.setFormat(JasperReportConstants.FORMAT_XML);
     }
+
+
+    private static final Map<String, String>[] JR_MAP_ARRAY_DATA_SOURCE = new Map[]{
+            new HashMap<String, String>() {{
+                put("firstName", "Foo");
+                put("lastName", "Bar");
+            }}
+    };
+
+    private static final NotExcludedAcceptedPatternsChecker NO_EXCLUSION_ACCEPT_ALL_PATTERNS_CHECKER
+            = new NotExcludedAcceptedPatternsChecker() {
+        @Override
+        public IsAllowed isAllowed(String value) {
+            return IsAllowed.yes("*");
+        }
+
+        @Override
+        public IsAccepted isAccepted(String value) {
+            return null;
+        }
+
+        @Override
+        public void setAcceptedPatterns(String commaDelimitedPatterns) {
+
+        }
+
+        @Override
+        public void setAcceptedPatterns(String[] patterns) {
+
+        }
+
+        @Override
+        public void setAcceptedPatterns(Set<String> patterns) {
+
+        }
+
+        @Override
+        public Set<Pattern> getAcceptedPatterns() {
+            return null;
+        }
+
+        @Override
+        public IsExcluded isExcluded(String value) {
+            return null;
+        }
+
+        @Override
+        public void setExcludedPatterns(String commaDelimitedPatterns) {
+
+        }
+
+        @Override
+        public void setExcludedPatterns(String[] patterns) {
+
+        }
+
+        @Override
+        public void setExcludedPatterns(Set<String> patterns) {
+
+        }
+
+        @Override
+        public Set<Pattern> getExcludedPatterns() {
+            return null;
+        }
+    };
 }
diff --git a/plugins/jasperreports/src/test/resources/org/apache/struts2/views/jasperreports/simple.jrxml b/plugins/jasperreports/src/test/resources/org/apache/struts2/views/jasperreports/simple.jrxml
new file mode 100644
index 0000000..9de586a
--- /dev/null
+++ b/plugins/jasperreports/src/test/resources/org/apache/struts2/views/jasperreports/simple.jrxml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+ * 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.
+ */
+-->
+<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports"
+              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+              xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd"
+              name="test" pageWidth="842" pageHeight="595" orientation="Landscape" columnWidth="802">
+    <parameter name="title" />
+    <field name="firstName" />
+    <field name="lastName" />
+    <title>
+        <band height="16">
+            <textField>
+                <reportElement x="0" y="0" width="100" height="16" />
+                <textFieldExpression><![CDATA[$P{title}]]> + " Report"</textFieldExpression>
+            </textField>
+        </band>
+    </title>
+    <detail>
+        <band height="16">
+            <textField>
+                <reportElement x="0" y="0" width="100" height="16" />
+                <textFieldExpression>"Hello " + <![CDATA[$F{firstName}]]> + " " + <![CDATA[$F{lastName}]]> + "!"</textFieldExpression>
+            </textField>
+        </band>
+    </detail>
+</jasperReport>
\ No newline at end of file
diff --git a/plugins/javatemplates/src/test/java/org/apache/struts2/views/java/simple/AnchorTest.java b/plugins/javatemplates/src/test/java/org/apache/struts2/views/java/simple/AnchorTest.java
index 673d812..d727a74 100644
--- a/plugins/javatemplates/src/test/java/org/apache/struts2/views/java/simple/AnchorTest.java
+++ b/plugins/javatemplates/src/test/java/org/apache/struts2/views/java/simple/AnchorTest.java
@@ -20,6 +20,7 @@
  */
 package org.apache.struts2.views.java.simple;
 
+import com.opensymphony.xwork2.security.DefaultNotExcludedAcceptedPatternsChecker;
 import org.apache.struts2.components.Anchor;
 import org.apache.struts2.components.UIBean;
 import org.apache.struts2.components.ServletUrlRenderer;
@@ -73,6 +74,7 @@ public class AnchorTest extends AbstractTest {
         super.setUp();
         this.tag = new Anchor(stack, request, response);
         this.tag.setUrlRenderer(new ServletUrlRenderer());
+        this.tag.setNotExcludedAcceptedPatterns(new DefaultNotExcludedAcceptedPatternsChecker());
     }
 
     @Override
diff --git a/plugins/javatemplates/src/test/java/org/apache/struts2/views/java/simple/CheckboxTest.java b/plugins/javatemplates/src/test/java/org/apache/struts2/views/java/simple/CheckboxTest.java
index d29ca88..10a8e36 100644
--- a/plugins/javatemplates/src/test/java/org/apache/struts2/views/java/simple/CheckboxTest.java
+++ b/plugins/javatemplates/src/test/java/org/apache/struts2/views/java/simple/CheckboxTest.java
@@ -20,6 +20,7 @@
  */
 package org.apache.struts2.views.java.simple;
 
+import com.opensymphony.xwork2.security.DefaultNotExcludedAcceptedPatternsChecker;
 import org.apache.struts2.components.Checkbox;
 import org.apache.struts2.components.UIBean;
 
@@ -68,6 +69,7 @@ public class CheckboxTest extends AbstractCommonAttributesTest {
     protected void setUp() throws Exception {
         super.setUp();
         tag = new Checkbox(stack, request, response);
+        tag.setNotExcludedAcceptedPatterns(new DefaultNotExcludedAcceptedPatternsChecker());
     }
 
     @Override
diff --git a/plugins/portlet/src/main/java/org/apache/struts2/components/PortletUrlRenderer.java b/plugins/portlet/src/main/java/org/apache/struts2/components/PortletUrlRenderer.java
index c31a3ff..62eed8f 100644
--- a/plugins/portlet/src/main/java/org/apache/struts2/components/PortletUrlRenderer.java
+++ b/plugins/portlet/src/main/java/org/apache/struts2/components/PortletUrlRenderer.java
@@ -192,7 +192,9 @@ public class PortletUrlRenderer implements UrlRenderer {
                 } else {
                     id = action.substring(slash + 1);
                 }
-                formComponent.addParameter("id", formComponent.escape(id));
+                String escapedId = formComponent.escape(id);
+                formComponent.addParameter("id", escapedId);
+                formComponent.addParameter("escapedId", escapedId);
             }
         }
     }