Compare commits

..

34 Commits

Author SHA1 Message Date
Lukasz Lenart 8274f77bc1 [maven-release-plugin] prepare release STRUTS_2_5_31 2023-06-13 10:05:05 +02:00
Lukasz Lenart 87a18db15f Uses proper plugin attribute to disable doclint 2023-06-13 10:01:22 +02:00
Yasser Zamani e58edfba83 add some improvements 2023-06-05 01:15:15 +04:30
Lukasz Lenart 2520513310 [maven-release-plugin] prepare for next development iteration 2022-03-15 14:53:45 +01:00
Lukasz Lenart e4db6b720e [maven-release-plugin] prepare release STRUTS_2_5_30 2022-03-15 14:53:37 +01:00
Lukasz Lenart e1209fde05 Merge pull request #496 from apache/fix/double_evaluations_2_5
Fixes double evaluations
2022-03-15 14:33:04 +01:00
Lukasz Lenart cf1c674feb WW-5172 Upgrades Freemarker to version 2.3.31 2022-03-15 14:26:33 +01:00
Lukasz Lenart 992ca3a525 WW-5170 Upgrades Jackson Core to 2.10.5 and Jackson Databind to 2.10.5.1 2022-02-14 07:52:18 +01:00
Yasser Zamani e7834d4345 Revert "keep dynamic attributes order"
This reverts commit 8a46e931
2022-01-20 02:22:13 +03:30
Yasser Zamani bb6c186078 fix test for jdk8+ 2022-01-20 02:17:05 +03:30
Yasser Zamani 88f04fa337 Revert "keep dynamic attributes order"
This reverts commit 8a46e931
2022-01-20 01:30:00 +03:30
Yasser Zamani 8a46e9313f keep dynamic attributes order 2022-01-19 23:10:46 +03:30
Yasser Zamani 4e05233cc2 reorder to pass tests after merge 2022-01-18 10:18:06 +03:30
Yasser Zamani e647e5cbfc Merge branch 'struts-2-5-x' into fix/double_evaluations_2_5 2022-01-18 10:10:16 +03:30
Lukasz Lenart 8d0382c34e [maven-release-plugin] prepare for next development iteration 2022-01-13 08:48:34 +01:00
Lukasz Lenart 00be9803d5 [maven-release-plugin] prepare release STRUTS_2_5_29 2022-01-13 08:48:24 +01:00
Lukasz Lenart e6b9aaba54 Fixes wrong SNAPSHOT version in BOM 2022-01-13 08:39:48 +01:00
Lukasz Lenart c495824e56 Merge pull request #524 from apache/WW-5117-evaluate-dynamic-attributes
[WW-5117] Evaluates dynamic attributes
2022-01-13 08:16:49 +01:00
Lukasz Lenart 5c247b38ed WW-5117 Uses translateVariables instead of findString to allow join expressions 2022-01-06 09:06:26 +01:00
Lukasz Lenart a7fdf13446 WW-5117 Does a conditional evaluation depending on the tag
Some tags requires to perform a lazy evaluation which can only happen in ftl template as performing it in the component class is not possible
2022-01-06 09:06:26 +01:00
Lukasz Lenart 2825fdb41a WW-5117 Evaluates dynamic attributes when assigning them to tag
Reverts https://github.com/apache/struts/pull/447/commits/8bbe1949e17d58e1b5aef9c71e1279ad12ad7ba7#diff-0a39f082871f48bd14037ab2e3a3911b0b1046506c1d93338024d77d412a7075L305-L309
2022-01-06 09:06:26 +01:00
Lukasz Lenart 5e624faa82 WW-5117 Restores previous behavior where tag was before action on stack 2022-01-06 09:06:26 +01:00
Lukasz Lenart 377c0cb20b Fixes versioning after the latest release 2022-01-06 09:05:08 +01:00
Lukasz Lenart 25da849d0a [maven-release-plugin] prepare for next development iteration 2021-12-30 07:34:55 +01:00
Lukasz Lenart 9f14276c00 [maven-release-plugin] prepare release STRUTS_2_5_28_3 2021-12-30 07:34:46 +01:00
Lukasz Lenart 45ccf7d83b Upgrades Log4j to version 2.12.4 2021-12-30 07:29:43 +01:00
Lukasz Lenart 6ae6283f5e [maven-release-plugin] prepare for next development iteration 2021-12-22 07:43:19 +01:00
Lukasz Lenart 53aeafd7a2 [maven-release-plugin] prepare release STRUTS_2_5_28_2 2021-12-22 07:43:10 +01:00
Lukasz Lenart 3dd751b730 Upgrades Log4j to version 2.12.3 2021-12-22 07:38:22 +01:00
Lukasz Lenart 93303b45cb [maven-release-plugin] prepare for next development iteration 2021-12-16 16:33:51 +01:00
Lukasz Lenart 8ce5811fe7 [maven-release-plugin] prepare release STRUTS_2_5_28_1 2021-12-16 16:33:42 +01:00
Lukasz Lenart dc349dd356 Upgrades Log4j to version 2.12.2 2021-12-16 16:29:14 +01:00
Lukasz Lenart 18ff6c1ef4 [maven-release-plugin] prepare for next development iteration 2021-11-29 08:34:12 +01:00
Yasser Zamani 8d6e26e0fe fix double evaluations
address known issues reported at https://securitylab.github.com/research/apache-struts-double-evaluation/
2021-07-09 10:45:22 +04:30
101 changed files with 1933 additions and 385 deletions
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-apps</artifactId>
<packaging>pom</packaging>
+2 -2
View File
@@ -24,12 +24,12 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-apps</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-rest-showcase</artifactId>
<packaging>war</packaging>
<version>2.5.28</version>
<version>2.5.31</version>
<name>Struts 2 Rest Showcase Webapp</name>
<description>Struts 2 Rest Showcase Example</description>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-apps</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-showcase</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-assembly</artifactId>
+3 -3
View File
@@ -30,7 +30,7 @@
</parent>
<artifactId>struts2-bom</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
<packaging>pom</packaging>
<name>Struts 2 Bill of Materials</name>
@@ -45,7 +45,7 @@
</licenses>
<properties>
<struts-version.version>2.5.28</struts-version.version>
<struts-version.version>2.5.31</struts-version.version>
<maven.site.skip>true</maven.site.skip>
<maven.site.deploy.skip>true</maven.site.deploy.skip>
</properties>
@@ -181,7 +181,7 @@
</dependencyManagement>
<scm>
<tag>STRUTS_2_5_28</tag>
<tag>STRUTS_2_5_31</tag>
<connection>scm:git:https://gitbox.apache.org/repos/asf/struts.git</connection>
<developerConnection>scm:git:https://gitbox.apache.org/repos/asf/struts.git</developerConnection>
<url>https://github.com/apache/struts/</url>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-osgi-bundles</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-osgi-admin-bundle</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-osgi-bundles</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-osgi-demo-bundle</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-osgi-bundles</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-core</artifactId>
<packaging>jar</packaging>
@@ -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)
;
@@ -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);
}
}
@@ -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;
}
}
@@ -115,6 +115,11 @@ public class XWorkListPropertyAccessor extends ListPropertyAccessor {
if (listSize <= index) {
Object result;
if (index > autoGrowCollectionLimit) {
throw new OgnlException("Error auto growing collection size to " + index + " which limited to "
+ autoGrowCollectionLimit);
}
for (int i = listSize; i < index; i++) {
list.add(null);
}
@@ -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 {
@@ -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();
}
}
@@ -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 +
" }";
}
}
}
@@ -96,13 +96,13 @@ public final class StrutsConstants {
/** Update freemarker templates cache in seconds */
public static final String STRUTS_FREEMARKER_TEMPLATES_CACHE_UPDATE_DELAY = "struts.freemarker.templatesCache.updateDelay";
/** Cache model instances at BeanWrapper level */
public static final String STRUTS_FREEMARKER_BEANWRAPPER_CACHE = "struts.freemarker.beanwrapperCache";
/** Maximum strong sizing for MruCacheStorage for freemarker */
public static final String STRUTS_FREEMARKER_MRU_MAX_STRONG_SIZE = "struts.freemarker.mru.max.strong.size";
/** org.apache.struts2.views.velocity.VelocityManager implementation class */
public static final String STRUTS_VELOCITY_MANAGER_CLASSNAME = "struts.velocity.manager.classname";
@@ -127,6 +127,9 @@ public final class StrutsConstants {
/** The maximize size of a multipart request (file upload) */
public static final String STRUTS_MULTIPART_MAXSIZE = "struts.multipart.maxSize";
/** The maximum length of a string parameter in a multipart request. */
public static final String STRUTS_MULTIPART_MAX_STRING_LENGTH = "struts.multipart.maxStringLength";
/** The directory to use for storing uploaded files */
public static final String STRUTS_MULTIPART_SAVEDIR = "struts.multipart.saveDir";
@@ -180,7 +183,7 @@ public final class StrutsConstants {
* You can specify different prefixes that will be handled by different mappers
*/
public static final String PREFIX_BASED_MAPPER_CONFIGURATION = "struts.mapper.prefixMapping";
/** Whether the Struts filter should serve static content or not */
public static final String STRUTS_SERVE_STATIC_CONTENT = "struts.serve.static";
@@ -322,6 +325,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";
@@ -48,7 +48,7 @@ import com.opensymphony.xwork2.util.ValueStack;
allowDynamicAttributes = true)
public class CheckboxList extends ListUIBean {
final public static String TEMPLATE = "checkboxlist";
public CheckboxList(ValueStack stack, HttpServletRequest request, HttpServletResponse response) {
super(stack, request, response);
}
@@ -56,9 +56,19 @@ public class CheckboxList extends ListUIBean {
protected String getDefaultTemplate() {
return TEMPLATE;
}
public void evaluateExtraParams() {
super.evaluateExtraParams();
}
}
/**
* Checkboxlist tag requires lazy evaluation as list of tags is dynamically generated using <s:iterator/>
*
* @return boolean true by default
*/
@Override
protected boolean lazyEvaluation() {
return true;
}
}
@@ -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) {
@@ -382,15 +391,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
@@ -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;
}
}
@@ -125,6 +125,7 @@ public abstract class FormButton extends ClosingUIBean {
}
}
addParameter("id", _tmp_id);
addParameter("escapedId", escape(_tmp_id));
}
/**
@@ -188,7 +188,6 @@ public abstract class ListUIBean extends UIBean {
this.listTitle = listTitle;
}
public void setThrowExceptionOnNullValueAttribute(boolean throwExceptionOnNullValueAttribute) {
this.throwExceptionOnNullValueAttribute = throwExceptionOnNullValueAttribute;
}
@@ -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);
}
}
@@ -57,7 +57,7 @@ import javax.servlet.http.HttpServletResponse;
allowDynamicAttributes = true)
public class Radio extends ListUIBean {
final public static String TEMPLATE = "radiomap";
public Radio(ValueStack stack, HttpServletRequest request, HttpServletResponse response) {
super(stack, request, response);
}
@@ -65,8 +65,19 @@ public class Radio extends ListUIBean {
protected String getDefaultTemplate() {
return TEMPLATE;
}
public void evaluateExtraParams() {
super.evaluateExtraParams();
}
}
/**
* Radio tag requires lazy evaluation as list of tags is dynamically generated using <s:iterator/>
*
* @return boolean true by default
*/
@Override
protected boolean lazyEvaluation() {
return true;
}
}
@@ -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);
}
}
@@ -20,7 +20,9 @@ package org.apache.struts2.components;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.TextParseUtil;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -30,6 +32,7 @@ import org.apache.struts2.components.template.Template;
import org.apache.struts2.components.template.TemplateEngine;
import org.apache.struts2.components.template.TemplateEngineManager;
import org.apache.struts2.components.template.TemplateRenderingContext;
import org.apache.struts2.util.ComponentUtils;
import org.apache.struts2.util.TextProviderHelper;
import org.apache.struts2.views.annotations.StrutsTagAttribute;
import org.apache.struts2.views.util.ContextUtil;
@@ -555,16 +558,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 +581,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 +660,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 +785,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 +823,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 +894,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 +945,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 +962,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;
}
/**
@@ -1258,29 +1260,45 @@ 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 attrName = entry.getKey();
String attrValue = entry.getValue();
if (!isValidTagAttribute(key)) {
dynamicAttributes.put(key, entry.getValue());
if (!isValidTagAttribute(attrName)) {
if (ComponentUtils.altSyntax(getStack()) && ComponentUtils.containsExpression(attrValue) && !lazyEvaluation()) {
String translated = TextParseUtil.translateVariables('%', attrValue, stack);
dynamicAttributes.put(attrName, ObjectUtils.defaultIfNull(translated, attrValue));
} else {
dynamicAttributes.put(attrName, attrValue);
}
}
}
}
@Override
/**
* supports dynamic attributes for freemarker ui tags
* @see https://issues.apache.org/jira/browse/WW-3174
* @see https://issues.apache.org/jira/browse/WW-4166
* @see "https://issues.apache.org/jira/browse/WW-3174"
* @see "https://issues.apache.org/jira/browse/WW-4166"
*/
@Override
public void copyParams(Map params) {
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());
}
}
}
/**
* Used to avoid evaluating attributes in {@link #evaluateParams()} or {@link #evaluateExtraParams()}
* as evaluation will happen in tag's template
*
* @return boolean false if evaluation should be performed in ftl
*/
protected boolean lazyEvaluation() {
return false;
}
}
@@ -148,16 +148,13 @@ public class FreemarkerTemplateEngine extends BaseTemplateEngine {
}
};
LOG.debug("Puts action on the top of ValueStack, just before the tag");
action = stack.pop();
LOG.debug("Push tag on top of the stack");
stack.push(templateContext.getTag());
stack.push(action);
try {
template.process(model, writer);
} finally {
stack.pop(); // removes action
stack.pop(); // removes tag
stack.push(action); // puts back action
LOG.debug("Removes tag from top of the stack");
stack.pop();
}
}
@@ -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);
@@ -54,6 +54,11 @@ public abstract class AbstractMultiPartRequest implements MultiPartRequest {
protected long maxSize;
protected boolean maxSizeProvided;
/**
* Specifies the maximum length of a string parameter in a multipart request.
*/
protected Long maxStringLength;
/**
* Specifies the buffer size to use during streaming.
*/
@@ -88,6 +93,11 @@ public abstract class AbstractMultiPartRequest implements MultiPartRequest {
this.maxSize = Long.parseLong(maxSize);
}
@Inject(StrutsConstants.STRUTS_MULTIPART_MAX_STRING_LENGTH)
public void setMaxStringLength(String maxStringLength) {
this.maxStringLength = Long.parseLong(maxStringLength);
}
@Inject
public void setLocaleProviderFactory(LocaleProviderFactory localeProviderFactory) {
defaultLocale = localeProviderFactory.createLocaleProvider().getLocale();
@@ -134,9 +144,9 @@ public abstract class AbstractMultiPartRequest implements MultiPartRequest {
int forwardSlash = fileName.lastIndexOf('/');
int backwardSlash = fileName.lastIndexOf('\\');
if (forwardSlash != -1 && forwardSlash > backwardSlash) {
fileName = fileName.substring(forwardSlash + 1, fileName.length());
fileName = fileName.substring(forwardSlash + 1);
} else {
fileName = fileName.substring(backwardSlash + 1, fileName.length());
fileName = fileName.substring(backwardSlash + 1);
}
return fileName;
}
@@ -25,6 +25,7 @@ import org.apache.commons.fileupload.RequestContext;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.dispatcher.LocalizedMessage;
@@ -55,9 +56,8 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
*
* @param saveDir the directory to save off the file
* @param request the request containing the multipart
* @throws java.io.IOException is thrown if encoding fails.
*/
public void parse(HttpServletRequest request, String saveDir) throws IOException {
public void parse(HttpServletRequest request, String saveDir) {
try {
setLocale(request);
processUpload(request, saveDir);
@@ -126,13 +126,26 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
values = new ArrayList<>();
}
// note: see http://jira.opensymphony.com/browse/WW-633
// basically, in some cases the charset may be null, so
// we're just going to try to "other" method (no idea if this
// will work)
if (charset != null) {
long size = item.getSize();
if (size == 0) {
values.add(StringUtils.EMPTY);
} else if (size > maxStringLength) {
String errorKey = "struts.messages.upload.error.parameter.too.long";
LocalizedMessage localizedMessage = new LocalizedMessage(this.getClass(), errorKey, null,
new Object[] { item.getFieldName(), maxStringLength, size });
if (!errors.contains(localizedMessage)) {
errors.add(localizedMessage);
}
return;
} else if (charset != null) {
values.add(item.getString(charset));
} else {
// note: see http://jira.opensymphony.com/browse/WW-633
// basically, in some cases the charset may be null, so
// we're just going to try to "other" method (no idea if this
// will work)
values.add(item.getString());
}
params.put(item.getFieldName(), values);
@@ -183,7 +196,7 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
contentTypes.add(fileItem.getContentType());
}
return contentTypes.toArray(new String[contentTypes.size()]);
return contentTypes.toArray(new String[0]);
}
/* (non-Javadoc)
@@ -209,7 +222,7 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
fileList.add(new StrutsUploadedFile(storeLocation));
}
return fileList.toArray(new UploadedFile[fileList.size()]);
return fileList.toArray(new UploadedFile[0]);
}
/* (non-Javadoc)
@@ -227,7 +240,7 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
fileNames.add(getCanonicalName(fileItem.getName()));
}
return fileNames.toArray(new String[fileNames.size()]);
return fileNames.toArray(new String[0]);
}
/* (non-Javadoc)
@@ -245,7 +258,7 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
fileNames.add(((DiskFileItem) fileItem).getStoreLocation().getName());
}
return fileNames.toArray(new String[fileNames.size()]);
return fileNames.toArray(new String[0]);
}
/* (non-Javadoc)
@@ -253,7 +266,7 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
*/
public String getParameter(String name) {
List<String> v = params.get(name);
if (v != null && v.size() > 0) {
if (v != null && !v.isEmpty()) {
return v.get(0);
}
@@ -272,8 +285,8 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
*/
public String[] getParameterValues(String name) {
List<String> v = params.get(name);
if (v != null && v.size() > 0) {
return v.toArray(new String[v.size()]);
if (v != null && !v.isEmpty()) {
return v.toArray(new String[0]);
}
return null;
@@ -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;
}
}
@@ -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('\'', '"') + "')");
}
/*
@@ -68,6 +68,7 @@ struts.multipart.parser=jakarta
# uses javax.servlet.context.tempdir by default
struts.multipart.saveDir=
struts.multipart.maxSize=2097152
struts.multipart.maxStringLength=4096
### Load custom property files (does not override struts.properties!)
# struts.custom.properties=application,org/apache/struts2/extension/custom
@@ -26,6 +26,7 @@ struts.messages.invalid.content.type=Could not find a Content-Type for {0}. Veri
struts.messages.removing.file=Removing file {0} {1}
struts.messages.error.uploading=Error uploading: {0}
struts.messages.error.file.too.large=File {0} is too large to be uploaded. Maximum allowed size is {4} bytes!
struts.messages.upload.error.parameter.too.long=The request parameter "{0}" was too long. Max length allowed is {1}, but found {2}!
struts.messages.error.content.type.not.allowed=Content-Type not allowed: {0} "{1}" "{2}" {3}
struts.messages.error.file.extension.not.allowed=File extension not allowed: {0} "{1}" "{2}" {3}
@@ -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"/>
@@ -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>
@@ -30,7 +30,7 @@
<#assign itemKeyStr = stack.findString('top')>
</#if>
<#if parameters.listLabelKey??>
<#-- checks the valueStack for the 'valueKey.' The valueKey is then looked-up in the locale
<#-- checks the valueStack for the 'valueKey.' The valueKey is then looked-up in the locale
file for it's localized value. This is then used as a label -->
<#assign itemValue = struts.getText(stack.findString(parameters.listLabelKey))/>
<#elseif parameters.listValue??>
@@ -95,9 +95,10 @@
<#include "/${parameters.templateDir}/${parameters.expandTheme}/css.ftl" />
<#include "/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl" />
<#include "/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" />
<#global evaluate_dynamic_attributes = true/>
<#include "/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" />
/>
<label<#rt/>
<label<#rt/>
<#if parameters.id?has_content>
for="${parameters.id?html}-${itemCount}"<#rt/>
<#else>
@@ -106,11 +107,10 @@
class="checkboxLabel">${itemValue?html}</label>
</@s.iterator>
<#else>
&nbsp;
</#if>
<input type="hidden" id="__multiselect_${parameters.id?html}" name="__multiselect_${parameters.name?html}"
value=""<#rt/>
<#if parameters.disabled!false>
disabled="disabled"<#rt/>
</#if>
/>
/>
@@ -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" />
@@ -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>
@@ -23,9 +23,13 @@
<#list aKeys as aKey><#rt/>
<#assign keyValue = parameters.dynamicAttributes.get(aKey)/>
<#if keyValue?is_string>
<#assign value = struts.translateVariables(keyValue)!keyValue/>
<#if evaluate_dynamic_attributes!false == true>
<#assign value = struts.translateVariables(keyValue)!keyValue/><#rt/>
<#else>
<#assign value = keyValue/><#rt/>
</#if>
<#else>
<#assign value = keyValue?string/>
<#assign value = keyValue?string/><#rt/>
</#if>
${aKey}="${value?html}"<#rt/>
</#list><#rt/>
@@ -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>
@@ -27,7 +27,7 @@
<#assign itemKeyStr = stack.findString('top')>
</#if>
<#if parameters.listValueKey??>
<#-- checks the valueStack for the 'valueKey.' The valueKey is then looked-up in the locale
<#-- checks the valueStack for the 'valueKey.' The valueKey is then looked-up in the locale
file for it's localized value. This is then used as a label -->
<#assign valueKey = stack.findString(parameters.listValueKey)!''/>
<#if valueKey?has_content>
@@ -94,9 +94,10 @@
<#include "/${parameters.templateDir}/${parameters.expandTheme}/css.ftl" />
<#include "/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl" />
<#include "/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" />
<#global evaluate_dynamic_attributes = true/>
<#include "/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" />
/><#rt/>
<label for="${parameters.id?html}${itemKeyStr?html}"<#include "/${parameters.templateDir}/${parameters.expandTheme}/css.ftl"/>><#rt/>
${itemValue}<#t/>
</label>
</@s.iterator>
</@s.iterator>
@@ -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}
@@ -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();
}
@@ -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>
@@ -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;
@@ -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<>();
@@ -22,7 +22,7 @@ import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.XWorkTestCase;
import com.opensymphony.xwork2.util.ListHolder;
import com.opensymphony.xwork2.util.ValueStack;
import ognl.ListPropertyAccessor;
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
import ognl.PropertyAccessor;
import java.util.ArrayList;
@@ -42,11 +42,11 @@ public class XWorkListPropertyAccessorTest extends XWorkTestCase {
assertNotNull(listHolder.getLongs());
assertEquals(3, listHolder.getLongs().size());
assertEquals(new Long(1), (Long) listHolder.getLongs().get(0));
assertEquals(new Long(2), (Long) listHolder.getLongs().get(1));
assertEquals(new Long(3), (Long) listHolder.getLongs().get(2));
assertEquals(new Long(1), listHolder.getLongs().get(0));
assertEquals(new Long(2), listHolder.getLongs().get(1));
assertEquals(new Long(3), listHolder.getLongs().get(2));
assertTrue(((Boolean) vs.findValue("longs.contains(1)")).booleanValue());
assertTrue((Boolean) vs.findValue("longs.contains(1)"));
}
public void testCanAccessListSizeProperty() {
@@ -60,8 +60,8 @@ public class XWorkListPropertyAccessorTest extends XWorkTestCase {
vs.push(listHolder);
assertEquals(new Integer(myList.size()), vs.findValue("strings.size()"));
assertEquals(new Integer(myList.size()), vs.findValue("strings.size"));
assertEquals(myList.size(), vs.findValue("strings.size()"));
assertEquals(myList.size(), vs.findValue("strings.size"));
}
public void testAutoGrowthCollectionLimit() {
@@ -73,12 +73,14 @@ public class XWorkListPropertyAccessorTest extends XWorkTestCase {
listHolder.setStrings(myList);
ValueStack vs = ActionContext.getContext().getValueStack();
ReflectionContextState.setCreatingNullObjects(vs.getContext(), true);
vs.push(listHolder);
vs.setValue("strings[0]", "a");
vs.setValue("strings[1]", "b");
vs.setValue("strings[2]", "c");
vs.setValue("strings[3]", "d");
vs.findValue("strings[3]");
assertEquals(3, vs.findValue("strings.size()"));
}
@@ -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;
}
};
}
@@ -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;
}
};
}
@@ -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;
}
};
}
@@ -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);
}
}
}
@@ -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() {
@@ -89,7 +89,7 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase {
private File tempDir;
private TestAction action;
public void testAcceptFileWithEmptyAllowedTypesAndExtensions() throws Exception {
public void testAcceptFileWithEmptyAllowedTypesAndExtensions() {
// when allowed type is empty
ValidationAwareSupport validation = new ValidationAwareSupport();
boolean ok = interceptor.acceptFile(action, EMPTY_FILE, "filename", "text/plain", "inputName", validation);
@@ -99,7 +99,7 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase {
assertFalse(validation.hasErrors());
}
public void testAcceptFileWithoutEmptyTypes() throws Exception {
public void testAcceptFileWithoutEmptyTypes() {
interceptor.setAllowedTypes("text/plain");
// when file is of allowed types
@@ -120,7 +120,7 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase {
}
public void testAcceptFileWithWildcardContent() throws Exception {
public void testAcceptFileWithWildcardContent() {
interceptor.setAllowedTypes("text/*");
ValidationAwareSupport validation = new ValidationAwareSupport();
@@ -139,7 +139,7 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase {
assertTrue(validation.hasErrors());
}
public void testAcceptFileWithoutEmptyExtensions() throws Exception {
public void testAcceptFileWithoutEmptyExtensions() {
interceptor.setAllowedExtensions(".txt");
// when file is of allowed extensions
@@ -168,7 +168,7 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase {
assertFalse(validation.hasErrors());
}
public void testAcceptFileWithNoFile() throws Exception {
public void testAcceptFileWithNoFile() {
FileUploadInterceptor interceptor = new FileUploadInterceptor();
interceptor.setAllowedTypes("text/plain");
@@ -188,7 +188,7 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase {
public void testAcceptFileWithMaxSize() throws Exception {
interceptor.setAllowedTypes("text/plain");
interceptor.setMaximumSize(new Long(10));
interceptor.setMaximumSize(10L);
// when file is not of allowed types
ValidationAwareSupport validation = new ValidationAwareSupport();
@@ -201,7 +201,7 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase {
assertFalse(notOk);
assertFalse(validation.getFieldErrors().isEmpty());
assertTrue(validation.hasErrors());
List errors = (List) validation.getFieldErrors().get("inputName");
List errors = validation.getFieldErrors().get("inputName");
assertEquals(1, errors.size());
String msg = (String) errors.get(0);
// the error message should contain at least this test
@@ -235,7 +235,7 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase {
mai.setInvocationContext(ActionContext.getContext());
ActionContext.getContext().setParameters(HttpParameters.create().build());
ActionContext.getContext().put(ServletActionContext.HTTP_REQUEST, createMultipartRequest(req, 2000));
ActionContext.getContext().put(ServletActionContext.HTTP_REQUEST, createMultipartRequest(req, 2000, -1));
interceptor.intercept(mai);
@@ -257,7 +257,7 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase {
mai.setInvocationContext(ActionContext.getContext());
ActionContext.getContext().setParameters(HttpParameters.create().build());
ActionContext.getContext().put(ServletActionContext.HTTP_REQUEST, createMultipartRequest(req, 2000));
ActionContext.getContext().put(ServletActionContext.HTTP_REQUEST, createMultipartRequest(req, 2000, -1));
interceptor.intercept(mai);
@@ -278,7 +278,7 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase {
"Unit test of FileUploadInterceptor" +
"\r\n" +
"-----1234--\r\n");
req.setContent(content.getBytes("US-ASCII"));
req.setContent(content.getBytes(StandardCharsets.US_ASCII));
MyFileupAction action = new MyFileupAction();
@@ -288,14 +288,14 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase {
mai.setInvocationContext(ActionContext.getContext());
Map<String, Object> param = new HashMap<>();
ActionContext.getContext().setParameters(HttpParameters.create(param).build());
ActionContext.getContext().put(ServletActionContext.HTTP_REQUEST, createMultipartRequest(req, 2000));
ActionContext.getContext().put(ServletActionContext.HTTP_REQUEST, createMultipartRequest(req, 2000, -1));
interceptor.intercept(mai);
assertTrue(!action.hasErrors());
assertFalse(action.hasErrors());
HttpParameters parameters = mai.getInvocationContext().getParameters();
assertTrue(parameters.keySet().size() == 3);
assertEquals(3, parameters.keySet().size());
UploadedFile[] files = (UploadedFile[]) parameters.get("file").getObject();
String[] fileContentTypes = parameters.get("fileContentType").getMultipleValues();
String[] fileRealFilenames = parameters.get("fileFileName").getMultipleValues();
@@ -303,9 +303,9 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase {
assertNotNull(files);
assertNotNull(fileContentTypes);
assertNotNull(fileRealFilenames);
assertTrue(files.length == 1);
assertTrue(fileContentTypes.length == 1);
assertTrue(fileRealFilenames.length == 1);
assertEquals(1, files.length);
assertEquals(1, fileContentTypes.length);
assertEquals(1, fileRealFilenames.length);
assertEquals("text/html", fileContentTypes[0]);
assertNotNull("deleteme.txt", fileRealFilenames[0]);
}
@@ -349,7 +349,7 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase {
mai.setInvocationContext(ActionContext.getContext());
Map<String, Object> param = new HashMap<String, Object>();
ActionContext.getContext().setParameters(HttpParameters.create(param).build());
ActionContext.getContext().put(ServletActionContext.HTTP_REQUEST, createMultipartRequest(req, 2000));
ActionContext.getContext().put(ServletActionContext.HTTP_REQUEST, createMultipartRequest(req, 2000, -1));
interceptor.setAllowedTypes("text/html");
interceptor.intercept(mai);
@@ -370,6 +370,54 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase {
assertNotNull("test1.html", fileRealFilenames[0]);
}
public void testMultipartRequestMaxStringLength() throws Exception {
MockHttpServletRequest req = new MockHttpServletRequest();
req.setCharacterEncoding(StandardCharsets.UTF_8.name());
req.setMethod("post");
req.addHeader("Content-type", "multipart/form-data; boundary=---1234");
// inspired by the unit tests for jakarta commons fileupload
String content = ("-----1234\r\n" +
"Content-Disposition: form-data; name=\"file\"; filename=\"deleteme.txt\"\r\n" +
"Content-Type: text/html\r\n" +
"\r\n" +
"Unit test of FileUploadInterceptor" +
"\r\n" +
"-----1234\r\n" +
"Content-Disposition: form-data; name=\"normalFormField1\"\r\n" +
"\r\n" +
"it works" +
"\r\n" +
"-----1234\r\n" +
"Content-Disposition: form-data; name=\"normalFormField2\"\r\n" +
"\r\n" +
"long string should not work" +
"\r\n" +
"-----1234--\r\n");
req.setContent(content.getBytes(StandardCharsets.US_ASCII));
MyFileupAction action = container.inject(MyFileupAction.class);
MockActionInvocation mai = new MockActionInvocation();
mai.setAction(action);
mai.setResultCode("success");
mai.setInvocationContext(ActionContext.getContext());
Map<String, Object> param = new HashMap<>();
ActionContext.getContext().setParameters(HttpParameters.create(param).build());
ActionContext.getContext().put(ServletActionContext.HTTP_REQUEST, createMultipartRequest(req, -1, 20));
interceptor.intercept(mai);
assertTrue(action.hasActionErrors());
Collection<String> errors = action.getActionErrors();
assertEquals(1, errors.size());
String msg = errors.iterator().next();
assertEquals(
"The request parameter \"normalFormField2\" was too long. Max length allowed is 20, but found 27!",
msg);
}
public void testMultipartRequestLocalizedError() throws Exception {
MockHttpServletRequest req = new MockHttpServletRequest();
req.setCharacterEncoding(StandardCharsets.UTF_8.name());
@@ -384,7 +432,7 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase {
"Unit test of FileUploadInterceptor" +
"\r\n" +
"-----1234--\r\n");
req.setContent(content.getBytes("US-ASCII"));
req.setContent(content.getBytes(StandardCharsets.US_ASCII));
MyFileupAction action = container.inject(MyFileupAction.class);
@@ -396,7 +444,7 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase {
ActionContext.getContext().setParameters(HttpParameters.create(param).build());
// set German locale
ActionContext.getContext().setLocale(Locale.GERMAN);
ActionContext.getContext().put(ServletActionContext.HTTP_REQUEST, createMultipartRequest(req, 10));
ActionContext.getContext().put(ServletActionContext.HTTP_REQUEST, createMultipartRequest(req, 10, -1));
interceptor.intercept(mai);
@@ -429,9 +477,10 @@ public class FileUploadInterceptorTest extends StrutsInternalTestCase {
return sb.toString();
}
private MultiPartRequestWrapper createMultipartRequest(HttpServletRequest req, int maxsize) throws IOException {
private MultiPartRequestWrapper createMultipartRequest(HttpServletRequest req, int maxsize, int maxStringLength) {
JakartaMultiPartRequest jak = new JakartaMultiPartRequest();
jak.setMaxSize(String.valueOf(maxsize));
jak.setMaxStringLength(String.valueOf(maxStringLength));
return new MultiPartRequestWrapper(jak, req, tempDir.getAbsolutePath(), new DefaultLocaleProvider());
}
@@ -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();
}
}
@@ -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("", "")
@@ -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";
}
@@ -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) {
@@ -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();
@@ -24,6 +24,7 @@ import org.apache.logging.log4j.LogManager;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.views.jsp.ui.AbstractUITag;
import org.fest.assertions.Assertions;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
@@ -213,24 +214,28 @@ public abstract class AbstractUITagTest extends AbstractTagTest {
* Attempt to verify the contents of this.writer against the contents of the URL specified. verify() performs a
* trim on both ends
*
* @param url the HTML snippet that we want to validate against
* @param urls the HTML snippets that we want to validate against any of them
* @throws Exception if the validation failed
*/
public void verify(URL url) throws Exception {
if (url == null) {
fail("unable to verify a null URL");
} else if (this.writer == null) {
fail("AbstractJspWriter.writer not initialized. Unable to verify");
}
public void verify(URL... urls) throws Exception {
List<String> bufferStrings = new ArrayList<>();
for(URL url : urls) {
if (url == null) {
fail("unable to verify a null URL");
} else if (this.writer == null) {
fail("AbstractJspWriter.writer not initialized. Unable to verify");
}
StringBuilder buffer = new StringBuilder(128);
try (InputStream in = url.openStream()) {
byte[] buf = new byte[4096];
int nbytes;
while ((nbytes = in.read(buf)) > 0) {
buffer.append(new String(buf, 0, nbytes));
}
StringBuilder buffer = new StringBuilder(128);
try (InputStream in = url.openStream()) {
byte[] buf = new byte[4096];
int nbytes;
while ((nbytes = in.read(buf)) > 0) {
buffer.append(new String(buf, 0, nbytes));
}
}
bufferStrings.add(normalize(buffer.toString(), true));
}
/**
@@ -238,9 +243,12 @@ public abstract class AbstractUITagTest extends AbstractTagTest {
* normalize the strings first to account for line termination differences between platforms.
*/
String writerString = normalize(writer.toString(), true);
String bufferString = normalize(buffer.toString(), true);
assertEquals(bufferString, writerString);
if (bufferStrings.size() == 1) {
assertEquals(bufferStrings.get(0), writerString);
} else {
Assertions.assertThat(writerString).isIn(bufferStrings);
}
}
protected void setUp() throws Exception {
@@ -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();
}
}
@@ -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();
@@ -92,7 +92,7 @@ public class TextfieldTest extends AbstractUITagTest {
verify(TextFieldTag.class.getResource("Textfield-3.txt"));
}
public void testLabelSeparatorJsp() throws Exception {
TestAction testAction = (TestAction) action;
testAction.setFoo("bar");
@@ -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,13 +172,16 @@ public class TextfieldTest extends AbstractUITagTest {
tag.setName("myname");
tag.setValue("%{foo}");
tag.setSize("10");
tag.setDynamicAttribute(null, "anotherAttr", "%{foo}");
tag.setDynamicAttribute(null, "secondAttr", "second_%{foo}");
tag.doStartTag();
tag.doEndTag();
verify(TextFieldTag.class.getResource("Textfield-5.txt"));
verify(TextFieldTag.class.getResource("Textfield-5.txt"),
TextFieldTag.class.getResource("Textfield-5jdk8.txt"));
}
public void testSimple_recursionTestNoValue() throws Exception {
TestAction testAction = (TestAction) action;
testAction.setFoo("%{1+1}");
@@ -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>
<@s.iterator value="{'a','b','c'}" status="stat">
<@s.textfield id="itemId%{#stat.index}" name="items[%{#stat.index}].name" theme="simple"/>
</@s.iterator>
@@ -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>
@@ -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>
@@ -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" secondAttr="second_%{1+1}" anotherAttr="%{1+1}"/></td>
</tr>
@@ -0,0 +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" anotherAttr="%{1+1}" secondAttr="second_%{1+1}"/></td>
</tr>
+5
View File
@@ -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">
+23 -1
View File
@@ -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>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -25,7 +25,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-cdi-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-config-browser-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-convention-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-dwr-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-embeddedjsp-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-gxp-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-jasperreports-plugin</artifactId>
@@ -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;
}
}
@@ -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;
}
};
}
@@ -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>
+1 -1
View File
@@ -25,7 +25,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-javatemplates-plugin</artifactId>
@@ -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
@@ -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
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-jfreechart-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-json-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-junit-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-osgi-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-oval-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-pell-multipart-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-plexus-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-plugins</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-portlet-tiles-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-portlet-plugin</artifactId>
@@ -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);
}
}
}
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-rest-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-sitegraph-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-sitemesh-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-spring-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-testng-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.5.28</version>
<version>2.5.31</version>
</parent>
<artifactId>struts2-tiles-plugin</artifactId>

Some files were not shown because too many files have changed in this diff Show More