From b50616942b81de3b0d1c02b77787134b6fccee2e Mon Sep 17 00:00:00 2001 From: Kusal Kithul-Godage Date: Thu, 18 Jan 2024 19:54:54 +1100 Subject: [PATCH] WW-5352 Acceptance test coverage --- .../action/ParamsAnnotationAction.java | 133 ++++++++++ .../apache/struts2/showcase/model/MyDto.java | 38 +++ .../resources/struts-params-annotation.xml | 32 +++ apps/showcase/src/main/resources/struts.xml | 2 + .../main/webapp/WEB-INF/paramsannotation.vm | 19 ++ .../showcase/StrutsParametersTest.java | 239 ++++++++++++++++++ 6 files changed, 463 insertions(+) create mode 100644 apps/showcase/src/main/java/org/apache/struts2/showcase/action/ParamsAnnotationAction.java create mode 100644 apps/showcase/src/main/java/org/apache/struts2/showcase/model/MyDto.java create mode 100644 apps/showcase/src/main/resources/struts-params-annotation.xml create mode 100644 apps/showcase/src/main/webapp/WEB-INF/paramsannotation.vm create mode 100644 apps/showcase/src/test/java/it/org/apache/struts2/showcase/StrutsParametersTest.java diff --git a/apps/showcase/src/main/java/org/apache/struts2/showcase/action/ParamsAnnotationAction.java b/apps/showcase/src/main/java/org/apache/struts2/showcase/action/ParamsAnnotationAction.java new file mode 100644 index 000000000..0c3ff4f7c --- /dev/null +++ b/apps/showcase/src/main/java/org/apache/struts2/showcase/action/ParamsAnnotationAction.java @@ -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 publicList = new ArrayList<>(singletonList("no")); + + @StrutsParameter(depth = 1) + public List publicListAnnotated = new ArrayList<>(singletonList("no")); + + private List privateList = new ArrayList<>(singletonList("no")); + + public Map publicMap = new HashMap<>(singletonMap("key", "no")); + + @StrutsParameter(depth = 1) + public Map 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 getPrivateListMethod() { + return privateList; + } + + @StrutsParameter(depth = 1) + public List 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"; + } + } +} diff --git a/apps/showcase/src/main/java/org/apache/struts2/showcase/model/MyDto.java b/apps/showcase/src/main/java/org/apache/struts2/showcase/model/MyDto.java new file mode 100644 index 000000000..7abee847e --- /dev/null +++ b/apps/showcase/src/main/java/org/apache/struts2/showcase/model/MyDto.java @@ -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 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); + } +} diff --git a/apps/showcase/src/main/resources/struts-params-annotation.xml b/apps/showcase/src/main/resources/struts-params-annotation.xml new file mode 100644 index 000000000..db3992884 --- /dev/null +++ b/apps/showcase/src/main/resources/struts-params-annotation.xml @@ -0,0 +1,32 @@ + + + + + + + + /WEB-INF/paramsannotation.vm + + + diff --git a/apps/showcase/src/main/resources/struts.xml b/apps/showcase/src/main/resources/struts.xml index 373cf5f7b..33095326c 100644 --- a/apps/showcase/src/main/resources/struts.xml +++ b/apps/showcase/src/main/resources/struts.xml @@ -83,6 +83,8 @@ + + diff --git a/apps/showcase/src/main/webapp/WEB-INF/paramsannotation.vm b/apps/showcase/src/main/webapp/WEB-INF/paramsannotation.vm new file mode 100644 index 000000000..a0c4efefc --- /dev/null +++ b/apps/showcase/src/main/webapp/WEB-INF/paramsannotation.vm @@ -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. +*# +
$action.renderVarToPrint()
diff --git 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 new file mode 100644 index 000000000..417909544 --- /dev/null +++ b/apps/showcase/src/test/java/it/org/apache/struts2/showcase/StrutsParametersTest.java @@ -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 com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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); + } +}