Merge remote-tracking branch 'origin/master' into merge-master-to-7xx-2024-02-01

# Conflicts:
#	Jenkinsfile
#	core/src/main/java/org/apache/struts2/views/jsp/ComponentTagSupport.java
#	core/src/test/java/org/apache/struts2/views/jsp/IteratorTagTest.java
#	pom.xml
This commit is contained in:
Lukasz Lenart
2024-02-01 09:55:34 +01:00
39 changed files with 1564 additions and 181 deletions
+1 -1
View File
@@ -58,7 +58,7 @@ jobs:
publish_results: true
- name: "Upload artifact"
uses: actions/upload-artifact@1eb3cb2b3e0f29609092a73eb033bb759a334595 # 4.1.0
uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # 4.3.0
with:
name: SARIF file
path: results.sarif
+1
View File
@@ -32,6 +32,7 @@ jobs:
sonarcloud:
name: Scan
runs-on: ubuntu-latest
if: ${{ !github.event.pull_request.head.repo.fork }}
steps:
- uses: actions/checkout@v4
with:
Vendored
+5 -5
View File
@@ -67,7 +67,7 @@ pipeline {
MAVEN_OPTS = "-Xmx1024m"
}
stages {
stage('Test') {
stage('Test & Coverage') {
steps {
sh './mvnw -B verify -Pcoverage -DskipAssembly --no-transfer-progress'
}
@@ -86,7 +86,7 @@ pipeline {
}
steps {
withCredentials([string(credentialsId: 'asf-struts-sonarcloud', variable: 'SONARCLOUD_TOKEN')]) {
sh './mvnw -B -Pcoverage -DskipAssembly -Dsonar.login=${SONARCLOUD_TOKEN} verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar'
sh './mvnw -B -Pcoverage -DskipAssembly -Dsonar.login=${SONARCLOUD_TOKEN} verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar --no-transfer-progress'
}
}
}
@@ -100,7 +100,7 @@ pipeline {
dir("local-snapshots-dir/") {
deleteDir()
}
sh './mvnw -B source:jar javadoc:jar -DskipTests -DskipAssembly'
sh './mvnw -B source:jar javadoc:jar -DskipTests -DskipAssembly --no-transfer-progress'
}
}
stage('Deploy Snapshot') {
@@ -111,7 +111,7 @@ pipeline {
}
steps {
withCredentials([file(credentialsId: 'lukaszlenart-repository-access-token', variable: 'CUSTOM_SETTINGS')]) {
sh './mvnw -s \${CUSTOM_SETTINGS} deploy -DskipTests -DskipAssembly'
sh './mvnw -s \${CUSTOM_SETTINGS} deploy -DskipTests -DskipAssembly --no-transfer-progress'
}
}
}
@@ -123,7 +123,7 @@ pipeline {
}
}
steps {
sh './mvnw -B package -DskipTests'
sh './mvnw -B package -DskipTests --no-transfer-progress'
sshPublisher(publishers: [
sshPublisherDesc(
configName: 'Nightlies',
+6 -6
View File
@@ -2,13 +2,13 @@
## Supported Versions
Please vist the [Releases](https://struts.apache.org/releases.html#prior-releases) page to see full information about each version
Please visit the [Releases](https://struts.apache.org/releases.html#prior-releases) page to see full information about each version
and what potential vulnerability it can have:
| Version | Supported |
| ------- | ------------------ |
| 6.0.0 | :white_check_mark: |
| 2.5.30 | :white_check_mark: |
|---------|--------------------|
| 6.x | :white_check_mark: |
| 2.5.x | :white_check_mark: |
## Reporting New Security Issues with thr Apache Struts
@@ -28,8 +28,8 @@ All mail sent to this address that does not relate to security problems in the A
```
Note that all networked servers are subject to denial of service attacks, and we cannot promise magic
workarounds to generic problems (such as a client streaming lots of data to your server, or re-requesting
the same URL repeatedly). In general our philosophy is to avoid any attacks which can cause the server
workarounds to generic problems (such as a client streaming lots of data to your server, or requesting
the same URL repeatedly). In general, our philosophy is to avoid any attacks that can cause the server
to consume resources in a non-linear relationship to the size of inputs.
The mailing address is: [security@struts.apache.org](mailto:security@struts.apache.org)
@@ -24,9 +24,15 @@ import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.Validateable;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.interceptor.parameter.StrutsParameter;
import java.io.File;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*/
@@ -89,6 +95,7 @@ public class UITagExample extends ActionSupport implements Validateable {
return leftSideCartoonCharacters;
}
@StrutsParameter
public void setLeftSideCartoonCharacters(List leftSideCartoonCharacters) {
this.leftSideCartoonCharacters = leftSideCartoonCharacters;
}
@@ -98,6 +105,7 @@ public class UITagExample extends ActionSupport implements Validateable {
return rightSideCartoonCharacters;
}
@StrutsParameter
public void setRightSideCartoonCharacters(List rightSideCartoonCharacters) {
this.rightSideCartoonCharacters = rightSideCartoonCharacters;
}
@@ -107,6 +115,7 @@ public class UITagExample extends ActionSupport implements Validateable {
return favouriteVehicalType;
}
@StrutsParameter
public void setFavouriteVehicalType(String favouriteVehicalType) {
this.favouriteVehicalType = favouriteVehicalType;
}
@@ -115,6 +124,7 @@ public class UITagExample extends ActionSupport implements Validateable {
return favouriteVehicalSpecific;
}
@StrutsParameter
public void setFavouriteVehicalSpecific(String favouriteVehicalSpecific) {
this.favouriteVehicalSpecific = favouriteVehicalSpecific;
}
@@ -145,6 +155,7 @@ public class UITagExample extends ActionSupport implements Validateable {
return name;
}
@StrutsParameter
public void setName(String name) {
this.name = name;
}
@@ -153,6 +164,7 @@ public class UITagExample extends ActionSupport implements Validateable {
return birthday;
}
@StrutsParameter
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@@ -161,6 +173,7 @@ public class UITagExample extends ActionSupport implements Validateable {
return bio;
}
@StrutsParameter
public void setBio(String bio) {
this.bio = bio;
}
@@ -169,6 +182,7 @@ public class UITagExample extends ActionSupport implements Validateable {
return favouriteColor;
}
@StrutsParameter
public void setFavouriteColor(String favoriteColor) {
this.favouriteColor = favoriteColor;
}
@@ -177,6 +191,7 @@ public class UITagExample extends ActionSupport implements Validateable {
return friends;
}
@StrutsParameter
public void setFriends(List friends) {
this.friends = friends;
}
@@ -193,6 +208,7 @@ public class UITagExample extends ActionSupport implements Validateable {
return legalAge;
}
@StrutsParameter
public void setLegalAge(boolean legalAge) {
this.legalAge = legalAge;
}
@@ -201,6 +217,7 @@ public class UITagExample extends ActionSupport implements Validateable {
return state;
}
@StrutsParameter
public void setState(String state) {
this.state = state;
}
@@ -209,6 +226,7 @@ public class UITagExample extends ActionSupport implements Validateable {
return region;
}
@StrutsParameter
public void setRegion(String region) {
this.region = region;
}
@@ -229,6 +247,7 @@ public class UITagExample extends ActionSupport implements Validateable {
this.pictureFileName = pictureFileName;
}
@StrutsParameter
public void setFavouriteLanguage(String favouriteLanguage) {
this.favouriteLanguage = favouriteLanguage;
}
@@ -237,7 +256,7 @@ public class UITagExample extends ActionSupport implements Validateable {
return favouriteLanguage;
}
@StrutsParameter
public void setThoughts(String thoughts) {
this.thoughts = thoughts;
}
@@ -250,6 +269,7 @@ public class UITagExample extends ActionSupport implements Validateable {
return wakeup;
}
@StrutsParameter
public void setWakeup(Date wakeup) {
this.wakeup = wakeup;
}
@@ -0,0 +1,133 @@
/*
* 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.showcase.action;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.interceptor.parameter.StrutsParameter;
import org.apache.struts2.showcase.model.MyDto;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
/**
* This class supports {@link com.atlassian.confluence.stateless.webdriver.selenium3.security.StrutsParametersTest}
* which prevents critical security regressions. Do NOT modify without understanding the motivation behind the tests and
* the implications of any changes.
*/
public class ParamsAnnotationAction extends ActionSupport {
@StrutsParameter
public String varToPrint;
public String publicField = "no";
@StrutsParameter
public String publicFieldAnnotated = "no";
private String privateField = "no";
public int[] publicArray = new int[]{0};
@StrutsParameter(depth = 1)
public int[] publicArrayAnnotated = new int[]{0};
public List<String> publicList = new ArrayList<>(singletonList("no"));
@StrutsParameter(depth = 1)
public List<String> publicListAnnotated = new ArrayList<>(singletonList("no"));
private List<String> privateList = new ArrayList<>(singletonList("no"));
public Map<String, String> publicMap = new HashMap<>(singletonMap("key", "no"));
@StrutsParameter(depth = 1)
public Map<String, String> publicMapAnnotated = new HashMap<>(singletonMap("key", "no"));
public MyDto publicMyDto = new MyDto();
@StrutsParameter(depth = 2)
public MyDto publicMyDtoAnnotated = new MyDto();
@StrutsParameter(depth = 1)
public MyDto publicMyDtoAnnotatedDepthOne = new MyDto();
private MyDto privateMyDto = new MyDto();
public void setPrivateFieldMethod(String privateField) {
this.privateField = privateField;
}
@StrutsParameter
public void setPrivateFieldMethodAnnotated(String privateField) {
this.privateField = privateField;
}
public List<String> getPrivateListMethod() {
return privateList;
}
@StrutsParameter(depth = 1)
public List<String> getPrivateListMethodAnnotated() {
return privateList;
}
public MyDto getUnsafeMethodMyDto() {
return privateMyDto;
}
@StrutsParameter(depth = 2)
public MyDto getSafeMethodMyDto() {
return privateMyDto;
}
@StrutsParameter(depth = 1)
public MyDto getSafeMethodMyDtoDepthOne() {
return privateMyDto;
}
public String renderVarToPrint() throws ReflectiveOperationException {
if (varToPrint == null) {
return "null";
}
Field field = this.getClass().getDeclaredField(varToPrint);
field.setAccessible(true);
try {
return String.format("%s{%s}", varToPrint,
field.getType().isArray() ? stringifyArray(field.get(this)) : field.get(this));
} finally {
field.setAccessible(false);
}
}
private String stringifyArray(Object array) {
switch (array.getClass().getComponentType().getName()) {
case "int":
return Arrays.toString((int[]) array);
default:
return "TODO";
}
}
}
@@ -21,6 +21,7 @@ package org.apache.struts2.showcase.action;
import com.opensymphony.xwork2.Preparable;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.interceptor.parameter.StrutsParameter;
import org.apache.struts2.showcase.dao.Dao;
import org.apache.struts2.showcase.dao.SkillDao;
import org.apache.struts2.showcase.model.Skill;
@@ -71,6 +72,7 @@ public class SkillAction extends AbstractCRUDAction implements Preparable {
return skillDao;
}
@StrutsParameter(depth = 1)
public Skill getCurrentSkill() {
return currentSkill;
}
@@ -19,6 +19,7 @@
package org.apache.struts2.showcase.async;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.interceptor.parameter.StrutsParameter;
import java.util.ArrayList;
import java.util.List;
@@ -34,10 +35,12 @@ public class ChatRoomAction extends ActionSupport {
private static final List<String> messages = new ArrayList<>();
@StrutsParameter
public void setMessage(String message) {
this.message = message;
}
@StrutsParameter
public void setLastIndex(Integer lastIndex) {
this.lastIndex = lastIndex;
}
@@ -21,6 +21,7 @@
package org.apache.struts2.showcase.conversion;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.interceptor.parameter.StrutsParameter;
import java.util.LinkedHashSet;
import java.util.Set;
@@ -30,7 +31,7 @@ import java.util.Set;
*/
public class AddressAction extends ActionSupport {
private Set<Address> addresses = new LinkedHashSet<Address>();
private Set<Address> addresses = new LinkedHashSet<>();
public String input() throws Exception {
return SUCCESS;
@@ -41,6 +42,7 @@ public class AddressAction extends ActionSupport {
return SUCCESS;
}
@StrutsParameter(depth = 2)
public Set<Address> getAddresses() {
return addresses;
}
@@ -21,6 +21,7 @@
package org.apache.struts2.showcase.conversion;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.interceptor.parameter.StrutsParameter;
import java.util.Arrays;
import java.util.LinkedList;
@@ -47,6 +48,7 @@ public class OperationsEnumAction extends ActionSupport {
return this.selectedOperations;
}
@StrutsParameter
public void setSelectedOperations(List<OperationsEnum> selectedOperations) {
this.selectedOperations = selectedOperations;
}
@@ -21,6 +21,7 @@
package org.apache.struts2.showcase.conversion;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.interceptor.parameter.StrutsParameter;
import java.util.List;
@@ -36,6 +37,7 @@ public class PersonAction extends ActionSupport {
return SUCCESS;
}
@StrutsParameter(depth = 2)
public List<Person> getPersons() {
return persons;
}
@@ -22,6 +22,7 @@ package org.apache.struts2.showcase.filedownload;
import com.opensymphony.xwork2.Action;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.interceptor.parameter.StrutsParameter;
import java.io.InputStream;
@@ -38,6 +39,7 @@ public class FileDownloadAction implements Action {
return SUCCESS;
}
@StrutsParameter
public void setInputPath(String value) {
inputPath = sanitizeInputPath(value);
}
@@ -23,6 +23,7 @@ package org.apache.struts2.showcase.fileupload;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.action.UploadedFilesAware;
import org.apache.struts2.dispatcher.multipart.UploadedFile;
import org.apache.struts2.interceptor.parameter.StrutsParameter;
import java.io.File;
import java.util.List;
@@ -66,6 +67,7 @@ public class FileUploadAction extends ActionSupport implements UploadedFilesAwar
return caption;
}
@StrutsParameter
public void setCaption(String caption) {
this.caption = caption;
}
@@ -0,0 +1,38 @@
/*
* 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.showcase.model;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import static java.util.Collections.singletonMap;
public class MyDto {
public String str = "no";
public Map<String, String> map = new HashMap<>(singletonMap("key", "no"));
public int[] array = new int[]{0};
@Override
public String toString() {
return "str=" + str + ", map=" + map + ", array=" + Arrays.toString(array);
}
}
@@ -20,6 +20,8 @@
*/
package org.apache.struts2.showcase.validation;
import org.apache.struts2.interceptor.parameter.StrutsParameter;
import java.sql.Date;
/**
@@ -44,6 +46,7 @@ public class FieldValidatorsExampleAction extends AbstractValidationActionSuppor
return dateValidatorField;
}
@StrutsParameter
public void setDateValidatorField(Date dateValidatorField) {
this.dateValidatorField = dateValidatorField;
}
@@ -52,6 +55,7 @@ public class FieldValidatorsExampleAction extends AbstractValidationActionSuppor
return emailValidatorField;
}
@StrutsParameter
public void setEmailValidatorField(String emailValidatorField) {
this.emailValidatorField = emailValidatorField;
}
@@ -60,6 +64,7 @@ public class FieldValidatorsExampleAction extends AbstractValidationActionSuppor
return integerValidatorField;
}
@StrutsParameter
public void setIntegerValidatorField(Integer integerValidatorField) {
this.integerValidatorField = integerValidatorField;
}
@@ -68,6 +73,7 @@ public class FieldValidatorsExampleAction extends AbstractValidationActionSuppor
return regexValidatorField;
}
@StrutsParameter
public void setRegexValidatorField(String regexValidatorField) {
this.regexValidatorField = regexValidatorField;
}
@@ -76,6 +82,7 @@ public class FieldValidatorsExampleAction extends AbstractValidationActionSuppor
return requiredStringValidatorField;
}
@StrutsParameter
public void setRequiredStringValidatorField(String requiredStringValidatorField) {
this.requiredStringValidatorField = requiredStringValidatorField;
}
@@ -84,6 +91,7 @@ public class FieldValidatorsExampleAction extends AbstractValidationActionSuppor
return requiredValidatorField;
}
@StrutsParameter
public void setRequiredValidatorField(String requiredValidatorField) {
this.requiredValidatorField = requiredValidatorField;
}
@@ -92,6 +100,7 @@ public class FieldValidatorsExampleAction extends AbstractValidationActionSuppor
return stringLengthValidatorField;
}
@StrutsParameter
public void setStringLengthValidatorField(String stringLengthValidatorField) {
this.stringLengthValidatorField = stringLengthValidatorField;
}
@@ -100,6 +109,7 @@ public class FieldValidatorsExampleAction extends AbstractValidationActionSuppor
return fieldExpressionValidatorField;
}
@StrutsParameter
public void setFieldExpressionValidatorField(
String fieldExpressionValidatorField) {
this.fieldExpressionValidatorField = fieldExpressionValidatorField;
@@ -109,6 +119,7 @@ public class FieldValidatorsExampleAction extends AbstractValidationActionSuppor
return urlValidatorField;
}
@StrutsParameter
public void setUrlValidatorField(String urlValidatorField) {
this.urlValidatorField = urlValidatorField;
}
@@ -21,6 +21,7 @@
package org.apache.struts2.showcase.wait;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.interceptor.parameter.StrutsParameter;
/**
* Example to illustrate the <code>execAndWait</code> interceptor.
@@ -41,6 +42,7 @@ public class LongProcessAction extends ActionSupport {
return time;
}
@StrutsParameter
public void setTime(int time) {
this.time = time;
}
@@ -0,0 +1,32 @@
<?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.
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
<struts>
<package name="params-annotation" extends="velocity-default" namespace="/paramsannotation">
<action name="test" class="org.apache.struts2.showcase.action.ParamsAnnotationAction">
<result type="velocity">/WEB-INF/paramsannotation.vm</result>
</action>
</package>
</struts>
+3 -11
View File
@@ -35,17 +35,7 @@
<constant name="struts.action.extension" value="action,," />
<constant name="struts.allowlist.enable" value="true" />
<constant name="struts.allowlist.packageNames"
value="
org.apache.struts2.showcase.model,
org.apache.struts2.showcase.conversion
"/>
<constant name="struts.allowlist.classes"
value="
org.apache.struts2.showcase.UITagExample$Language,
org.apache.struts2.showcase.UITagExample$VehicalType,
org.apache.struts2.showcase.UITagExample$VehicalSpecific
"/>
<constant name="struts.parameters.requireAnnotations" value="true" />
<constant name="struts.convention.package.locators.basePackage" value="org.apache.struts2.showcase" />
<constant name="struts.convention.result.path" value="/WEB-INF" />
@@ -93,6 +83,8 @@
<include file="struts-dispatcher.xml" />
<include file="struts-params-annotation.xml" />
<package name="default" extends="struts-default">
<interceptors>
<interceptor-stack name="crudStack">
@@ -0,0 +1,19 @@
#*
* 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.
*#
<div id="output">$action.renderVarToPrint()</div>
@@ -0,0 +1,239 @@
/*
* 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 it.org.apache.struts2.showcase;
import org.htmlunit.WebClient;
import org.htmlunit.html.HtmlPage;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.web.util.UriComponentsBuilder;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.assertEquals;
public class StrutsParametersTest {
private WebClient webClient;
@Before
public void setUp() throws Exception {
webClient = new WebClient();
}
@After
public void tearDown() throws Exception {
webClient.close();
}
@Test
public void public_StringField_WithoutGetterSetter_FieldNotAnnotated() throws Exception {
Map<String, String> params = new HashMap<>();
params.put("publicField", "yes");
params.put("varToPrint", "publicField");
assertText(params, "publicField{no}");
}
@Test
public void public_StringField_WithoutGetterSetter_FieldAnnotated() throws Exception {
Map<String, String> params = new HashMap<>();
params.put("publicFieldAnnotated", "yes");
params.put("varToPrint", "publicFieldAnnotated");
assertText(params, "publicFieldAnnotated{yes}");
}
@Test
public void private_StringField_WithSetter_MethodNotAnnotated() throws Exception {
Map<String, String> params = new HashMap<>();
params.put("privateFieldMethod", "yes");
params.put("varToPrint", "privateField");
assertText(params, "privateField{no}");
}
@Test
public void private_StringField_WithSetter_MethodAnnotated() throws Exception {
Map<String, String> params = new HashMap<>();
params.put("privateFieldMethodAnnotated", "yes");
params.put("varToPrint", "privateField");
assertText(params, "privateField{yes}");
}
@Test
public void public_ArrayField_WithoutGetterSetter_FieldNotAnnotated() throws Exception {
Map<String, String> params = new HashMap<>();
params.put("publicArray[0]", "1");
params.put("varToPrint", "publicArray");
assertText(params, "publicArray{[0]}");
}
@Test
public void public_ArrayField_WithoutGetterSetter_FieldAnnotated() throws Exception {
Map<String, String> params = new HashMap<>();
params.put("publicArrayAnnotated[0]", "1");
params.put("varToPrint", "publicArrayAnnotated");
assertText(params, "publicArrayAnnotated{[1]}");
}
@Test
public void public_ListField_WithoutGetterSetter_FieldNotAnnotated() throws Exception {
Map<String, String> params = new HashMap<>();
params.put("publicList[0]", "yes");
params.put("varToPrint", "publicList");
assertText(params, "publicList{[no]}");
}
@Test
public void public_ListField_WithoutGetterSetter_FieldAnnotated() throws Exception {
Map<String, String> params = new HashMap<>();
params.put("publicListAnnotated[0]", "yes");
params.put("varToPrint", "publicListAnnotated");
assertText(params, "publicListAnnotated{[yes]}");
}
@Test
public void private_ListField_WithGetterNoSetter_MethodNotAnnotated() throws Exception {
Map<String, String> params = new HashMap<>();
params.put("privateListMethod[0]", "yes");
params.put("varToPrint", "privateList");
assertText(params, "privateList{[no]}");
}
@Test
public void private_ListField_WithGetterNoSetter_MethodAnnotated() throws Exception {
Map<String, String> params = new HashMap<>();
params.put("privateListMethodAnnotated[0]", "yes");
params.put("varToPrint", "privateList");
assertText(params, "privateList{[yes]}");
}
@Test
public void public_MapField_WithoutGetterSetter_FieldNotAnnotated() throws Exception {
Map<String, String> params = new HashMap<>();
params.put("publicMap['key']", "yes");
params.put("varToPrint", "publicMap");
assertText(params, "publicMap{{key=no}}");
}
@Test
public void public_MapField_WithoutGetterSetter_FieldAnnotated() throws Exception {
Map<String, String> params = new HashMap<>();
params.put("publicMapAnnotated['key']", "yes");
params.put("varToPrint", "publicMapAnnotated");
assertText(params, "publicMapAnnotated{{key=yes}}");
}
@Test
public void public_MapField_Insert_WithoutGetterSetter_FieldNotAnnotated() throws Exception {
Map<String, String> params = new HashMap<>();
params.put("publicMap[999]", "yes");
params.put("varToPrint", "publicMap");
assertText(params, "publicMap{{key=no}}");
}
@Test
public void public_MapField_Insert_WithoutGetterSetter_FieldAnnotated() throws Exception {
Map<String, String> params = new HashMap<>();
params.put("publicMapAnnotated[999]", "yes");
params.put("varToPrint", "publicMapAnnotated");
assertText(params, "publicMapAnnotated{{999=yes, key=no}}");
}
@Test
public void public_MyDtoField_WithoutGetter_FieldNotAnnotated() throws Exception {
Map<String, String> params = new HashMap<>();
params.put("publicMyDto.str", "yes");
params.put("publicMyDto.map['key']", "yes");
params.put("publicMyDto.array[0]", "1");
params.put("varToPrint", "publicMyDto");
assertText(params, "publicMyDto{str=no, map={key=no}, array=[0]}");
}
@Test
public void public_MyDtoField_WithoutGetter_FieldAnnotated() throws Exception {
Map<String, String> params = new HashMap<>();
params.put("publicMyDtoAnnotated.str", "yes");
params.put("publicMyDtoAnnotated.map['key']", "yes");
params.put("publicMyDtoAnnotated.array[0]", "1");
params.put("varToPrint", "publicMyDtoAnnotated");
assertText(params, "publicMyDtoAnnotated{str=yes, map={key=yes}, array=[1]}");
}
@Test
public void public_MyDtoField_WithoutGetter_FieldAnnotatedDepthOne() throws Exception {
Map<String, String> params = new HashMap<>();
params.put("publicMyDtoAnnotatedDepthOne.str", "yes");
params.put("publicMyDtoAnnotatedDepthOne.map['key']", "yes");
params.put("publicMyDtoAnnotatedDepthOne.array[0]", "1");
params.put("varToPrint", "publicMyDtoAnnotatedDepthOne");
assertText(params, "publicMyDtoAnnotatedDepthOne{str=yes, map={key=no}, array=[0]}");
}
@Test
public void private_MyDtoField_WithGetter_MethodNotAnnotated() throws Exception {
Map<String, String> params = new HashMap<>();
params.put("unsafeMethodMyDto.str", "yes");
params.put("unsafeMethodMyDto.map['key']", "yes");
params.put("unsafeMethodMyDto.array[0]", "1");
params.put("varToPrint", "privateMyDto");
assertText(params, "privateMyDto{str=no, map={key=no}, array=[0]}");
}
@Test
public void private_MyDtoField_WithGetter_MethodNotAnnotated_Alternate() throws Exception {
Map<String, String> params = new HashMap<>();
params.put("unsafeMethodMyDto['str']", "yes");
params.put("unsafeMethodMyDto['map']['key']", "yes");
params.put("unsafeMethodMyDto['map'][999]", "yes");
params.put("unsafeMethodMyDto['array'][0]", "1");
params.put("varToPrint", "privateMyDto");
assertText(params, "privateMyDto{str=no, map={key=no}, array=[0]}");
}
@Test
public void private_MyDtoField_WithGetter_MethodAnnotated() throws Exception {
Map<String, String> params = new HashMap<>();
params.put("safeMethodMyDto.str", "yes");
params.put("safeMethodMyDto.map['key']", "yes");
params.put("safeMethodMyDto.array[0]", "1");
params.put("varToPrint", "privateMyDto");
assertText(params, "privateMyDto{str=yes, map={key=yes}, array=[1]}");
}
@Test
public void private_MyDtoField_WithGetter_MethodAnnotatedDepthOne() throws Exception {
Map<String, String> params = new HashMap<>();
params.put("safeMethodMyDtoDepthOne.str", "yes");
params.put("safeMethodMyDtoDepthOne.map['key']", "yes");
params.put("safeMethodMyDtoDepthOne.array[0]", "1");
params.put("varToPrint", "privateMyDto");
assertText(params, "privateMyDto{str=yes, map={key=no}, array=[0]}");
}
private void assertText(Map<String, String> params, String text) throws IOException {
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(ParameterUtils.getBaseUrl()).path("/paramsannotation/test.action");
params.forEach(builder::queryParam);
String url = builder.toUriString();
HtmlPage page = webClient.getPage(url);
String output = page.getElementById("output").asNormalizedText();
assertEquals(text, output);
}
}
@@ -112,6 +112,7 @@ import org.apache.struts2.conversion.StrutsTypeConverterHolder;
import org.apache.struts2.ognl.OgnlGuard;
import org.apache.struts2.ognl.ProviderAllowlist;
import org.apache.struts2.ognl.StrutsOgnlGuard;
import org.apache.struts2.ognl.ThreadAllowlist;
import java.util.ArrayList;
import java.util.Collections;
@@ -395,6 +396,7 @@ public class DefaultConfiguration implements Configuration {
.factory(SecurityMemberAccess.class, Scope.PROTOTYPE)
.factory(OgnlGuard.class, StrutsOgnlGuard.class, Scope.SINGLETON)
.factory(ProviderAllowlist.class, Scope.SINGLETON)
.factory(ThreadAllowlist.class, Scope.SINGLETON)
.factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON);
}
@@ -26,6 +26,7 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.ognl.ProviderAllowlist;
import org.apache.struts2.ognl.ThreadAllowlist;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
@@ -75,6 +76,7 @@ public class SecurityMemberAccess implements MemberAccess {
)));
private final ProviderAllowlist providerAllowlist;
private final ThreadAllowlist threadAllowlist;
private boolean allowStaticFieldAccess = true;
private Set<Pattern> excludeProperties = emptySet();
private Set<Pattern> acceptProperties = emptySet();
@@ -89,8 +91,9 @@ public class SecurityMemberAccess implements MemberAccess {
private boolean disallowDefaultPackageAccess = false;
@Inject
public SecurityMemberAccess(@Inject ProviderAllowlist providerAllowlist) {
public SecurityMemberAccess(@Inject ProviderAllowlist providerAllowlist, @Inject ThreadAllowlist threadAllowlist) {
this.providerAllowlist = providerAllowlist;
this.threadAllowlist = threadAllowlist;
}
/**
@@ -99,11 +102,11 @@ public class SecurityMemberAccess implements MemberAccess {
* - block or allow access to properties (configurable-after-construction)
*
* @param allowStaticFieldAccess if set to true static fields (constants) will be accessible
* @deprecated since 6.4.0, use {@link #SecurityMemberAccess(ProviderAllowlist)} instead.
* @deprecated since 6.4.0, use {@link #SecurityMemberAccess(ProviderAllowlist, ThreadAllowlist)} instead.
*/
@Deprecated
public SecurityMemberAccess(boolean allowStaticFieldAccess) {
this(null);
this(null, null);
useAllowStaticFieldAccess(String.valueOf(allowStaticFieldAccess));
}
@@ -223,6 +226,7 @@ public class SecurityMemberAccess implements MemberAccess {
return allowlistClasses.contains(clazz)
|| ALLOWLIST_REQUIRED_CLASSES.contains(clazz)
|| (providerAllowlist != null && providerAllowlist.getProviderAllowlist().contains(clazz))
|| (threadAllowlist != null && threadAllowlist.getAllowlist().contains(clazz))
|| isClassBelongsToPackages(clazz, ALLOWLIST_REQUIRED_PACKAGES)
|| isClassBelongsToPackages(clazz, allowlistPackageNames);
}
@@ -25,12 +25,14 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsConstants;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;
import static java.util.Arrays.asList;
import static java.util.Collections.unmodifiableSet;
import static java.util.stream.Collectors.joining;
public class DefaultAcceptedPatternsChecker implements AcceptedPatternsChecker {
private static final Logger LOG = LogManager.getLogger(DefaultAcceptedPatternsChecker.class);
@@ -39,6 +41,12 @@ public class DefaultAcceptedPatternsChecker implements AcceptedPatternsChecker {
"\\w+((\\.\\w+)|(\\[\\d+])|(\\(\\d+\\))|(\\['(\\w-?|[\\u4e00-\\u9fa5]-?)+'])|(\\('(\\w-?|[\\u4e00-\\u9fa5]-?)+'\\)))*"
};
/**
* Must match {@link #ACCEPTED_PATTERNS} RegEx. Signifies characters which result in a nested lookup via OGNL.
*/
public static final Set<Character> NESTING_CHARS = unmodifiableSet(new HashSet<>(asList('.', '[', '(')));
public static final String NESTING_CHARS_STR = NESTING_CHARS.stream().map(String::valueOf).collect(joining());
public static final String[] DMI_AWARE_ACCEPTED_PATTERNS = {
"\\w+([:]?\\w+)?((\\.\\w+)|(\\[\\d+])|(\\(\\d+\\))|(\\['(\\w-?|[\\u4e00-\\u9fa5]-?)+'])|(\\('(\\w-?|[\\u4e00-\\u9fa5]-?)+'\\)))*([!]?\\w+)?"
};
@@ -74,7 +82,7 @@ public class DefaultAcceptedPatternsChecker implements AcceptedPatternsChecker {
newAcceptedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
}
} finally {
acceptedPatterns = Collections.unmodifiableSet(newAcceptedPatterns);
acceptedPatterns = unmodifiableSet(newAcceptedPatterns);
}
}
@@ -85,7 +93,7 @@ public class DefaultAcceptedPatternsChecker implements AcceptedPatternsChecker {
@Override
public void setAcceptedPatterns(String[] additionalPatterns) {
setAcceptedPatterns(new HashSet<>(Arrays.asList(additionalPatterns)));
setAcceptedPatterns(new HashSet<>(asList(additionalPatterns)));
}
@Override
@@ -97,7 +105,7 @@ public class DefaultAcceptedPatternsChecker implements AcceptedPatternsChecker {
newAcceptedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
}
} finally {
acceptedPatterns = Collections.unmodifiableSet(newAcceptedPatterns);
acceptedPatterns = unmodifiableSet(newAcceptedPatterns);
}
}
@@ -476,6 +476,9 @@ public final class StrutsConstants {
public static final String STRUTS_ADDITIONAL_EXCLUDED_PATTERNS = "struts.additional.excludedPatterns";
public static final String STRUTS_ADDITIONAL_ACCEPTED_PATTERNS = "struts.additional.acceptedPatterns";
public static final String STRUTS_PARAMETERS_REQUIRE_ANNOTATIONS = "struts.parameters.requireAnnotations";
public static final String STRUTS_PARAMETERS_REQUIRE_ANNOTATIONS_TRANSITION = "struts.parameters.requireAnnotations.transitionMode";
public static final String STRUTS_CONTENT_TYPE_MATCHER = "struts.contentTypeMatcher";
public static final String STRUTS_SMI_METHOD_REGEX = "struts.strictMethodInvocation.methodRegex";
@@ -18,9 +18,11 @@
*/
package org.apache.struts2.components;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.ognl.ThreadAllowlist;
import org.apache.struts2.util.MakeIterator;
import org.apache.struts2.views.annotations.StrutsTag;
import org.apache.struts2.views.annotations.StrutsTagAttribute;
@@ -188,8 +190,8 @@ import java.util.List;
* <!-- START SNIPPET: example6description -->
*
* <p>Another way to create a simple loop, similar to JSTL's
* &lt;c:forEach begin="..." end="..." ...&gt; is to use some
* OGNL magic, which provides some under-the-covers magic to
* &lt;c:forEach begin="..." end="..." ...&gt; is to use some
* OGNL magic, which provides some under-the-covers magic to
* make 0-n loops trivial. This example also loops five times.</p>
*
* <!-- END SNIPPET: example6description -->
@@ -237,11 +239,17 @@ public class IteratorComponent extends ContextBean {
protected Integer end;
protected String stepStr;
protected Integer step;
private ThreadAllowlist threadAllowlist;
public IteratorComponent(ValueStack stack) {
super(stack);
}
@Inject
public void setThreadAllowlist(ThreadAllowlist threadAllowlist) {
this.threadAllowlist = threadAllowlist;
}
public boolean start(Writer writer) {
//Create an iterator status if the status attribute was set.
if (statusAttr != null) {
@@ -298,6 +306,7 @@ public class IteratorComponent extends ContextBean {
if ((iterator != null) && iterator.hasNext()) {
Object currentValue = iterator.next();
stack.push(currentValue);
threadAllowlist.allowClass(currentValue.getClass());
String var = getVar();
@@ -68,6 +68,7 @@ import org.apache.struts2.dispatcher.mapper.ActionMapper;
import org.apache.struts2.dispatcher.mapper.ActionMapping;
import org.apache.struts2.dispatcher.multipart.MultiPartRequest;
import org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper;
import org.apache.struts2.ognl.ThreadAllowlist;
import org.apache.struts2.util.ObjectFactoryDestroyable;
import org.apache.struts2.util.fs.JBossFileManager;
@@ -199,6 +200,7 @@ public class Dispatcher {
private LocaleProviderFactory localeProviderFactory;
private StaticContentLoader staticContentLoader;
private ActionMapper actionMapper;
private ThreadAllowlist threadAllowlist;
/**
* Provide the dispatcher instance for the current thread.
@@ -404,6 +406,11 @@ public class Dispatcher {
return actionMapper;
}
@Inject
public void setThreadAllowlist(ThreadAllowlist threadAllowlist) {
this.threadAllowlist = threadAllowlist;
}
/**
* Releases all instances bound to this dispatcher instance.
*/
@@ -1043,6 +1050,7 @@ public class Dispatcher {
*/
public void cleanUpRequest(HttpServletRequest request) {
ContainerHolder.clear();
threadAllowlist.clearAllowlist();
if (!(request instanceof MultiPartRequestWrapper)) {
return;
}
@@ -25,6 +25,7 @@ import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
import com.opensymphony.xwork2.interceptor.ValidationAware;
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
import com.opensymphony.xwork2.security.DefaultAcceptedPatternsChecker;
import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
import com.opensymphony.xwork2.util.ClearableValueStack;
import com.opensymphony.xwork2.util.MemberAccessValueStack;
@@ -33,6 +34,7 @@ import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.util.ValueStackFactory;
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.ClassUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsConstants;
@@ -41,17 +43,33 @@ import org.apache.struts2.action.ParameterNameAware;
import org.apache.struts2.action.ParameterValueAware;
import org.apache.struts2.dispatcher.HttpParameters;
import org.apache.struts2.dispatcher.Parameter;
import org.apache.struts2.ognl.ThreadAllowlist;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Pattern;
import static com.opensymphony.xwork2.security.DefaultAcceptedPatternsChecker.NESTING_CHARS;
import static com.opensymphony.xwork2.security.DefaultAcceptedPatternsChecker.NESTING_CHARS_STR;
import static java.util.Collections.unmodifiableSet;
import static java.util.stream.Collectors.joining;
import static org.apache.commons.lang3.StringUtils.indexOfAny;
import static org.apache.commons.lang3.StringUtils.normalizeSpace;
/**
@@ -70,8 +88,11 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
private boolean dmiEnabled = false;
protected boolean ordered = false;
protected boolean requireAnnotations = false;
protected boolean requireAnnotationsTransitionMode = false;
private ValueStackFactory valueStackFactory;
protected ThreadAllowlist threadAllowlist;
private ExcludedPatternsChecker excludedPatterns;
private AcceptedPatternsChecker acceptedPatterns;
private Set<Pattern> excludedValuePatterns = null;
@@ -82,11 +103,35 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
this.valueStackFactory = valueStackFactory;
}
@Inject
public void setThreadAllowlist(ThreadAllowlist threadAllowlist) {
this.threadAllowlist = threadAllowlist;
}
@Inject(StrutsConstants.STRUTS_DEVMODE)
public void setDevMode(String mode) {
this.devMode = BooleanUtils.toBoolean(mode);
}
@Inject(value = StrutsConstants.STRUTS_PARAMETERS_REQUIRE_ANNOTATIONS, required = false)
public void setRequireAnnotations(String requireAnnotations) {
this.requireAnnotations = BooleanUtils.toBoolean(requireAnnotations);
}
/**
* When 'Transition Mode' is enabled, parameters that are not 'nested' will be accepted without annotations. What
* this means in practice is that all public setters on an Action will be exposed for parameter injection again, and
* only 'nested' parameters, i.e. public getters on an Action, will require annotations.
* <p>
* In this mode, the OGNL auto-allowlisting capability is not degraded in any way, and as such, it offers a
* convenient option for applications to enable the OGNL allowlist capability whilst they work through the process
* of annotating all their Action parameters.
*/
@Inject(value = StrutsConstants.STRUTS_PARAMETERS_REQUIRE_ANNOTATIONS_TRANSITION, required = false)
public void setRequireAnnotationsTransitionMode(String transitionMode) {
this.requireAnnotationsTransitionMode = BooleanUtils.toBoolean(transitionMode);
}
@Inject
public void setExcludedPatterns(ExcludedPatternsChecker excludedPatterns) {
this.excludedPatterns = excludedPatterns;
@@ -295,13 +340,168 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
* @return true if parameter is accepted
*/
protected boolean isAcceptableParameter(String name, Object action) {
return acceptableName(name) && isAcceptableParameterNameAware(name, action);
return acceptableName(name) && isAcceptableParameterNameAware(name, action) && isParameterAnnotatedAndAllowlist(name, action);
}
protected boolean isAcceptableParameterNameAware(String name, Object action) {
return !(action instanceof ParameterNameAware) || ((ParameterNameAware) action).acceptableParameterName(name);
}
/**
* Checks if the Action class member corresponding to a parameter is appropriately annotated with
* {@link StrutsParameter} and OGNL allowlists any necessary classes.
* <p>
* Note that this logic relies on the use of {@link DefaultAcceptedPatternsChecker#NESTING_CHARS} and may also
* be adversely impacted by the use of custom OGNL property accessors.
*/
protected boolean isParameterAnnotatedAndAllowlist(String name, Object action) {
if (!requireAnnotations) {
return true;
}
long paramDepth = name.codePoints().mapToObj(c -> (char) c).filter(NESTING_CHARS::contains).count();
if (requireAnnotationsTransitionMode && paramDepth == 0) {
return true;
}
int nestingIndex = indexOfAny(name, NESTING_CHARS_STR);
String rootProperty = nestingIndex == -1 ? name : name.substring(0, nestingIndex);
String normalisedRootProperty = Character.toLowerCase(rootProperty.charAt(0)) + rootProperty.substring(1);
return hasValidAnnotatedMember(normalisedRootProperty, action, paramDepth);
}
/**
* Note that we check for a public field last or only if there is no valid, annotated property descriptor. This is
* because this check is likely to fail more often than not, as the relative use of public fields is low - so we
* save computation by checking this last.
*/
protected boolean hasValidAnnotatedMember(String rootProperty, Object action, long paramDepth) {
BeanInfo beanInfo = getBeanInfo(action);
if (beanInfo == null) {
return hasValidAnnotatedField(action, rootProperty, paramDepth);
}
Optional<PropertyDescriptor> propDescOpt = Arrays.stream(beanInfo.getPropertyDescriptors())
.filter(desc -> desc.getName().equals(rootProperty)).findFirst();
if (!propDescOpt.isPresent()) {
return hasValidAnnotatedField(action, rootProperty, paramDepth);
}
if (hasValidAnnotatedPropertyDescriptor(propDescOpt.get(), paramDepth)) {
return true;
}
return hasValidAnnotatedField(action, rootProperty, paramDepth);
}
protected boolean hasValidAnnotatedPropertyDescriptor(PropertyDescriptor propDesc, long paramDepth) {
Method relevantMethod = paramDepth == 0 ? propDesc.getWriteMethod() : propDesc.getReadMethod();
if (relevantMethod == null) {
return false;
}
if (getPermittedInjectionDepth(relevantMethod) < paramDepth) {
LOG.debug(
"Parameter injection for method [{}] on action [{}] rejected. Ensure it is annotated with @StrutsParameter with an appropriate 'depth'.",
relevantMethod.getName(),
relevantMethod.getDeclaringClass().getName());
return false;
}
if (paramDepth >= 1) {
allowlistClass(relevantMethod.getReturnType());
}
if (paramDepth >= 2) {
allowlistReturnTypeIfParameterized(relevantMethod);
}
return true;
}
protected void allowlistReturnTypeIfParameterized(Method method) {
allowlistParameterizedTypeArg(method.getGenericReturnType());
}
protected void allowlistParameterizedTypeArg(Type genericType) {
if (!(genericType instanceof ParameterizedType)) {
return;
}
Type[] paramTypes = ((ParameterizedType) genericType).getActualTypeArguments();
allowlistParamType(paramTypes[0]);
if (paramTypes.length > 1) {
// Probably useful for Map or Map-like classes
allowlistParamType(paramTypes[1]);
}
}
protected void allowlistParamType(Type paramType) {
if (paramType instanceof Class) {
allowlistClass((Class<?>) paramType);
}
}
protected void allowlistClass(Class<?> clazz) {
threadAllowlist.allowClass(clazz);
ClassUtils.getAllSuperclasses(clazz).forEach(threadAllowlist::allowClass);
ClassUtils.getAllInterfaces(clazz).forEach(threadAllowlist::allowClass);
}
protected boolean hasValidAnnotatedField(Object action, String fieldName, long paramDepth) {
Field field;
try {
field = action.getClass().getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
return false;
}
if (!Modifier.isPublic(field.getModifiers())) {
return false;
}
if (getPermittedInjectionDepth(field) < paramDepth) {
LOG.debug(
"Parameter injection for field [{}] on action [{}] rejected. Ensure it is annotated with @StrutsParameter with an appropriate 'depth'.",
fieldName,
action.getClass().getName());
return false;
}
if (paramDepth >= 1) {
allowlistClass(field.getType());
}
if (paramDepth >= 2) {
allowlistFieldIfParameterized(field);
}
return true;
}
protected void allowlistFieldIfParameterized(Field field) {
allowlistParameterizedTypeArg(field.getGenericType());
}
/**
* @return permitted injection depth where -1 indicates not permitted
*/
protected int getPermittedInjectionDepth(AnnotatedElement element) {
StrutsParameter annotation = getParameterAnnotation(element);
if (annotation == null) {
return -1;
}
return annotation.depth();
}
/**
* Annotation retrieval logic. Can be overridden to support extending annotations or some other form of annotation
* inheritance.
*/
protected StrutsParameter getParameterAnnotation(AnnotatedElement element) {
return element.getAnnotation(StrutsParameter.class);
}
protected BeanInfo getBeanInfo(Object action) {
try {
return Introspector.getBeanInfo(action.getClass());
} catch (IntrospectionException e) {
LOG.warn("Error introspecting Action {} for parameter injection validation", action.getClass(), e);
return null;
}
}
/**
* Checks if parameter value can be accepted or thrown away
*
@@ -0,0 +1,44 @@
/*
* 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.interceptor.parameter;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Used to annotate public getter/setter methods or fields on {@link com.opensymphony.xwork2.Action} classes that are
* intended for parameter injection by the {@link ParametersInterceptor}.
*
* @since 6.4.0
*/
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface StrutsParameter {
/**
* The depth to which parameter injection is permitted, where a depth of 0 only allows setters/fields directly on
* the action class. Setting within a POJO on an action will require a depth of 1 or more depending on the level of
* nesting within the POJO.
* <p>
* In a practical sense, the depth dictates the number of periods or brackets that can appear in the parameter name.
*/
int depth() default 0;
}
@@ -0,0 +1,51 @@
/*
* 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.ognl;
import java.util.HashSet;
import java.util.Set;
import static java.util.Collections.emptySet;
import static java.util.Collections.unmodifiableSet;
/**
* Allows any bean to allowlist a class for use in OGNL expressions, for the current thread only. The allowlist can be
* cleared once any desired OGNL expressions have been evaluated.
*
* @since 6.4.0
*/
public class ThreadAllowlist {
private final ThreadLocal<Set<Class<?>>> allowlist = new ThreadLocal<>();
public void allowClass(Class<?> clazz) {
if (allowlist.get() == null) {
allowlist.set(new HashSet<>());
}
allowlist.get().add(clazz);
}
public void clearAllowlist() {
allowlist.remove();
}
public Set<Class<?>> getAllowlist() {
return allowlist.get() != null ? unmodifiableSet(allowlist.get()) : emptySet();
}
}
@@ -22,7 +22,6 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.jsp.JspException;
import com.opensymphony.xwork2.ActionContext;
import org.apache.struts2.components.Component;
import com.opensymphony.xwork2.inject.Container;
@@ -39,8 +38,7 @@ public abstract class ComponentTagSupport extends StrutsBodyTagSupport {
public int doEndTag() throws JspException {
component.end(pageContext.getOut(), getBody());
component = null; // Always clear component reference (since clearTagStateForTagPoolingServers() is conditional).
clearTagStateForTagPoolingServers();
return EVAL_PAGE;
return super.doEndTag();
}
@Override
@@ -49,7 +47,7 @@ public abstract class ComponentTagSupport extends StrutsBodyTagSupport {
component = getBean(stack, (HttpServletRequest) pageContext.getRequest(), (HttpServletResponse) pageContext.getResponse());
Container container = stack.getActionContext().getContainer();
container.inject(component);
populateParams();
boolean evalBody = component.start(pageContext.getOut());
@@ -62,7 +60,7 @@ public abstract class ComponentTagSupport extends StrutsBodyTagSupport {
/**
* Define method to populate component state based on the Tag parameters.
*
*
* Descendants should override this method for custom behaviour, but should <em>always</em> call the ancestor method when doing so.
*/
protected void populateParams() {
@@ -71,7 +69,7 @@ public abstract class ComponentTagSupport extends StrutsBodyTagSupport {
/**
* Specialized method to populate the performClearTagStateForTagPoolingServers state of the Component to match the value set in the Tag.
*
*
* Generally only unit tests would call this method directly, to avoid calling the whole populateParams() chain again after doStartTag()
* has been called. Doing that can break tag / component state behaviour, but unit tests still need a way to set the
* performClearTagStateForTagPoolingServers state for the component (which only comes into being after doStartTag() is called).
@@ -26,23 +26,29 @@ package org.apache.struts2.views.jsp;
* <li>count: iterations so far, starts on 1. count is always index + 1</li>
* <li>first: true if index == 0</li>
* <li>even: true if (index + 1) % 2 == 0</li>
* <li>last: true if current iteration is the last iteration</li>
* <li>last: true if current iteration is the last iteration</li>
* <li>odd: true if (index + 1) % 2 == 1</li>
* </ul>
* <p>Example</p>
* <pre>
* &lt;s:iterator status="status" value='{0, 1}'&gt;
* Index: &lt;s:property value="%{#status.index}" /&gt; &lt;br /&gt;
* Count: &lt;s:property value="%{#status.count}" /&gt; &lt;br /&gt;
* Index Str: &lt;s:property value="%{#status.indexStr}" /&gt; &lt;br /&gt;
* Count: &lt;s:property value="%{#status.count}" /&gt; &lt;br /&gt;
* Count Str: &lt;s:property value="%{#status.countStr}" /&gt; &lt;br /&gt;
* &lt;/s:iterator&gt;
* </pre>
*
*
* <p>will print</p>
* <pre>
* Index: 0
* Index Str: 0
* Count: 1
* Count Str: 1
* Index: 1
* Index Str: 1
* Count: 2
* Count Str: 2
* </pre>
*/
public class IteratorStatus {
@@ -56,6 +62,10 @@ public class IteratorStatus {
return state.index + 1;
}
public String getCountStr() {
return String.valueOf(state.index + 1);
}
public boolean isEven() {
return ((state.index + 1) % 2) == 0;
}
@@ -68,6 +78,10 @@ public class IteratorStatus {
return state.index;
}
public String getIndexStr() {
return String.valueOf(state.index);
}
public boolean isLast() {
return state.last;
}
+1
View File
@@ -170,6 +170,7 @@
<bean type="org.apache.struts2.ognl.OgnlGuard" name="struts"
class="org.apache.struts2.ognl.StrutsOgnlGuard"/>
<bean class="org.apache.struts2.ognl.ProviderAllowlist"/>
<bean class="org.apache.struts2.ognl.ThreadAllowlist"/>
<bean type="com.opensymphony.xwork2.util.TextParser" name="struts"
class="com.opensymphony.xwork2.util.OgnlTextParser" scope="singleton"/>
@@ -25,6 +25,7 @@ import com.opensymphony.xwork2.util.Foo;
import ognl.MemberAccess;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.struts2.ognl.ProviderAllowlist;
import org.apache.struts2.ognl.ThreadAllowlist;
import org.junit.Before;
import org.junit.Test;
@@ -54,18 +55,21 @@ public class SecurityMemberAccessTest {
private FooBar target;
protected SecurityMemberAccess sma;
private ProviderAllowlist mockedProviderAllowlist;
private ThreadAllowlist mockedThreadAllowlist;
@Before
public void setUp() throws Exception {
context = new HashMap<>();
target = new FooBar();
mockedProviderAllowlist = mock(ProviderAllowlist.class);
mockedThreadAllowlist = mock(ThreadAllowlist.class);
assignNewSma(true);
}
protected void assignNewSma(boolean allowStaticFieldAccess) {
when(mockedProviderAllowlist.getProviderAllowlist()).thenReturn(new HashSet<>());
sma = new SecurityMemberAccess(mockedProviderAllowlist);
when(mockedThreadAllowlist.getAllowlist()).thenReturn(new HashSet<>());
sma = new SecurityMemberAccess(mockedProviderAllowlist, mockedThreadAllowlist);
sma.useAllowStaticFieldAccess(String.valueOf(allowStaticFieldAccess));
}
@@ -37,6 +37,12 @@ public class User implements UserMarker {
private String email2;
private String name;
public User() {
}
public User(String name) {
this.name = name;
}
public void setCollection(Collection collection) {
this.collection = collection;
@@ -45,6 +45,7 @@ public class TestAction extends ActionSupport {
private String result;
private User user;
private String[] array;
private Object[] objectArray;
private String[][] list;
private List list2;
private List list3;
@@ -135,6 +136,14 @@ public class TestAction extends ActionSupport {
this.array = array;
}
public Object[] getObjectArray() {
return objectArray;
}
public void setObjectArray(Object[] arrayObject) {
this.objectArray = arrayObject;
}
public String[][] getList() {
return list;
}
@@ -19,23 +19,38 @@
package org.apache.struts2.components;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.test.User;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.struts2.StrutsInternalTestCase;
import org.apache.struts2.ognl.ThreadAllowlist;
import org.apache.struts2.TestAction;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
public class IteratorComponentTest extends StrutsInternalTestCase {
private ValueStack stack;
private IteratorComponent ic;
@Override
public void setUp() throws Exception {
super.setUp();
stack = ActionContext.getContext().getValueStack();
ic = new IteratorComponent(stack);
ThreadAllowlist threadAllowlist = new ThreadAllowlist();
ic.setThreadAllowlist(threadAllowlist);
}
public void testIterator() throws Exception {
// given
final ValueStack stack = ActionContext.getContext().getValueStack();
stack.push(new FooAction());
StringWriter out = new StringWriter();
IteratorComponent ic = new IteratorComponent(stack);
ic.setValue("items");
ic.setVar("val");
@@ -62,14 +77,53 @@ public class IteratorComponentTest extends StrutsInternalTestCase {
assertEquals("item1 item2 item3 item4 ", out.getBuffer().toString());
}
public void testIteratorWithBegin() throws Exception {
public void testSimpleIterator() {
// given
stack.push(new FooAction());
StringWriter out = new StringWriter();
ic.setBegin("1");
ic.setEnd("8");
ic.setStep("2");
ic.setStatus("status");
Property prop = new Property(stack);
Property status = new Property(stack);
status.setValue("#status.index");
ic.getComponentStack().push(prop);
ic.getComponentStack().push(status);
ic.getComponentStack().push(prop);
ic.getComponentStack().push(status);
ic.getComponentStack().push(prop);
ic.getComponentStack().push(status);
ic.getComponentStack().push(prop);
ic.getComponentStack().push(status);
String body = " ";
// when
assertTrue(ic.start(out));
for (int i = 0; i < 4; i++) {
status.start(out);
status.end(out, body);
prop.start(out);
prop.end(out, body);
ic.end(out, null);
}
// then
assertEquals("0 1 1 3 2 5 3 7 ", out.getBuffer().toString());
}
public void testIteratorWithBegin() {
// given
final ValueStack stack = ActionContext.getContext().getValueStack();
stack.push(new FooAction());
StringWriter out = new StringWriter();
IteratorComponent ic = new IteratorComponent(stack);
ic.setValue("items");
ic.setVar("val");
ic.setBegin("1");
@@ -94,20 +148,18 @@ public class IteratorComponentTest extends StrutsInternalTestCase {
assertEquals("item2 item3 item4 ", out.getBuffer().toString());
}
public void testIteratorWithNulls() throws Exception {
public void testIteratorWithNulls() {
// given
final ValueStack stack = ActionContext.getContext().getValueStack();
stack.push(new FooAction() {
private List items = Arrays.asList("1", "2", null, "4");
private final List<String> items = Arrays.asList("1", "2", null, "4");
public List getItems() {
public List<String> getItems() {
return items;
}
});
StringWriter out = new StringWriter();
IteratorComponent ic = new IteratorComponent(stack);
ic.setValue("items");
ic.setVar("val");
Property prop = new Property(stack);
@@ -132,15 +184,147 @@ public class IteratorComponentTest extends StrutsInternalTestCase {
assertEquals("1, 2, , 4, ", out.getBuffer().toString());
}
public void testIteratorWithDifferentLocale() {
// given
ActionContext.getContext().withLocale(new Locale("fa_IR"));
stack.push(new FooAction());
StringWriter out = new StringWriter();
ic.setBegin("1");
ic.setEnd("3");
ic.setStatus("status");
Property prop = new Property(stack);
Property status = new Property(stack);
status.setValue("#status.count");
ic.getComponentStack().push(prop);
ic.getComponentStack().push(status);
ic.getComponentStack().push(prop);
ic.getComponentStack().push(status);
ic.getComponentStack().push(prop);
ic.getComponentStack().push(status);
String body = ",";
// when
assertTrue(ic.start(out));
for (int i = 0; i < 3; i++) {
status.start(out);
status.end(out, body);
prop.start(out);
prop.end(out, body);
ic.end(out, null);
}
// then
assertEquals("1,1,2,2,3,3,", out.getBuffer().toString());
}
public void testListOfBeansIterator() {
// given
TestAction action = new TestAction();
action.setList2(new ArrayList<User>() {{
add(new User("Anton"));
add(new User("Tym"));
add(new User("Luk"));
}});
stack.push(action);
StringWriter out = new StringWriter();
ic.setValue("list2");
ic.setStatus("status");
Property prop = new Property(stack);
prop.setValue("name");
Property status = new Property(stack);
status.setValue("#status.indexStr");
ic.getComponentStack().push(status);
ic.getComponentStack().push(prop);
ic.getComponentStack().push(status);
ic.getComponentStack().push(prop);
ic.getComponentStack().push(status);
ic.getComponentStack().push(prop);
String body = ",";
// when
assertTrue(ic.start(out));
for (int i = 0; i < 3; i++) {
status.start(out);
status.end(out, body);
prop.start(out);
prop.end(out, body);
ic.end(out, null);
}
// then
assertEquals("0,Anton,1,Tym,2,Luk,", out.getBuffer().toString());
}
public void testArrayOfBeansIterator() {
// given
TestAction action = new TestAction();
action.setObjectArray(new ArrayList<User>() {{
add(new User("Anton"));
add(new User("Tym"));
add(new User("Luk"));
}}.toArray());
stack.push(action);
StringWriter out = new StringWriter();
ic.setValue("objectArray");
ic.setStatus("status");
Property prop = new Property(stack);
prop.setValue("name");
Property status = new Property(stack);
status.setValue("#status.countStr");
ic.getComponentStack().push(status);
ic.getComponentStack().push(prop);
ic.getComponentStack().push(status);
ic.getComponentStack().push(prop);
ic.getComponentStack().push(status);
ic.getComponentStack().push(prop);
String body = " ";
// when
assertTrue(ic.start(out));
for (int i = 0; i < 3; i++) {
status.start(out);
status.end(out, body);
prop.start(out);
prop.end(out, body);
ic.end(out, null);
}
// then
assertEquals("1 Anton 2 Tym 3 Luk ", out.getBuffer().toString());
}
static class FooAction {
private List items;
private final List<String> items;
public FooAction() {
items = Arrays.asList("item1", "item2", "item3", "item4");
}
public List getItems() {
public List<String> getItems() {
return items;
}
}
@@ -0,0 +1,348 @@
/*
* 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.interceptor.parameter;
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
import com.opensymphony.xwork2.security.NotExcludedAcceptedPatternsChecker;
import org.apache.commons.lang3.ClassUtils;
import org.apache.struts2.dispatcher.HttpParameters;
import org.apache.struts2.dispatcher.Parameter;
import org.apache.struts2.ognl.ThreadAllowlist;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class StrutsParameterAnnotationTest {
private ParametersInterceptor parametersInterceptor;
private ThreadAllowlist threadAllowlist;
@Before
public void setUp() throws Exception {
parametersInterceptor = new ParametersInterceptor();
parametersInterceptor.setRequireAnnotations(Boolean.TRUE.toString());
threadAllowlist = new ThreadAllowlist();
parametersInterceptor.setThreadAllowlist(threadAllowlist);
NotExcludedAcceptedPatternsChecker checker = mock(NotExcludedAcceptedPatternsChecker.class);
when(checker.isAccepted(anyString())).thenReturn(AcceptedPatternsChecker.IsAccepted.yes(""));
when(checker.isExcluded(anyString())).thenReturn(NotExcludedAcceptedPatternsChecker.IsExcluded.no(new HashSet<>()));
parametersInterceptor.setAcceptedPatterns(checker);
parametersInterceptor.setExcludedPatterns(checker);
}
@After
public void tearDown() throws Exception {
threadAllowlist.clearAllowlist();
}
private void testParameter(Object action, String paramName, boolean shouldContain) {
Map<String, String[]> requestParamMap = new HashMap<>();
requestParamMap.put(paramName, new String[]{"value"});
HttpParameters httpParameters = HttpParameters.create(requestParamMap).build();
Map<String, Parameter> acceptedParameters = parametersInterceptor.toAcceptableParameters(httpParameters, action);
if (shouldContain) {
assertThat(acceptedParameters).containsOnlyKeys(paramName);
} else {
assertThat(acceptedParameters).isEmpty();
assertThat(threadAllowlist.getAllowlist()).isEmpty();
}
}
private Set<Class<?>> getParentClasses(Class<?> ...clazzes) {
Set<Class<?>> set = new HashSet<>();
for (Class<?> clazz : clazzes) {
set.add(clazz);
set.addAll(ClassUtils.getAllSuperclasses(clazz));
set.addAll(ClassUtils.getAllInterfaces(clazz));
}
return set;
}
@Test
public void privateStrAnnotated() {
testParameter(new FieldAction(), "privateStr", false);
}
@Test
public void publicStrAnnotated() {
testParameter(new FieldAction(), "publicStr", true);
assertThat(threadAllowlist.getAllowlist()).isEmpty();
}
@Test
public void publicStrNotAnnotated() {
testParameter(new FieldAction(), "publicStrNotAnnotated", false);
}
@Test
public void privatePojoAnnotated() {
testParameter(new FieldAction(), "privatePojo.key", false);
}
@Test
public void publicPojoDepthZero() {
testParameter(new FieldAction(), "publicPojoDepthZero.key", false);
}
@Test
public void publicPojoDepthOne() {
testParameter(new FieldAction(), "publicPojoDepthOne.key", true);
assertThat(threadAllowlist.getAllowlist()).containsExactlyInAnyOrderElementsOf(getParentClasses(Pojo.class));
}
@Test
public void publicPojoDepthOne_sqrBracket() {
testParameter(new FieldAction(), "publicPojoDepthOne['key']", true);
assertThat(threadAllowlist.getAllowlist()).containsExactlyInAnyOrderElementsOf(getParentClasses(Pojo.class));
}
@Test
public void publicPojoDepthOne_bracket() {
testParameter(new FieldAction(), "publicPojoDepthOne('key')", true);
assertThat(threadAllowlist.getAllowlist()).containsExactlyInAnyOrderElementsOf(getParentClasses(Pojo.class));
}
@Test
public void publicNestedPojoDepthOne() {
testParameter(new FieldAction(), "publicPojoDepthOne.key.key", false);
}
@Test
public void publicPojoDepthTwo() {
testParameter(new FieldAction(), "publicPojoDepthTwo.key", true);
assertThat(threadAllowlist.getAllowlist()).containsExactlyInAnyOrderElementsOf(getParentClasses(Pojo.class));
}
@Test
public void publicNestedPojoDepthTwo() {
testParameter(new FieldAction(), "publicPojoDepthTwo.key.key", true);
assertThat(threadAllowlist.getAllowlist()).containsExactlyInAnyOrderElementsOf(getParentClasses(Pojo.class));
}
@Test
public void publicNestedPojoDepthTwo_sqrBracket() {
testParameter(new FieldAction(), "publicPojoDepthTwo['key']['key']", true);
assertThat(threadAllowlist.getAllowlist()).containsExactlyInAnyOrderElementsOf(getParentClasses(Pojo.class));
}
@Test
public void publicNestedPojoDepthTwo_bracket() {
testParameter(new FieldAction(), "publicPojoDepthTwo('key')('key')", true);
assertThat(threadAllowlist.getAllowlist()).containsExactlyInAnyOrderElementsOf(getParentClasses(Pojo.class));
}
@Test
public void privateStrAnnotatedMethod() {
testParameter(new MethodAction(), "privateStr", false);
}
@Test
public void publicStrAnnotatedMethod() {
testParameter(new MethodAction(), "publicStr", true);
assertThat(threadAllowlist.getAllowlist()).isEmpty();
}
@Test
public void publicStrNotAnnotatedMethod() {
testParameter(new MethodAction(), "publicStrNotAnnotated", false);
}
@Test
public void privatePojoAnnotatedMethod() {
testParameter(new MethodAction(), "privatePojo.key", false);
}
@Test
public void publicPojoDepthZeroMethod() {
testParameter(new MethodAction(), "publicPojoDepthZero.key", false);
}
@Test
public void publicPojoDepthOneMethod() {
testParameter(new MethodAction(), "publicPojoDepthOne.key", true);
assertThat(threadAllowlist.getAllowlist()).containsExactlyInAnyOrderElementsOf(getParentClasses(Pojo.class));
}
@Test
public void publicNestedPojoDepthOneMethod() {
testParameter(new MethodAction(), "publicPojoDepthOne.key.key", false);
}
@Test
public void publicPojoDepthTwoMethod() {
testParameter(new MethodAction(), "publicPojoDepthTwo.key", true);
assertThat(threadAllowlist.getAllowlist()).containsExactlyInAnyOrderElementsOf(getParentClasses(Pojo.class));
}
@Test
public void publicNestedPojoDepthTwoMethod() {
testParameter(new MethodAction(), "publicPojoDepthTwo.key.key", true);
assertThat(threadAllowlist.getAllowlist()).containsExactlyInAnyOrderElementsOf(getParentClasses(Pojo.class));
}
@Test
public void publicPojoListDepthOne() {
testParameter(new FieldAction(), "publicPojoListDepthOne[0].key", false);
}
@Test
public void publicPojoListDepthTwo() {
testParameter(new FieldAction(), "publicPojoListDepthTwo[0].key", true);
assertThat(threadAllowlist.getAllowlist()).containsExactlyInAnyOrderElementsOf(getParentClasses(List.class, Pojo.class));
}
@Test
public void publicPojoMapDepthTwo() {
testParameter(new FieldAction(), "publicPojoMapDepthTwo['a'].key", true);
assertThat(threadAllowlist.getAllowlist()).containsExactlyInAnyOrderElementsOf(getParentClasses(Map.class, String.class, Pojo.class));
}
@Test
public void publicPojoListDepthOneMethod() {
testParameter(new MethodAction(), "publicPojoListDepthOne[0].key", false);
}
@Test
public void publicPojoListDepthTwoMethod() {
testParameter(new MethodAction(), "publicPojoListDepthTwo[0].key", true);
assertThat(threadAllowlist.getAllowlist()).containsExactlyInAnyOrderElementsOf(getParentClasses(List.class, Pojo.class));
}
@Test
public void publicPojoMapDepthTwoMethod() {
testParameter(new MethodAction(), "publicPojoMapDepthTwo['a'].key", true);
assertThat(threadAllowlist.getAllowlist()).containsExactlyInAnyOrderElementsOf(getParentClasses(Map.class, String.class, Pojo.class));
}
@Test
public void publicStrNotAnnotated_transitionMode() {
parametersInterceptor.setRequireAnnotationsTransitionMode(Boolean.TRUE.toString());
testParameter(new FieldAction(), "publicStrNotAnnotated", true);
}
@Test
public void publicStrNotAnnotatedMethod_transitionMode() {
parametersInterceptor.setRequireAnnotationsTransitionMode(Boolean.TRUE.toString());
testParameter(new MethodAction(), "publicStrNotAnnotated", true);
}
class FieldAction {
@StrutsParameter
private String privateStr;
@StrutsParameter
public String publicStr;
public String publicStrNotAnnotated;
@StrutsParameter(depth = 1)
private Pojo privatePojo;
@StrutsParameter
public Pojo publicPojoDepthZero;
@StrutsParameter(depth = 1)
public Pojo publicPojoDepthOne ;
@StrutsParameter(depth = 2)
public Pojo publicPojoDepthTwo;
@StrutsParameter(depth = 1)
public List<Pojo> publicPojoListDepthOne;
@StrutsParameter(depth = 2)
public List<Pojo> publicPojoListDepthTwo;
@StrutsParameter(depth = 2)
public Map<String, Pojo> publicPojoMapDepthTwo;
}
class MethodAction {
@StrutsParameter
private void setPrivateStr(String str) {
}
@StrutsParameter
public void setPublicStr(String str) {
}
public void setPublicStrNotAnnotated(String str) {
}
@StrutsParameter(depth = 1)
private Pojo getPrivatePojo() {
return null;
}
@StrutsParameter
public Pojo getPublicPojoDepthZero() {
return null;
}
@StrutsParameter
public void setPublicPojoDepthZero() {
}
@StrutsParameter(depth = 1)
public Pojo getPublicPojoDepthOne() {
return null;
}
@StrutsParameter(depth = 2)
public Pojo getPublicPojoDepthTwo() {
return null;
}
@StrutsParameter(depth = 1)
public List<Pojo> getPublicPojoListDepthOne() {
return null;
}
@StrutsParameter(depth = 2)
public List<Pojo> getPublicPojoListDepthTwo() {
return null;
}
@StrutsParameter(depth = 2)
public Map<String, Pojo> getPublicPojoMapDepthTwo() {
return null;
}
}
class Pojo {
}
}
@@ -18,6 +18,12 @@
*/
package org.apache.struts2.views.jsp;
import jakarta.servlet.jsp.JspException;
import jakarta.servlet.jsp.tagext.TagSupport;
import org.apache.commons.collections.ListUtils;
import org.springframework.mock.web.MockBodyContent;
import org.springframework.mock.web.MockJspWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
@@ -25,27 +31,19 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.commons.collections.ListUtils;
import org.springframework.mock.web.MockBodyContent;
import org.springframework.mock.web.MockJspWriter;
import jakarta.servlet.jsp.JspException;
import jakarta.servlet.jsp.tagext.TagSupport;
/**
* Test Case for Iterator Tag
*
*/
public class IteratorTagTest extends AbstractUITagTest {
IteratorTag tag;
private IteratorTag tag;
public void testIteratingWithIdSpecified() throws Exception {
List list = new ArrayList();
List<String> list = new ArrayList<>();
list.add("one");
list.add("two");
list.add("three");
@@ -107,12 +105,12 @@ public class IteratorTagTest extends AbstractUITagTest {
IteratorTag freshTag = new IteratorTag();
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testIteratingWithIdSpecified_clearTagStateSet() throws Exception {
List list = new ArrayList();
List<String> list = new ArrayList<>();
list.add("one");
list.add("two");
list.add("three");
@@ -177,12 +175,12 @@ public class IteratorTagTest extends AbstractUITagTest {
freshTag.setPerformClearTagStateForTagPoolingServers(true);
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testIteratingWithIdSpecifiedAndNullElementOnCollection() throws Exception {
List list = new ArrayList();
List<String> list = new ArrayList<>();
list.add("one");
list.add(null);
list.add("three");
@@ -227,12 +225,12 @@ public class IteratorTagTest extends AbstractUITagTest {
IteratorTag freshTag = new IteratorTag();
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testIteratingWithIdSpecifiedAndNullElementOnCollection_clearTagStateSet() throws Exception {
List list = new ArrayList();
List<String> list = new ArrayList<>();
list.add("one");
list.add(null);
list.add("three");
@@ -280,7 +278,7 @@ public class IteratorTagTest extends AbstractUITagTest {
freshTag.setPerformClearTagStateForTagPoolingServers(true);
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
@@ -297,7 +295,7 @@ public class IteratorTagTest extends AbstractUITagTest {
public void testCollectionIterator() {
Foo foo = new Foo();
ArrayList list = new ArrayList();
List<String> list = new ArrayList<>();
list.add("test1");
list.add("test2");
list.add("test3");
@@ -317,7 +315,7 @@ public class IteratorTagTest extends AbstractUITagTest {
public void testMapIterator() {
Foo foo = new Foo();
HashMap map = new HashMap();
HashMap<String, String> map = new HashMap<>();
map.put("test1", "123");
map.put("test2", "456");
map.put("test3", "789");
@@ -332,8 +330,7 @@ public class IteratorTagTest extends AbstractUITagTest {
try {
result = tag.doStartTag();
} catch (JspException e) {
e.printStackTrace();
fail();
fail(e.getMessage());
}
assertEquals(TagSupport.EVAL_BODY_INCLUDE, result);
@@ -343,8 +340,7 @@ public class IteratorTagTest extends AbstractUITagTest {
try {
result = tag.doAfterBody();
} catch (JspException e) {
e.printStackTrace();
fail();
fail(e.getMessage());
}
assertEquals(TagSupport.EVAL_BODY_AGAIN, result);
@@ -354,8 +350,7 @@ public class IteratorTagTest extends AbstractUITagTest {
try {
result = tag.doAfterBody();
} catch (JspException e) {
e.printStackTrace();
fail();
fail(e.getMessage());
}
assertEquals(TagSupport.EVAL_BODY_AGAIN, result);
@@ -365,8 +360,7 @@ public class IteratorTagTest extends AbstractUITagTest {
try {
result = tag.doAfterBody();
} catch (JspException e) {
e.printStackTrace();
fail();
fail(e.getMessage());
}
assertEquals(TagSupport.SKIP_BODY, result);
@@ -375,8 +369,7 @@ public class IteratorTagTest extends AbstractUITagTest {
try {
result = tag.doEndTag();
} catch (JspException e) {
e.printStackTrace();
fail();
fail(e.getMessage());
}
assertEquals(TagSupport.EVAL_PAGE, result);
@@ -385,13 +378,13 @@ public class IteratorTagTest extends AbstractUITagTest {
IteratorTag freshTag = new IteratorTag();
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testMapIterator_clearTagStateSet() {
Foo foo = new Foo();
HashMap map = new HashMap();
HashMap<String, String> map = new HashMap<>();
map.put("test1", "123");
map.put("test2", "456");
map.put("test3", "789");
@@ -408,8 +401,7 @@ public class IteratorTagTest extends AbstractUITagTest {
result = tag.doStartTag();
setComponentTagClearTagState(tag, true); // Ensure component tag state clearing is set true (to match tag).
} catch (JspException e) {
e.printStackTrace();
fail();
fail(e.getMessage());
}
assertEquals(TagSupport.EVAL_BODY_INCLUDE, result);
@@ -419,8 +411,7 @@ public class IteratorTagTest extends AbstractUITagTest {
try {
result = tag.doAfterBody();
} catch (JspException e) {
e.printStackTrace();
fail();
fail(e.getMessage());
}
assertEquals(TagSupport.EVAL_BODY_AGAIN, result);
@@ -430,8 +421,7 @@ public class IteratorTagTest extends AbstractUITagTest {
try {
result = tag.doAfterBody();
} catch (JspException e) {
e.printStackTrace();
fail();
fail(e.getMessage());
}
assertEquals(TagSupport.EVAL_BODY_AGAIN, result);
@@ -441,8 +431,7 @@ public class IteratorTagTest extends AbstractUITagTest {
try {
result = tag.doAfterBody();
} catch (JspException e) {
e.printStackTrace();
fail();
fail(e.getMessage());
}
assertEquals(TagSupport.SKIP_BODY, result);
@@ -451,8 +440,7 @@ public class IteratorTagTest extends AbstractUITagTest {
try {
result = tag.doEndTag();
} catch (JspException e) {
e.printStackTrace();
fail();
fail(e.getMessage());
}
assertEquals(TagSupport.EVAL_PAGE, result);
@@ -462,7 +450,7 @@ public class IteratorTagTest extends AbstractUITagTest {
freshTag.setPerformClearTagStateForTagPoolingServers(true);
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
@@ -480,8 +468,7 @@ public class IteratorTagTest extends AbstractUITagTest {
try {
result = tag.doStartTag();
} catch (JspException e) {
e.printStackTrace();
fail();
fail(e.getMessage());
}
assertEquals(TagSupport.EVAL_BODY_INCLUDE, result);
@@ -493,15 +480,16 @@ public class IteratorTagTest extends AbstractUITagTest {
assertFalse(status.isLast());
assertTrue(status.isFirst());
assertEquals(0, status.getIndex());
assertEquals("0", status.getIndexStr());
assertEquals(1, status.getCount());
assertEquals("1", status.getCountStr());
assertTrue(status.isOdd());
assertFalse(status.isEven());
try {
result = tag.doAfterBody();
} catch (JspException e) {
e.printStackTrace();
fail();
fail(e.getMessage());
}
assertEquals(TagSupport.EVAL_BODY_AGAIN, result);
@@ -520,8 +508,7 @@ public class IteratorTagTest extends AbstractUITagTest {
try {
result = tag.doAfterBody();
} catch (JspException e) {
e.printStackTrace();
fail();
fail(e.getMessage());
}
assertEquals(TagSupport.EVAL_BODY_AGAIN, result);
@@ -540,8 +527,7 @@ public class IteratorTagTest extends AbstractUITagTest {
try {
result = tag.doEndTag();
} catch (JspException e) {
e.printStackTrace();
fail();
fail(e.getMessage());
}
assertEquals(TagSupport.EVAL_PAGE, result);
@@ -550,7 +536,7 @@ public class IteratorTagTest extends AbstractUITagTest {
IteratorTag freshTag = new IteratorTag();
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
@@ -570,8 +556,7 @@ public class IteratorTagTest extends AbstractUITagTest {
result = tag.doStartTag();
setComponentTagClearTagState(tag, true); // Ensure component tag state clearing is set true (to match tag).
} catch (JspException e) {
e.printStackTrace();
fail();
fail(e.getMessage());
}
assertEquals(TagSupport.EVAL_BODY_INCLUDE, result);
@@ -590,8 +575,7 @@ public class IteratorTagTest extends AbstractUITagTest {
try {
result = tag.doAfterBody();
} catch (JspException e) {
e.printStackTrace();
fail();
fail(e.getMessage());
}
assertEquals(TagSupport.EVAL_BODY_AGAIN, result);
@@ -610,8 +594,7 @@ public class IteratorTagTest extends AbstractUITagTest {
try {
result = tag.doAfterBody();
} catch (JspException e) {
e.printStackTrace();
fail();
fail(e.getMessage());
}
assertEquals(TagSupport.EVAL_BODY_AGAIN, result);
@@ -630,8 +613,7 @@ public class IteratorTagTest extends AbstractUITagTest {
try {
result = tag.doEndTag();
} catch (JspException e) {
e.printStackTrace();
fail();
fail(e.getMessage());
}
assertEquals(TagSupport.EVAL_PAGE, result);
@@ -641,7 +623,7 @@ public class IteratorTagTest extends AbstractUITagTest {
freshTag.setPerformClearTagStateForTagPoolingServers(true);
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
@@ -669,7 +651,7 @@ public class IteratorTagTest extends AbstractUITagTest {
public void testEmptyCollection() {
Foo foo = new Foo();
foo.setList(new ArrayList());
foo.setList(new ArrayList<>());
stack.push(foo);
@@ -695,17 +677,41 @@ public class IteratorTagTest extends AbstractUITagTest {
validateCounter(new Integer[]{0, 1, 2, 3, 4, 5});
}
public void testCounterWithStackValues() throws JspException {
public void testCounterWithDifferentLocale() throws JspException {
stack.getActionContext().withLocale(new Locale("fa_IR"));
tag.setVar("it");
tag.setBegin("0");
tag.setEnd("5");
List<String> expectedValues = Arrays.asList("0", "1", "2", "3", "4", "5");
ArrayList<String> values = new ArrayList<>();
try {
int result = tag.doStartTag();
assertEquals(TagSupport.EVAL_BODY_INCLUDE, result);
values.add((String) stack.findValue("it", String.class));
} catch (JspException e) {
fail(e.getMessage());
}
while (tag.doAfterBody() == TagSupport.EVAL_BODY_AGAIN) {
values.add((String) stack.findValue("top", String.class));
}
assertEquals(expectedValues.size(), values.size());
assertEquals(expectedValues, values);
}
public void testCounterWithStackValues() throws JspException {
stack.getContext().put("begin", 0);
stack.getContext().put("end", 5);
tag.setBegin("%{#begin}");
tag.setEnd("%{#end}");
tag.setBegin("begin");
tag.setEnd("end");
validateCounter(new Integer[]{0, 1, 2, 3, 4, 5});
}
public void testCounterWithList() throws JspException {
Foo foo = new Foo();
ArrayList list = new ArrayList();
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
@@ -723,7 +729,6 @@ public class IteratorTagTest extends AbstractUITagTest {
public void testCounterWithArray() throws JspException {
Foo foo = new Foo();
ArrayList list = new ArrayList();
foo.setArray(new String[]{"a", "b", "c", "d"});
stack.push(foo);
@@ -738,7 +743,7 @@ public class IteratorTagTest extends AbstractUITagTest {
public void testCounterWithListNoEnd() throws JspException {
Foo foo = new Foo();
ArrayList list = new ArrayList();
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
@@ -755,7 +760,6 @@ public class IteratorTagTest extends AbstractUITagTest {
public void testCounterWithArrayNoEnd() throws JspException {
Foo foo = new Foo();
ArrayList list = new ArrayList();
foo.setArray(new String[]{"a", "b", "c", "d"});
stack.push(foo);
@@ -768,7 +772,7 @@ public class IteratorTagTest extends AbstractUITagTest {
public void testCounterWithList2() throws JspException {
Foo foo = new Foo();
ArrayList list = new ArrayList();
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
@@ -786,7 +790,6 @@ public class IteratorTagTest extends AbstractUITagTest {
public void testCounterWithArray2() throws JspException {
Foo foo = new Foo();
ArrayList list = new ArrayList();
foo.setArray(new String[]{"a", "b", "c", "d"});
stack.push(foo);
@@ -800,7 +803,7 @@ public class IteratorTagTest extends AbstractUITagTest {
public void testCounterWithListNoEnd2() throws JspException {
Foo foo = new Foo();
ArrayList list = new ArrayList();
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
@@ -815,9 +818,8 @@ public class IteratorTagTest extends AbstractUITagTest {
validateCounter(new String[]{"c", "d"});
}
public void testCounterWithArrayNoEnd2() throws JspException {
public void testCounterWithArrayNoEnd2() throws JspException {
Foo foo = new Foo();
ArrayList list = new ArrayList();
foo.setArray(new String[]{"a", "b", "c", "d"});
stack.push(foo);
@@ -841,9 +843,9 @@ public class IteratorTagTest extends AbstractUITagTest {
validateCounter(new Integer[]{0, 2, 4});
}
public void testCounterWithListAndStep() throws JspException {
public void testCounterWithListAndStep() throws JspException {
Foo foo = new Foo();
ArrayList list = new ArrayList();
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
@@ -861,9 +863,8 @@ public class IteratorTagTest extends AbstractUITagTest {
validateCounter(new String[]{"a", "c"});
}
public void testCounterWithArrayAndStep() throws JspException {
public void testCounterWithArrayAndStep() throws JspException {
Foo foo = new Foo();
ArrayList list = new ArrayList();
foo.setArray(new String[]{"a", "b", "c", "d"});
stack.push(foo);
@@ -879,7 +880,7 @@ public class IteratorTagTest extends AbstractUITagTest {
public void testCounterWithListAndStepNoEnd() throws JspException {
Foo foo = new Foo();
ArrayList list = new ArrayList();
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
@@ -898,7 +899,6 @@ public class IteratorTagTest extends AbstractUITagTest {
public void testCounterWithArrayAndStepNoEnd() throws JspException {
Foo foo = new Foo();
ArrayList list = new ArrayList();
foo.setArray(new String[]{"a", "b", "c", "d"});
stack.push(foo);
@@ -920,7 +920,7 @@ public class IteratorTagTest extends AbstractUITagTest {
public void testCounterWithListAndNegativeStep() throws JspException {
Foo foo = new Foo();
ArrayList list = new ArrayList();
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
@@ -940,7 +940,7 @@ public class IteratorTagTest extends AbstractUITagTest {
public void testCounterWithListAndNegativeStepNoEnd() throws JspException {
Foo foo = new Foo();
ArrayList list = new ArrayList();
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
@@ -957,9 +957,9 @@ public class IteratorTagTest extends AbstractUITagTest {
validateCounter(new String[]{"d", "c", "b", "a"});
}
public void testCounterWithArrayAndNegativeStep() throws JspException {
public void testCounterWithArrayAndNegativeStep() throws JspException {
Foo foo = new Foo();
ArrayList list = new ArrayList();
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
@@ -979,7 +979,7 @@ public class IteratorTagTest extends AbstractUITagTest {
public void testCounterWithArrayAndNegativeStepNoEnd() throws JspException {
Foo foo = new Foo();
ArrayList list = new ArrayList();
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
@@ -997,14 +997,13 @@ public class IteratorTagTest extends AbstractUITagTest {
}
protected void validateCounter(Object[] expectedValues) throws JspException {
List values = new ArrayList();
ArrayList<Object> values = new ArrayList<>();
try {
int result = tag.doStartTag();
assertEquals(TagSupport.EVAL_BODY_INCLUDE, result);
values.add(stack.getRoot().peek());
} catch (JspException e) {
e.printStackTrace();
fail();
fail(e.getMessage());
}
while (tag.doAfterBody() == TagSupport.EVAL_BODY_AGAIN) {
@@ -1012,7 +1011,7 @@ public class IteratorTagTest extends AbstractUITagTest {
}
assertEquals(expectedValues.length, values.size());
ListUtils.isEqualList(Arrays.asList(expectedValues), values);
assertTrue(ListUtils.isEqualList(Arrays.asList(expectedValues), values));
}
@Override
@@ -1035,8 +1034,7 @@ public class IteratorTagTest extends AbstractUITagTest {
try {
result = tag.doStartTag();
} catch (JspException e) {
e.printStackTrace();
fail();
fail(e.getMessage());
}
assertEquals(TagSupport.EVAL_BODY_INCLUDE, result);
@@ -1046,8 +1044,7 @@ public class IteratorTagTest extends AbstractUITagTest {
try {
result = tag.doAfterBody();
} catch (JspException e) {
e.printStackTrace();
fail();
fail(e.getMessage());
}
assertEquals(TagSupport.EVAL_BODY_AGAIN, result);
@@ -1057,8 +1054,7 @@ public class IteratorTagTest extends AbstractUITagTest {
try {
result = tag.doAfterBody();
} catch (JspException e) {
e.printStackTrace();
fail();
fail(e.getMessage());
}
assertEquals(TagSupport.EVAL_BODY_AGAIN, result);
@@ -1068,8 +1064,7 @@ public class IteratorTagTest extends AbstractUITagTest {
try {
result = tag.doAfterBody();
} catch (JspException e) {
e.printStackTrace();
fail();
fail(e.getMessage());
}
assertEquals(TagSupport.SKIP_BODY, result);
@@ -1082,16 +1077,14 @@ public class IteratorTagTest extends AbstractUITagTest {
try {
result = tag.doStartTag();
} catch (JspException e) {
e.printStackTrace();
fail();
fail(e.getMessage());
}
assertEquals(TagSupport.SKIP_BODY, result);
try {
result = tag.doEndTag();
} catch (JspException e) {
e.printStackTrace();
fail();
fail(e.getMessage());
}
assertEquals(TagSupport.EVAL_PAGE, result);
@@ -1100,13 +1093,13 @@ public class IteratorTagTest extends AbstractUITagTest {
IteratorTag freshTag = new IteratorTag();
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
class Foo {
private Collection list;
private Map map;
static class Foo {
private Collection<String> list;
private Map<String, String> map;
private String[] array;
public void setArray(String[] array) {
@@ -1117,19 +1110,19 @@ public class IteratorTagTest extends AbstractUITagTest {
return array;
}
public void setList(Collection list) {
public void setList(Collection<String> list) {
this.list = list;
}
public Collection getList() {
public Collection<String> getList() {
return list;
}
public void setMap(Map map) {
public void setMap(Map<String, String> map) {
this.map = map;
}
public Map getMap() {
public Map<String, String> getMap() {
return map;
}
}
+8 -13
View File
@@ -132,21 +132,16 @@
<notes><![CDATA[ file name: plexus-utils-1.2.jar]]></notes>
<packageUrl regex="true">^pkg:maven/org\.codehaus\.plexus/plexus\-utils@.*$</packageUrl>
<cpe>cpe:/a:plexus-utils_project:plexus-utils</cpe>
<cve>CVE-2022-4244</cve>
<cve>CVE-2022-4245</cve>
<cve>CVE-2017-1000487</cve>
</suppress>
<suppress>
<notes><![CDATA[file name: plexus-utils-1.2.jar]]></notes>
<packageUrl regex="true">^pkg:maven/org\.codehaus\.plexus/plexus\-utils@.*$</packageUrl>
<vulnerabilityName>CVE-2017-1000487</vulnerabilityName>
</suppress>
<suppress>
<notes><![CDATA[file name: plexus-utils-1.2.jar]]></notes>
<packageUrl regex="true">^pkg:maven/org\.codehaus\.plexus/plexus\-utils@.*$</packageUrl>
<vulnerabilityName>Directory traversal in org.codehaus.plexus.util.Expand</vulnerabilityName>
</suppress>
<suppress>
<notes><![CDATA[file name: plexus-utils-1.2.jar]]></notes>
<packageUrl regex="true">^pkg:maven/org\.codehaus\.plexus/plexus\-utils@.*$</packageUrl>
<vulnerabilityName>Possible XML Injection</vulnerabilityName>
<notes><![CDATA[ file name: plexus-container-default-1.0-alpha-10.jar]]></notes>
<packageUrl regex="true">^pkg:maven/org\.codehaus\.plexus\/plexus\-container\-default@.*$</packageUrl>
<cpe>cpe:/a:plexus-utils_project:plexus-utils</cpe>
<cve>CVE-2022-4244</cve>
<cve>CVE-2022-4245</cve>
</suppress>
<!-- TestNG -->
<suppress>