This is an automated email from the ASF dual-hosted git repository. lukaszlenart pushed a commit to branch merge-master-to-7xx-2024-02-01 in repository https://gitbox.apache.org/repos/asf/struts.git
commit 11d84a26c3c08078a1e28fa96359a7693edd79bc Merge: 84d350dac eb469779f Author: Lukasz Lenart <lukaszlen...@apache.org> AuthorDate: Thu Feb 1 09:55:34 2024 +0100 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 .github/workflows/scorecards-analysis.yaml | 2 +- .github/workflows/sonar.yml | 1 + Jenkinsfile | 10 +- SECURITY.md | 12 +- .../org/apache/struts2/showcase/UITagExample.java | 24 +- .../showcase/action/ParamsAnnotationAction.java | 133 ++++++++ .../struts2/showcase/action/SkillAction.java | 2 + .../struts2/showcase/async/ChatRoomAction.java | 3 + .../struts2/showcase/conversion/AddressAction.java | 4 +- .../showcase/conversion/OperationsEnumAction.java | 2 + .../struts2/showcase/conversion/PersonAction.java | 2 + .../showcase/filedownload/FileDownloadAction.java | 2 + .../showcase/fileupload/FileUploadAction.java | 2 + .../PersonAction.java => model/MyDto.java} | 35 +-- .../validation/FieldValidatorsExampleAction.java | 11 + .../struts2/showcase/wait/LongProcessAction.java | 2 + .../struts-params-annotation.xml} | 43 +-- apps/showcase/src/main/resources/struts.xml | 14 +- .../src/main/webapp/WEB-INF/paramsannotation.vm | 19 ++ .../struts2/showcase/StrutsParametersTest.java | 239 ++++++++++++++ .../xwork2/config/impl/DefaultConfiguration.java | 2 + .../xwork2/ognl/SecurityMemberAccess.java | 10 +- .../security/DefaultAcceptedPatternsChecker.java | 18 +- .../java/org/apache/struts2/StrutsConstants.java | 3 + .../struts2/components/IteratorComponent.java | 13 +- .../org/apache/struts2/dispatcher/Dispatcher.java | 8 + .../parameter/ParametersInterceptor.java | 204 +++++++++++- .../interceptor/parameter/StrutsParameter.java | 44 +++ .../org/apache/struts2/ognl/ThreadAllowlist.java | 51 +++ .../struts2/views/jsp/ComponentTagSupport.java | 10 +- .../apache/struts2/views/jsp/IteratorStatus.java | 20 +- core/src/main/resources/struts-beans.xml | 1 + .../xwork2/ognl/SecurityMemberAccessTest.java | 6 +- .../java/com/opensymphony/xwork2/test/User.java | 6 + .../test/java/org/apache/struts2/TestAction.java | 9 + .../struts2/components/IteratorComponentTest.java | 208 +++++++++++- .../parameter/StrutsParameterAnnotationTest.java | 348 +++++++++++++++++++++ .../apache/struts2/views/jsp/IteratorTagTest.java | 211 ++++++------- src/etc/project-suppression.xml | 21 +- 39 files changed, 1522 insertions(+), 233 deletions(-) diff --cc Jenkinsfile index ca830acf2,1d8aa072a..45cd77441 --- a/Jenkinsfile +++ b/Jenkinsfile @@@ -117,13 -191,10 +117,13 @@@ pipeline } stage('Upload nightlies') { when { - branch 'master' + anyOf { + branch 'master' + branch 'release/struts-7-0-x' + } } steps { - sh './mvnw -B package -DskipTests' + sh './mvnw -B package -DskipTests --no-transfer-progress' sshPublisher(publishers: [ sshPublisherDesc( configName: 'Nightlies', diff --cc apps/showcase/src/main/java/org/apache/struts2/showcase/fileupload/FileUploadAction.java index be6f38669,279c1e928..a1381c08c --- a/apps/showcase/src/main/java/org/apache/struts2/showcase/fileupload/FileUploadAction.java +++ b/apps/showcase/src/main/java/org/apache/struts2/showcase/fileupload/FileUploadAction.java @@@ -23,8 -23,8 +23,9 @@@ package org.apache.struts2.showcase.fil 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; /** diff --cc apps/showcase/src/test/java/it/org/apache/struts2/showcase/StrutsParametersTest.java index 000000000,417909544..927f0d101 mode 000000,100644..100644 --- a/apps/showcase/src/test/java/it/org/apache/struts2/showcase/StrutsParametersTest.java +++ b/apps/showcase/src/test/java/it/org/apache/struts2/showcase/StrutsParametersTest.java @@@ -1,0 -1,239 +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 com.gargoylesoftware.htmlunit.WebClient; -import com.gargoylesoftware.htmlunit.html.HtmlPage; ++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); + } + } diff --cc core/src/main/java/org/apache/struts2/views/jsp/ComponentTagSupport.java index 7ef4eed1e,c2208982c..e090daf77 --- a/core/src/main/java/org/apache/struts2/views/jsp/ComponentTagSupport.java +++ b/core/src/main/java/org/apache/struts2/views/jsp/ComponentTagSupport.java @@@ -18,15 -18,13 +18,14 @@@ */ package org.apache.struts2.views.jsp; -import com.opensymphony.xwork2.inject.Container; -import com.opensymphony.xwork2.util.ValueStack; +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 javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.jsp.JspException; +import com.opensymphony.xwork2.inject.Container; +import com.opensymphony.xwork2.util.ValueStack; /** */ @@@ -62,7 -59,7 +60,7 @@@ public abstract class ComponentTagSuppo /** * Define method to populate component state based on the Tag parameters. - * - * <p> ++ * * 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 -68,7 +69,7 @@@ /** * Specialized method to populate the performClearTagStateForTagPoolingServers state of the Component to match the value set in the Tag. - * - * <p> ++ * * 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). diff --cc core/src/test/java/org/apache/struts2/views/jsp/IteratorTagTest.java index ad3f68745,fd3fc9587..3c355bf1d --- a/core/src/test/java/org/apache/struts2/views/jsp/IteratorTagTest.java +++ b/core/src/test/java/org/apache/struts2/views/jsp/IteratorTagTest.java @@@ -18,34 -18,27 +18,32 @@@ */ package org.apache.struts2.views.jsp; -import com.mockobjects.servlet.MockBodyContent; -import com.mockobjects.servlet.MockJspWriter; -import com.opensymphony.xwork2.ActionContext; ++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 javax.servlet.jsp.JspException; -import javax.servlet.jsp.tagext.TagSupport; +import java.io.StringWriter; +import java.io.Writer; import java.util.ArrayList; 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"); diff --cc src/etc/project-suppression.xml index 38324163d,7b2a1c5fb..b0d5ebc95 --- a/src/etc/project-suppression.xml +++ b/src/etc/project-suppression.xml @@@ -132,22 -132,47 +132,17 @@@ <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> - <suppress> - <notes><![CDATA[file name: oval-1.90.jar]]></notes> - <packageUrl regex="true">^pkg:maven/net\.sf\.oval/oval@.*$</packageUrl> - <cpe>cpe:/a:apache:groovy</cpe> - </suppress> - <suppress> - <notes><![CDATA[file name: oval-1.90.jar]]></notes> - <packageUrl regex="true">^pkg:maven/net\.sf\.oval/oval@.*$</packageUrl> - <cpe>cpe:/a:apache:log4j</cpe> - </suppress> - <suppress> - <notes><![CDATA[file name: oval-1.90.jar]]></notes> - <packageUrl regex="true">^pkg:maven/net\.sf\.oval/oval@.*$</packageUrl> - <cpe>cpe:/a:jruby:jruby</cpe> - </suppress> - <suppress> - <notes><![CDATA[file name: oval-1.90.jar]]></notes> - <packageUrl regex="true">^pkg:maven/net\.sf\.oval/oval@.*$</packageUrl> - <cpe>cpe:/a:xstream_project:xstream</cpe> - </suppress> - <suppress> - <notes><![CDATA[file name: oval-1.90.jar: xstream-1.4.19.jar]]></notes> - <packageUrl regex="true">^pkg:maven/com\.thoughtworks\.xstream/xstream@.*$</packageUrl> - <cve>CVE-2022-40151</cve> - <cve>CVE-2022-40152</cve> - <cve>CVE-2022-40153</cve> - <cve>CVE-2022-40154</cve> - <cve>CVE-2022-40155</cve> - <cve>CVE-2022-40156</cve> - </suppress> <!-- TestNG --> <suppress> <notes><![CDATA[file name: guava-19.0.jar]]></notes>