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:11 UTC

[struts] branch fix/double_evaluations_2_5 created (now 8d6e26e)

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

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


      at 8d6e26e  fix double evaluations

This branch includes the following new commits:

     new 8d6e26e  fix double evaluations

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[struts] 01/01: fix double evaluations

Posted by ya...@apache.org.
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);
             }
         }
     }