From ac409d6cba5ebeceac1f439b15f7b8927ee31993 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Apr 2024 01:20:48 +0000 Subject: [PATCH 01/33] Bump org.apache.maven.plugins:maven-failsafe-plugin Bumps [org.apache.maven.plugins:maven-failsafe-plugin](https://github.com/apache/maven-surefire) from 3.0.0-M6 to 3.2.5. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.0.0-M6...surefire-3.2.5) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-failsafe-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- apps/showcase/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/showcase/pom.xml b/apps/showcase/pom.xml index a1a9138cd8..551ef179dd 100644 --- a/apps/showcase/pom.xml +++ b/apps/showcase/pom.xml @@ -157,7 +157,7 @@ org.apache.maven.plugins maven-failsafe-plugin - 3.0.0-M6 + 3.2.5 it.org.apache.struts2.showcase.*Test From ccd5f5d53466bcaaea9acfba24fb559f4d26c430 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Apr 2024 01:22:36 +0000 Subject: [PATCH 02/33] Bump asm.version from 9.6 to 9.7 Bumps `asm.version` from 9.6 to 9.7. Updates `org.ow2.asm:asm` from 9.6 to 9.7 Updates `org.ow2.asm:asm-commons` from 9.6 to 9.7 --- updated-dependencies: - dependency-name: org.ow2.asm:asm dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.ow2.asm:asm-commons dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 34490d5e35..4f2de27c2c 100644 --- a/pom.xml +++ b/pom.xml @@ -109,7 +109,7 @@ 1.8 - 9.6 + 9.7 2.16.1 2.23.1 3.3.4 From 268ed789ee1171035cac2722472606d9882e8a5e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Apr 2024 01:22:47 +0000 Subject: [PATCH 03/33] Bump org.assertj:assertj-core from 3.25.2 to 3.25.3 Bumps [org.assertj:assertj-core](https://github.com/assertj/assertj) from 3.25.2 to 3.25.3. - [Release notes](https://github.com/assertj/assertj/releases) - [Commits](https://github.com/assertj/assertj/compare/assertj-build-3.25.2...assertj-build-3.25.3) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 34490d5e35..dbbb9105d1 100644 --- a/pom.xml +++ b/pom.xml @@ -978,7 +978,7 @@ org.assertj assertj-core - 3.25.2 + 3.25.3 test From 758174c52ee1ce050cf671ca7e53c79c4115d8a3 Mon Sep 17 00:00:00 2001 From: "Erica S. Kane" Date: Wed, 10 Apr 2024 18:42:09 -0400 Subject: [PATCH 04/33] WW-5400 Extend default configuration options for the CSP interceptor. Previously, it was impossible to set global options for the CSP interceptor. The only options was to have every action individually implement CspSettingsAware. To fix this, we add an interceptor parameter of defaultCspSettingsClassName. Values from this class will be used in the CSP header instead of DefaultCspSettings. Users may define their own custom class which implements CspSettings, and that will be the default for all actions that do not implement the CspSettingsAware interface. It is now possible to create this custom class by simply extending DefaultCspSettings. I have fixed a spelling error in DefaultCspSettings.java -- cratePolicyFormat renamed to createPolicyFormat. --- .../interceptor/csp/CspInterceptor.java | 42 +++++++++- .../struts2/interceptor/csp/CspSettings.java | 6 ++ .../interceptor/csp/DefaultCspSettings.java | 34 ++++++-- .../interceptor/CspInterceptorTest.java | 83 +++++++++++++++++-- 4 files changed, 147 insertions(+), 18 deletions(-) diff --git a/core/src/main/java/org/apache/struts2/interceptor/csp/CspInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/csp/CspInterceptor.java index 32d6777869..d382dce94b 100644 --- a/core/src/main/java/org/apache/struts2/interceptor/csp/CspInterceptor.java +++ b/core/src/main/java/org/apache/struts2/interceptor/csp/CspInterceptor.java @@ -46,6 +46,9 @@ public final class CspInterceptor extends AbstractInterceptor { private boolean prependServletContext = true; private boolean enforcingMode; private String reportUri; + private String reportTo; + + private String defaultCspSettingsClassName = DefaultCspSettings.class.getName(); @Override public String intercept(ActionInvocation invocation) throws Exception { @@ -54,8 +57,24 @@ public String intercept(ActionInvocation invocation) throws Exception { LOG.trace("Using CspSettings provided by the action: {}", action); applySettings(invocation, ((CspSettingsAware) action).getCspSettings()); } else { - LOG.trace("Using DefaultCspSettings with action: {}", action); - applySettings(invocation, new DefaultCspSettings()); + LOG.trace("Using {} with action: {}", defaultCspSettingsClassName, action); + + // if the defaultCspSettingsClassName is not a real class, throw an exception + try { + Class.forName(defaultCspSettingsClassName, false, Thread.currentThread().getContextClassLoader()); + } + catch (ClassNotFoundException e) { + throw new IllegalArgumentException("The defaultCspSettingsClassName must be a real class."); + } + + // if defaultCspSettingsClassName does not implement CspSettings, throw an exception + if (!CspSettings.class.isAssignableFrom(Class.forName(defaultCspSettingsClassName))) { + throw new IllegalArgumentException("The defaultCspSettingsClassName must implement CspSettings."); + } + + CspSettings cspSettings = (CspSettings) Class.forName(defaultCspSettingsClassName) + .getDeclaredConstructor().newInstance(); + applySettings(invocation, cspSettings); } return invocation.invoke(); } @@ -76,6 +95,12 @@ private void applySettings(ActionInvocation invocation, CspSettings cspSettings) } cspSettings.setReportUri(finalReportUri); + + // apply reportTo if set + if (reportTo != null) { + LOG.trace("Applying: {} to reportTo", reportTo); + cspSettings.setReportTo(reportTo); + } } invocation.addPreResultListener((actionInvocation, resultCode) -> { @@ -97,6 +122,10 @@ public void setReportUri(String reportUri) { this.reportUri = reportUri; } + public void setReportTo(String reportTo) { + this.reportTo = reportTo; + } + private Optional buildUri(String reportUri) { try { return Optional.of(URI.create(reportUri)); @@ -124,4 +153,11 @@ public void setPrependServletContext(boolean prependServletContext) { this.prependServletContext = prependServletContext; } -} + /** + * Sets the class name of the default {@link CspSettings} implementation to use when the action does not + * set its own values. If not set, the default is {@link DefaultCspSettings}. + */ + public void setDefaultCspSettingsClassName(String defaultCspSettingsClassName) { + this.defaultCspSettingsClassName = defaultCspSettingsClassName; + } +} \ No newline at end of file diff --git a/core/src/main/java/org/apache/struts2/interceptor/csp/CspSettings.java b/core/src/main/java/org/apache/struts2/interceptor/csp/CspSettings.java index acb1429629..3b2cadb96b 100644 --- a/core/src/main/java/org/apache/struts2/interceptor/csp/CspSettings.java +++ b/core/src/main/java/org/apache/struts2/interceptor/csp/CspSettings.java @@ -37,6 +37,7 @@ public interface CspSettings { String SCRIPT_SRC = "script-src"; String BASE_URI = "base-uri"; String REPORT_URI = "report-uri"; + String REPORT_TO = "report-to"; String NONE = "none"; String STRICT_DYNAMIC = "strict-dynamic"; String HTTP = "http:"; @@ -56,6 +57,11 @@ public interface CspSettings { */ void setReportUri(String uri); + /** + * Sets the report group where csp violation reports will be sent + */ + void setReportTo(String group); + /** * Sets CSP headers in enforcing mode when true, and report-only when false */ diff --git a/core/src/main/java/org/apache/struts2/interceptor/csp/DefaultCspSettings.java b/core/src/main/java/org/apache/struts2/interceptor/csp/DefaultCspSettings.java index d1768e8696..51c76cfa80 100644 --- a/core/src/main/java/org/apache/struts2/interceptor/csp/DefaultCspSettings.java +++ b/core/src/main/java/org/apache/struts2/interceptor/csp/DefaultCspSettings.java @@ -20,6 +20,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.apache.struts2.action.CspSettingsAware; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -31,7 +32,11 @@ /** * Default implementation of {@link CspSettings}. - * The default policy implements strict CSP with a nonce based approach and follows the guide: https://csp.withgoogle.com/docs/index.html/ + * The default policy implements strict CSP with a nonce based approach and follows the guide: + * https://csp.withgoogle.com/docs/index.html/ + * You may extend or replace this class if you wish to customize the default policy further, and use your class + * by setting the {@link CspInterceptor} defaultCspSettingsClassName parameter. Actions that + * implement the {@link CspSettingsAware} interface will ignore the defaultCspSettingsClassName parameter. * * @see CspSettings * @see CspInterceptor @@ -42,20 +47,22 @@ public class DefaultCspSettings implements CspSettings { private final SecureRandom sRand = new SecureRandom(); - private String reportUri; + protected String reportUri; + protected String reportTo; // default to reporting mode - private String cspHeader = CSP_REPORT_HEADER; + protected String cspHeader = CSP_REPORT_HEADER; @Override public void addCspHeaders(HttpServletResponse response) { throw new UnsupportedOperationException("Unsupported implementation, use #addCspHeaders(HttpServletRequest request, HttpServletResponse response)"); } + @Override public void addCspHeaders(HttpServletRequest request, HttpServletResponse response) { if (isSessionActive(request)) { LOG.trace("Session is active, applying CSP settings"); associateNonceWithSession(request); - response.setHeader(cspHeader, cratePolicyFormat(request)); + response.setHeader(cspHeader, createPolicyFormat(request)); } else { LOG.trace("Session is not active, ignoring CSP settings"); } @@ -70,7 +77,7 @@ private void associateNonceWithSession(HttpServletRequest request) { request.getSession().setAttribute("nonce", nonceValue); } - private String cratePolicyFormat(HttpServletRequest request) { + protected String createPolicyFormat(HttpServletRequest request) { StringBuilder policyFormatBuilder = new StringBuilder() .append(OBJECT_SRC) .append(format(" '%s'; ", NONE)) @@ -84,13 +91,18 @@ private String cratePolicyFormat(HttpServletRequest request) { if (reportUri != null) { policyFormatBuilder .append(REPORT_URI) - .append(format(" %s", reportUri)); + .append(format(" %s; ", reportUri)); + if(reportTo != null) { + policyFormatBuilder + .append(REPORT_TO) + .append(format(" %s; ", reportTo)); + } } return format(policyFormatBuilder.toString(), getNonceString(request)); } - private String getNonceString(HttpServletRequest request) { + protected String getNonceString(HttpServletRequest request) { Object nonce = request.getSession().getAttribute("nonce"); return Objects.toString(nonce); } @@ -101,20 +113,28 @@ private byte[] getRandomBytes() { return ret; } + @Override public void setEnforcingMode(boolean enforcingMode) { if (enforcingMode) { cspHeader = CSP_ENFORCE_HEADER; } } + @Override public void setReportUri(String reportUri) { this.reportUri = reportUri; } + @Override + public void setReportTo(String reportTo) { + this.reportTo = reportTo; + } + @Override public String toString() { return "DefaultCspSettings{" + "reportUri='" + reportUri + '\'' + + "reportTo='" + reportTo + '\'' + ", cspHeader='" + cspHeader + '\'' + '}'; } diff --git a/core/src/test/java/org/apache/struts2/interceptor/CspInterceptorTest.java b/core/src/test/java/org/apache/struts2/interceptor/CspInterceptorTest.java index 0b03c6e54c..cd59c347da 100644 --- a/core/src/test/java/org/apache/struts2/interceptor/CspInterceptorTest.java +++ b/core/src/test/java/org/apache/struts2/interceptor/CspInterceptorTest.java @@ -31,6 +31,7 @@ import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import static org.junit.Assert.assertNotEquals; @@ -74,8 +75,10 @@ public void test_whenNonceAlreadySetInSession_andRequestReceived_thenNewNonceIsS public void testEnforcingCspHeadersSet() throws Exception { String reportUri = "/csp-reports"; + String reportTo = "csp-group"; boolean enforcingMode = true; interceptor.setReportUri(reportUri); + interceptor.setReportTo(reportTo); interceptor.setEnforcingMode(enforcingMode); session.setAttribute("nonce", "foo"); @@ -84,13 +87,15 @@ public void testEnforcingCspHeadersSet() throws Exception { assertNotNull("Nonce key does not exist", session.getAttribute("nonce")); assertFalse("Nonce value is empty", Strings.isEmpty((String) session.getAttribute("nonce"))); assertNotEquals("New nonce value couldn't be set", "foo", session.getAttribute("nonce")); - checkHeader(reportUri, enforcingMode); + checkHeader(reportUri, reportTo, enforcingMode); } public void testReportingCspHeadersSet() throws Exception { String reportUri = "/csp-reports"; + String reportTo = "csp-group"; boolean enforcingMode = false; interceptor.setReportUri(reportUri); + interceptor.setReportTo(reportTo); interceptor.setEnforcingMode(enforcingMode); session.setAttribute("nonce", "foo"); @@ -98,7 +103,7 @@ public void testReportingCspHeadersSet() throws Exception { assertNotNull("Nonce value is empty", session.getAttribute("nonce")); assertNotEquals("New nonce value couldn't be set", "foo", session.getAttribute("nonce")); - checkHeader(reportUri, enforcingMode); + checkHeader(reportUri, reportTo, enforcingMode); } public void test_uriSetOnlyWhenSetIsCalled() throws Exception { @@ -174,7 +179,47 @@ public void testNoPrependContext() throws Exception { checkHeader("/report-uri", enforcingMode); } + public void testInvalidDefaultCspSettingsClassName() throws Exception { + boolean enforcingMode = true; + mai.setAction(new TestAction()); + request.setContextPath("/app"); + + interceptor.setEnforcingMode(enforcingMode); + interceptor.setReportUri("/report-uri"); + interceptor.setPrependServletContext(false); + + try { + interceptor.setDefaultCspSettingsClassName("foo"); + interceptor.intercept(mai); + assert (false); + } catch (IllegalArgumentException e) { + assert (true); + } + } + + public void testCustomDefaultCspSettingsClassName() throws Exception { + boolean enforcingMode = true; + mai.setAction(new TestAction()); + request.setContextPath("/app"); + + interceptor.setEnforcingMode(enforcingMode); + interceptor.setReportUri("/report-uri"); + interceptor.setPrependServletContext(false); + interceptor.setDefaultCspSettingsClassName(CustomDefaultCspSettings.class.getName()); + + interceptor.intercept(mai); + + String header = response.getHeader(CspSettings.CSP_ENFORCE_HEADER); + + // no other customization matters for this particular class + assertEquals("foo", header); + } + public void checkHeader(String reportUri, boolean enforcingMode) { + checkHeader(reportUri, null, enforcingMode); + } + + public void checkHeader(String reportUri, String reportTo, boolean enforcingMode) { String expectedCspHeader; if (Strings.isEmpty(reportUri)) { expectedCspHeader = String.format("%s '%s'; %s 'nonce-%s' '%s' %s %s; %s '%s'; ", @@ -183,12 +228,23 @@ public void checkHeader(String reportUri, boolean enforcingMode) { CspSettings.BASE_URI, CspSettings.NONE ); } else { - expectedCspHeader = String.format("%s '%s'; %s 'nonce-%s' '%s' %s %s; %s '%s'; %s %s", - CspSettings.OBJECT_SRC, CspSettings.NONE, - CspSettings.SCRIPT_SRC, session.getAttribute("nonce"), CspSettings.STRICT_DYNAMIC, CspSettings.HTTP, CspSettings.HTTPS, - CspSettings.BASE_URI, CspSettings.NONE, - CspSettings.REPORT_URI, reportUri - ); + if (Strings.isEmpty(reportTo)) { + expectedCspHeader = String.format("%s '%s'; %s 'nonce-%s' '%s' %s %s; %s '%s'; %s %s; ", + CspSettings.OBJECT_SRC, CspSettings.NONE, + CspSettings.SCRIPT_SRC, session.getAttribute("nonce"), CspSettings.STRICT_DYNAMIC, CspSettings.HTTP, CspSettings.HTTPS, + CspSettings.BASE_URI, CspSettings.NONE, + CspSettings.REPORT_URI, reportUri + ); + } + else { + expectedCspHeader = String.format("%s '%s'; %s 'nonce-%s' '%s' %s %s; %s '%s'; %s %s; %s %s; ", + CspSettings.OBJECT_SRC, CspSettings.NONE, + CspSettings.SCRIPT_SRC, session.getAttribute("nonce"), CspSettings.STRICT_DYNAMIC, CspSettings.HTTP, CspSettings.HTTPS, + CspSettings.BASE_URI, CspSettings.NONE, + CspSettings.REPORT_URI, reportUri, + CspSettings.REPORT_TO, reportTo + ); + } } String header; @@ -230,4 +286,15 @@ public CspSettings getCspSettings() { return settings; } } + + /** + * Custom DefaultCspSettings class that overrides the createPolicyFormat method + * to return a fixed value. + */ + public static class CustomDefaultCspSettings extends DefaultCspSettings { + + protected String createPolicyFormat(HttpServletRequest request) { + return "foo"; + } + } } From 0151bdeb9ca23f750c2454acd4a5d94635993307 Mon Sep 17 00:00:00 2001 From: "Erica S. Kane" Date: Thu, 11 Apr 2024 16:52:44 -0400 Subject: [PATCH 05/33] WW-5400 Better toString formatting --- .../org/apache/struts2/interceptor/csp/DefaultCspSettings.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/apache/struts2/interceptor/csp/DefaultCspSettings.java b/core/src/main/java/org/apache/struts2/interceptor/csp/DefaultCspSettings.java index 51c76cfa80..b245ab3526 100644 --- a/core/src/main/java/org/apache/struts2/interceptor/csp/DefaultCspSettings.java +++ b/core/src/main/java/org/apache/struts2/interceptor/csp/DefaultCspSettings.java @@ -134,7 +134,7 @@ public void setReportTo(String reportTo) { public String toString() { return "DefaultCspSettings{" + "reportUri='" + reportUri + '\'' + - "reportTo='" + reportTo + '\'' + + ", reportTo='" + reportTo + '\'' + ", cspHeader='" + cspHeader + '\'' + '}'; } From 6ac8b04ad222f59b4e21711f0782bb717b28e042 Mon Sep 17 00:00:00 2001 From: "Erica S. Kane" Date: Fri, 12 Apr 2024 13:40:53 -0400 Subject: [PATCH 06/33] WW-5400 Added @since Struts 6.5.0 to new properties as requested --- .../apache/struts2/interceptor/csp/CspInterceptor.java | 10 ++++++++++ .../apache/struts2/interceptor/csp/CspSettings.java | 2 ++ 2 files changed, 12 insertions(+) diff --git a/core/src/main/java/org/apache/struts2/interceptor/csp/CspInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/csp/CspInterceptor.java index d382dce94b..54d9eeab1c 100644 --- a/core/src/main/java/org/apache/struts2/interceptor/csp/CspInterceptor.java +++ b/core/src/main/java/org/apache/struts2/interceptor/csp/CspInterceptor.java @@ -122,6 +122,14 @@ public void setReportUri(String reportUri) { this.reportUri = reportUri; } + /** + * Sets the report group where csp violation reports will be sent. This will + * only be used if the reportUri is set. + * + * @param reportTo the report group where csp violation reports will be sent + * + * @since Struts 6.5.0 + */ public void setReportTo(String reportTo) { this.reportTo = reportTo; } @@ -156,6 +164,8 @@ public void setPrependServletContext(boolean prependServletContext) { /** * Sets the class name of the default {@link CspSettings} implementation to use when the action does not * set its own values. If not set, the default is {@link DefaultCspSettings}. + * + * @since Struts 6.5.0 */ public void setDefaultCspSettingsClassName(String defaultCspSettingsClassName) { this.defaultCspSettingsClassName = defaultCspSettingsClassName; diff --git a/core/src/main/java/org/apache/struts2/interceptor/csp/CspSettings.java b/core/src/main/java/org/apache/struts2/interceptor/csp/CspSettings.java index 3b2cadb96b..a8c2a68c2e 100644 --- a/core/src/main/java/org/apache/struts2/interceptor/csp/CspSettings.java +++ b/core/src/main/java/org/apache/struts2/interceptor/csp/CspSettings.java @@ -59,6 +59,8 @@ public interface CspSettings { /** * Sets the report group where csp violation reports will be sent + * + * @since Struts 6.5.0 */ void setReportTo(String group); From b2b35f066ca036a2b6eeb2e7835c121d0ad7b863 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 01:23:03 +0000 Subject: [PATCH 07/33] Bump org.codehaus.mojo:versions-maven-plugin from 2.16.1 to 2.16.2 Bumps [org.codehaus.mojo:versions-maven-plugin](https://github.com/mojohaus/versions) from 2.16.1 to 2.16.2. - [Release notes](https://github.com/mojohaus/versions/releases) - [Changelog](https://github.com/mojohaus/versions/blob/master/ReleaseNotes.md) - [Commits](https://github.com/mojohaus/versions/compare/2.16.1...2.16.2) --- updated-dependencies: - dependency-name: org.codehaus.mojo:versions-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6c7c0045a8..a4f4394442 100644 --- a/pom.xml +++ b/pom.xml @@ -487,7 +487,7 @@ org.codehaus.mojo versions-maven-plugin - 2.16.1 + 2.16.2 From 856d98010a095ab30e93d7107e0ae568d88923cc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 01:35:16 +0000 Subject: [PATCH 08/33] Bump actions/upload-artifact from 4.3.1 to 4.3.2 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.1 to 4.3.2. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/5d5d22a31266ced268874388b861e4b58bb5c2f3...1746f4ab65b179e0ea60a494b83293b640dd5bba) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/scorecards-analysis.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scorecards-analysis.yaml b/.github/workflows/scorecards-analysis.yaml index 67faabc826..c6de03245a 100644 --- a/.github/workflows/scorecards-analysis.yaml +++ b/.github/workflows/scorecards-analysis.yaml @@ -57,7 +57,7 @@ jobs: publish_results: true - name: "Upload artifact" - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # 4.3.1 + uses: actions/upload-artifact@1746f4ab65b179e0ea60a494b83293b640dd5bba # 4.3.2 with: name: SARIF file path: results.sarif From a88cb741d0296302d033b7e13e4a7806b345dea3 Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Sun, 21 Apr 2024 10:21:55 +0200 Subject: [PATCH 09/33] WW-5419 Fixes support for loading Tiles definitions --- plugins/tiles/pom.xml | 10 +++ .../tiles/StrutsTilesContainerFactory.java | 16 ++-- ...rutsWildcardServletApplicationContext.java | 9 +-- .../StrutsTilesContainerFactoryTest.java | 9 +-- ...WildcardServletApplicationContextTest.java | 79 +++++++++++++++++++ .../src/test/resources/WEB-INF/tiles.xml | 39 +++++++++ .../apache/tiles/core/config/tiles_defs1.xml | 75 ++++++++++++++++++ 7 files changed, 216 insertions(+), 21 deletions(-) create mode 100644 plugins/tiles/src/test/java/org/apache/struts2/tiles/StrutsWildcardServletApplicationContextTest.java create mode 100644 plugins/tiles/src/test/resources/WEB-INF/tiles.xml create mode 100644 plugins/tiles/src/test/resources/org/apache/tiles/core/config/tiles_defs1.xml diff --git a/plugins/tiles/pom.xml b/plugins/tiles/pom.xml index 618bc6a8c4..53d79b89dc 100644 --- a/plugins/tiles/pom.xml +++ b/plugins/tiles/pom.xml @@ -102,6 +102,16 @@ log4j-jcl test + + org.assertj + assertj-core + test + + + org.springframework + spring-test + test + UTF-8 diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesContainerFactory.java b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesContainerFactory.java index ed03f8268f..4cda08522a 100644 --- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesContainerFactory.java +++ b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesContainerFactory.java @@ -68,10 +68,7 @@ import javax.el.ResourceBundleELResolver; import javax.servlet.jsp.JspFactory; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; @@ -105,19 +102,16 @@ public class StrutsTilesContainerFactory extends BasicTilesContainerFactory { /** * Default pattern to be used to collect Tiles definitions if user didn't configure any - * - * @deprecated since Struts 6.4.0, use {@link #TILES_DEFAULT_PATTERNS} instead */ - @Deprecated - public static final String TILES_DEFAULT_PATTERN = "/WEB-INF/**/tiles*.xml,classpath*:META-INF/**/tiles*.xml"; + public static final Set TILES_DEFAULT_PATTERNS = TextParseUtil.commaDelimitedStringToSet("*tiles*.xml"); /** * Default pattern to be used to collect Tiles definitions if user didn't configure any + * + * @deprecated since Struts 6.4.0, use {@link #TILES_DEFAULT_PATTERNS} instead */ - public static final Set TILES_DEFAULT_PATTERNS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList( - "/WEB-INF/**/tiles*.xml", - "classpath*:META-INF/**/tiles*.xml" - ))); + @Deprecated + public static final String TILES_DEFAULT_PATTERN = String.join(",", TILES_DEFAULT_PATTERNS); /** * Supported expression languages diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsWildcardServletApplicationContext.java b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsWildcardServletApplicationContext.java index 7af6069bba..2a30eb6261 100644 --- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsWildcardServletApplicationContext.java +++ b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsWildcardServletApplicationContext.java @@ -43,7 +43,7 @@ public class StrutsWildcardServletApplicationContext extends ServletApplicationC private static final Logger LOG = LogManager.getLogger(StrutsWildcardServletApplicationContext.class); - private ResourceFinder finder; + private final ResourceFinder finder; public StrutsWildcardServletApplicationContext(ServletContext context) { super(context); @@ -64,16 +64,15 @@ public StrutsWildcardServletApplicationContext(ServletContext context) { } try { - Enumeration resources = getClass().getClassLoader().getResources("/"); + Enumeration resources = getClass().getClassLoader().getResources(""); while (resources.hasMoreElements()) { - URL resource = resources.nextElement(); - urls.add(resource); + urls.add(resources.nextElement()); } } catch (IOException e) { throw new ConfigurationException(e); } - finder = new ResourceFinder(urls.toArray(new URL[urls.size()])); + finder = new ResourceFinder(urls.toArray(new URL[0])); } public Collection getResources(String path) { diff --git a/plugins/tiles/src/test/java/org/apache/struts2/tiles/StrutsTilesContainerFactoryTest.java b/plugins/tiles/src/test/java/org/apache/struts2/tiles/StrutsTilesContainerFactoryTest.java index 122bfe51d1..944aac2c99 100644 --- a/plugins/tiles/src/test/java/org/apache/struts2/tiles/StrutsTilesContainerFactoryTest.java +++ b/plugins/tiles/src/test/java/org/apache/struts2/tiles/StrutsTilesContainerFactoryTest.java @@ -34,8 +34,8 @@ import org.junit.Before; import org.junit.Test; -import javax.servlet.ServletContext; import javax.servlet.jsp.JspFactory; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -64,12 +64,11 @@ public void getSources() { Objects.requireNonNull(getClass().getResource("/org/apache/tiles/core/config/tiles-defs.xml")) ); ApplicationResource classpathResource = new URLApplicationResource( - "/org/apache/tiles/core/config/defs1.xml", - Objects.requireNonNull(getClass().getResource("/org/apache/tiles/core/config/defs1.xml")) + "/org/apache/tiles/core/config/tiles_defs1.xml", + Objects.requireNonNull(getClass().getResource("/org/apache/tiles/core/config/tiles_defs1.xml")) ); when(applicationContext.getInitParams()).thenReturn(Collections.emptyMap()); - when(applicationContext.getResources("/WEB-INF/**/tiles*.xml")).thenReturn(Collections.singleton(pathResource)); - when(applicationContext.getResources("classpath*:META-INF/**/tiles*.xml")).thenReturn(Collections.singleton(classpathResource)); + when(applicationContext.getResources("*tiles*.xml")).thenReturn(Arrays.asList(pathResource, classpathResource)); List resources = factory.getSources(applicationContext); assertEquals("The urls list is not two-sized", 2, resources.size()); diff --git a/plugins/tiles/src/test/java/org/apache/struts2/tiles/StrutsWildcardServletApplicationContextTest.java b/plugins/tiles/src/test/java/org/apache/struts2/tiles/StrutsWildcardServletApplicationContextTest.java new file mode 100644 index 0000000000..972ed705ad --- /dev/null +++ b/plugins/tiles/src/test/java/org/apache/struts2/tiles/StrutsWildcardServletApplicationContextTest.java @@ -0,0 +1,79 @@ +/* + * 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.tiles; + +import org.apache.tiles.core.definition.DefinitionsFactory; +import org.apache.tiles.request.ApplicationResource; +import org.junit.Before; +import org.junit.Test; +import org.springframework.core.io.FileSystemResource; +import org.springframework.core.io.FileUrlResource; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.mock.web.MockServletContext; + +import javax.servlet.ServletContext; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Collection; + +import static org.assertj.core.api.Assertions.assertThat; + +public class StrutsWildcardServletApplicationContextTest { + + private ServletContext context; + + @Before + public void setUp() throws Exception { + URL resource = getClass().getResource("/"); + context = new MockServletContext(resource.getPath(), new ResourceLoader() { + @Override + public Resource getResource(String location) { + try { + String finalLocation = location.replaceAll("//", "/"); + if (finalLocation.endsWith("/")) { + return new FileSystemResource(finalLocation); + } + return new FileUrlResource(finalLocation); + } catch (MalformedURLException e) { + return null; + } + } + + @Override + public ClassLoader getClassLoader() { + return StrutsWildcardServletApplicationContextTest.class.getClassLoader(); + } + }); + } + + @Test + public void wildcardSupport() { + StrutsWildcardServletApplicationContext applicationContext = new StrutsWildcardServletApplicationContext(context); + + Collection resources = applicationContext.getResources("*tiles*.xml"); + + assertThat(resources) + .hasSize(1) + .extracting(ApplicationResource::getLocalePath) + .first().asString() + .endsWith("/WEB-INF/tiles.xml"); + } + +} \ No newline at end of file diff --git a/plugins/tiles/src/test/resources/WEB-INF/tiles.xml b/plugins/tiles/src/test/resources/WEB-INF/tiles.xml new file mode 100644 index 0000000000..9fc36a4d6a --- /dev/null +++ b/plugins/tiles/src/test/resources/WEB-INF/tiles.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + diff --git a/plugins/tiles/src/test/resources/org/apache/tiles/core/config/tiles_defs1.xml b/plugins/tiles/src/test/resources/org/apache/tiles/core/config/tiles_defs1.xml new file mode 100644 index 0000000000..621d7cef9e --- /dev/null +++ b/plugins/tiles/src/test/resources/org/apache/tiles/core/config/tiles_defs1.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 36275b9e0470c5ace87e2e82d0d70ae708949b24 Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Thu, 25 Apr 2024 08:25:09 +0200 Subject: [PATCH 10/33] WW-5420 Upgrades commons-text to ver. 1.12.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c86b1290fd..b8dd012b15 100644 --- a/pom.xml +++ b/pom.xml @@ -864,7 +864,7 @@ org.apache.commons commons-text - 1.11.0 + 1.12.0 commons-el From c1c6a463a80af8ccbd9de9d23fc1e70546613544 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 01:15:19 +0000 Subject: [PATCH 11/33] Bump org.codehaus.mojo:exec-maven-plugin from 3.1.0 to 3.2.0 Bumps [org.codehaus.mojo:exec-maven-plugin](https://github.com/mojohaus/exec-maven-plugin) from 3.1.0 to 3.2.0. - [Release notes](https://github.com/mojohaus/exec-maven-plugin/releases) - [Commits](https://github.com/mojohaus/exec-maven-plugin/compare/exec-maven-plugin-3.1.0...3.2.0) --- updated-dependencies: - dependency-name: org.codehaus.mojo:exec-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- plugins/tiles/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/tiles/pom.xml b/plugins/tiles/pom.xml index 618bc6a8c4..208e50fd36 100644 --- a/plugins/tiles/pom.xml +++ b/plugins/tiles/pom.xml @@ -40,7 +40,7 @@ org.codehaus.mojo exec-maven-plugin - 3.1.0 + 3.2.0 compile From 2d9af47a90743ce8adf6e5b8c968740c83b9d1f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 01:46:50 +0000 Subject: [PATCH 12/33] Bump actions/upload-artifact from 4.3.2 to 4.3.3 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.2 to 4.3.3. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/1746f4ab65b179e0ea60a494b83293b640dd5bba...65462800fd760344b1a7b4382951275a0abb4808) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/scorecards-analysis.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scorecards-analysis.yaml b/.github/workflows/scorecards-analysis.yaml index c6de03245a..6867c45cc7 100644 --- a/.github/workflows/scorecards-analysis.yaml +++ b/.github/workflows/scorecards-analysis.yaml @@ -57,7 +57,7 @@ jobs: publish_results: true - name: "Upload artifact" - uses: actions/upload-artifact@1746f4ab65b179e0ea60a494b83293b640dd5bba # 4.3.2 + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # 4.3.3 with: name: SARIF file path: results.sarif From 4dfbe093438d8bda9fed71c227416a6ace22c934 Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Sat, 11 May 2024 08:23:56 +0200 Subject: [PATCH 13/33] WW-5414 Always call afterInvocation even in case of exception --- core/pom.xml | 6 ++ .../exec/StrutsBackgroundProcess.java | 20 +++++-- .../exec/StrutsBackgroundProcessTest.java | 60 +++++++++++++++++-- pom.xml | 7 +++ 4 files changed, 83 insertions(+), 10 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 8165d191a5..4027d37e71 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -260,6 +260,12 @@ test + + org.awaitility + awaitility + test + + junit junit diff --git a/core/src/main/java/org/apache/struts2/interceptor/exec/StrutsBackgroundProcess.java b/core/src/main/java/org/apache/struts2/interceptor/exec/StrutsBackgroundProcess.java index 42237262f8..b385c9b8fa 100644 --- a/core/src/main/java/org/apache/struts2/interceptor/exec/StrutsBackgroundProcess.java +++ b/core/src/main/java/org/apache/struts2/interceptor/exec/StrutsBackgroundProcess.java @@ -20,6 +20,8 @@ import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionInvocation; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import java.io.Serializable; @@ -30,6 +32,8 @@ public class StrutsBackgroundProcess implements BackgroundProcess, Serializable private static final long serialVersionUID = 3884464776311686443L; + private static final Logger LOG = LogManager.getLogger(StrutsBackgroundProcess.class); + private final String threadName; private final int threadPriority; @@ -44,8 +48,8 @@ public class StrutsBackgroundProcess implements BackgroundProcess, Serializable /** * Constructs a background process * - * @param invocation The action invocation - * @param threadName The name of background thread + * @param invocation The action invocation + * @param threadName The name of background thread * @param threadPriority The priority of background thread */ public StrutsBackgroundProcess(ActionInvocation invocation, String threadName, int threadPriority) { @@ -61,11 +65,19 @@ public BackgroundProcess prepare() { try { beforeInvocation(); result = invocation.invokeActionOnly(); - afterInvocation(); } catch (Exception e) { + LOG.warn("Exception during invokeActionOnly() execution", e); exception = e; } finally { - done = true; + try { + afterInvocation(); + } catch (Exception ex) { + if (exception == null) { + exception = ex; + } + LOG.warn("Exception during afterInvocation() execution", ex); + } + done = true; } }); processThread.setName(threadName); diff --git a/core/src/test/java/org/apache/struts2/interceptor/exec/StrutsBackgroundProcessTest.java b/core/src/test/java/org/apache/struts2/interceptor/exec/StrutsBackgroundProcessTest.java index 5906c995a6..a705c2c7b2 100644 --- a/core/src/test/java/org/apache/struts2/interceptor/exec/StrutsBackgroundProcessTest.java +++ b/core/src/test/java/org/apache/struts2/interceptor/exec/StrutsBackgroundProcessTest.java @@ -26,6 +26,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.NotSerializableException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.security.SecureRandom; @@ -41,6 +42,8 @@ import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; +import static org.awaitility.Awaitility.await; + /** * Test case for BackgroundProcessTest. */ @@ -59,9 +62,9 @@ public void testSerializeDeserialize() throws Exception { invocation.setInvocationContext(ActionContext.getContext()); StrutsBackgroundProcess bp = (StrutsBackgroundProcess) new StrutsBackgroundProcess( - invocation, - "BackgroundProcessTest.testSerializeDeserialize", - Thread.MIN_PRIORITY + invocation, + "BackgroundProcessTest.testSerializeDeserialize", + Thread.MIN_PRIORITY ).prepare(); executor.execute(bp); @@ -120,6 +123,31 @@ public void testMultipleProcesses() throws InterruptedException { assertEquals(100, mutableState.get()); } + public void testErrorableProcesses1() { + MockActionInvocationWithActionInvoker invocation = new MockActionInvocationWithActionInvoker(() -> { + throw new IllegalStateException("boom"); + }); + + BackgroundProcess bp = new ErrorableBackgroundProcess(invocation, null).prepare(); + executor.execute(bp); + + await().atLeast(100, TimeUnit.MILLISECONDS).until(bp::isDone); + + assertTrue("afterInvocation not called in case of exception", ((ErrorableBackgroundProcess) bp).isDoneAfter()); + } + + public void testErrorableProcesses2() { + MockActionInvocationWithActionInvoker invocation = new MockActionInvocationWithActionInvoker(() -> "done"); + + IllegalStateException expected = new IllegalStateException("after!"); + BackgroundProcess bp = new ErrorableBackgroundProcess(invocation, expected).prepare(); + executor.execute(bp); + + await().atLeast(100, TimeUnit.MILLISECONDS).until(bp::isDone); + + assertEquals(expected, bp.getException()); + } + public void testUnpreparedProcess() throws ExecutionException, InterruptedException, TimeoutException { // given MockActionInvocationWithActionInvoker invocation = new MockActionInvocationWithActionInvoker(() -> "done"); @@ -147,7 +175,8 @@ public String invokeActionOnly() throws Exception { } private static class NotSerializableException extends Exception { - private MockHttpServletRequest notSerializableField; + @SuppressWarnings("unused") + private final MockHttpServletRequest notSerializableField; NotSerializableException(MockHttpServletRequest notSerializableField) { this.notSerializableField = notSerializableField; @@ -170,10 +199,29 @@ public void run() { super.run(); } } +} + +class ErrorableBackgroundProcess extends StrutsBackgroundProcess { + + private final Exception afterException; + private boolean doneAfter; + + public ErrorableBackgroundProcess(ActionInvocation invocation, Exception afterException) { + super(invocation, "errorabale process", Thread.NORM_PRIORITY); + this.afterException = afterException; + } @Override protected void afterInvocation() throws Exception { - super.afterInvocation(); - lock.notify(); + if (afterException != null) { + throw afterException; + } else { + super.afterInvocation(); + doneAfter = true; + } + } + + public boolean isDoneAfter() { + return doneAfter; } } diff --git a/pom.xml b/pom.xml index 69eea6f951..0f27bd29c3 100644 --- a/pom.xml +++ b/pom.xml @@ -774,6 +774,13 @@ test + + org.awaitility + awaitility + 4.2.1 + test + + javax.servlet javax.servlet-api From b36e88ff4b2810776854041dc1e71fd95228aef5 Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Sun, 12 May 2024 09:47:38 +0200 Subject: [PATCH 14/33] WW-5415 Fixes accessing public constructors via expression --- .../xwork2/ognl/SecurityMemberAccess.java | 2 +- .../xwork2/validator/VisitorFieldValidatorTest.java | 11 +++++++++++ .../xwork2/validator/VisitorValidatorTestAction.java | 11 ++++++++++- .../VisitorValidatorTestAction-validation.xml | 8 ++++++++ 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java b/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java index 43ae992405..8a8c713269 100644 --- a/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java +++ b/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java @@ -147,7 +147,7 @@ public boolean isAccessible(Map context, Object target, Member member, String pr if (target != null) { // Special case: Target is a Class object but not Class.class if (Class.class.equals(target.getClass()) && !Class.class.equals(target)) { - if (!isStatic(member)) { + if (!isStatic(member) && Arrays.stream(((Class) target).getConstructors()).noneMatch(p -> p.getClass().equals(member.getClass()))) { throw new IllegalArgumentException("Member expected to be static!"); } if (!member.getDeclaringClass().equals(target)) { diff --git a/core/src/test/java/com/opensymphony/xwork2/validator/VisitorFieldValidatorTest.java b/core/src/test/java/com/opensymphony/xwork2/validator/VisitorFieldValidatorTest.java index 76c2eac718..de605d2c5f 100644 --- a/core/src/test/java/com/opensymphony/xwork2/validator/VisitorFieldValidatorTest.java +++ b/core/src/test/java/com/opensymphony/xwork2/validator/VisitorFieldValidatorTest.java @@ -28,6 +28,8 @@ import com.opensymphony.xwork2.conversion.impl.ConversionData; import org.easymock.EasyMock; +import java.sql.Date; +import java.time.LocalDate; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.HashMap; @@ -142,6 +144,15 @@ public void testCollectionValidation() throws Exception { assertEquals(1, errors.size()); } + public void testDateValidation() throws Exception { + action.setBirthday(Date.valueOf(LocalDate.now().minusYears(20))); + action.setContext("birthday"); + + validate("birthday"); + + assertFalse(action.hasFieldErrors()); + } + public void testContextIsOverriddenByContextParamInValidationXML() throws Exception { validate("visitorValidationAlias"); assertTrue(action.hasFieldErrors()); diff --git a/core/src/test/java/com/opensymphony/xwork2/validator/VisitorValidatorTestAction.java b/core/src/test/java/com/opensymphony/xwork2/validator/VisitorValidatorTestAction.java index 2050726f7f..9e672bf489 100644 --- a/core/src/test/java/com/opensymphony/xwork2/validator/VisitorValidatorTestAction.java +++ b/core/src/test/java/com/opensymphony/xwork2/validator/VisitorValidatorTestAction.java @@ -22,6 +22,7 @@ import com.opensymphony.xwork2.TestBean; import java.util.ArrayList; +import java.util.Date; import java.util.List; @@ -37,7 +38,7 @@ public class VisitorValidatorTestAction extends ActionSupport { private String context; private TestBean bean = new TestBean(); private TestBean[] testBeanArray; - + private Date birthday; public VisitorValidatorTestAction() { testBeanArray = new TestBean[5]; @@ -80,4 +81,12 @@ public void setTestBeanList(List testBeanList) { public List getTestBeanList() { return testBeanList; } + + public Date getBirthday() { + return birthday; + } + + public void setBirthday(Date birthday) { + this.birthday = birthday; + } } diff --git a/core/src/test/resources/com/opensymphony/xwork2/validator/VisitorValidatorTestAction-validation.xml b/core/src/test/resources/com/opensymphony/xwork2/validator/VisitorValidatorTestAction-validation.xml index a8de9f705c..fb2aa80bf2 100644 --- a/core/src/test/resources/com/opensymphony/xwork2/validator/VisitorValidatorTestAction-validation.xml +++ b/core/src/test/resources/com/opensymphony/xwork2/validator/VisitorValidatorTestAction-validation.xml @@ -26,4 +26,12 @@ You must enter a context. + + + + + + From 942fbd67e7a0923f64f7fd6ef783f18eed119a35 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 01:15:24 +0000 Subject: [PATCH 15/33] Bump org.apache.maven.plugins:maven-source-plugin from 3.3.0 to 3.3.1 Bumps [org.apache.maven.plugins:maven-source-plugin](https://github.com/apache/maven-source-plugin) from 3.3.0 to 3.3.1. - [Commits](https://github.com/apache/maven-source-plugin/compare/maven-source-plugin-3.3.0...maven-source-plugin-3.3.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-source-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 69eea6f951..74a28c9360 100644 --- a/pom.xml +++ b/pom.xml @@ -296,7 +296,7 @@ org.apache.maven.plugins maven-source-plugin - 3.3.0 + 3.3.1 org.apache.rat From c6b53d48875f79833a0b1f77d1a911b7a4c882e2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 01:16:15 +0000 Subject: [PATCH 16/33] Bump slf4j.version from 2.0.12 to 2.0.13 Bumps `slf4j.version` from 2.0.12 to 2.0.13. Updates `org.slf4j:slf4j-api` from 2.0.12 to 2.0.13 Updates `org.slf4j:slf4j-simple` from 2.0.12 to 2.0.13 --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.slf4j:slf4j-simple dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 69eea6f951..6bfb7f6c82 100644 --- a/pom.xml +++ b/pom.xml @@ -113,7 +113,7 @@ 2.16.1 2.23.1 3.3.5 - 2.0.12 + 2.0.13 5.3.31 3.0.8 1.0.7 From 6e12c25ac03a24148e396ccb8f72e1a23be6ce25 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 01:16:29 +0000 Subject: [PATCH 17/33] Bump org.apache.maven.plugins:maven-project-info-reports-plugin Bumps [org.apache.maven.plugins:maven-project-info-reports-plugin](https://github.com/apache/maven-project-info-reports-plugin) from 3.0.0 to 3.5.0. - [Commits](https://github.com/apache/maven-project-info-reports-plugin/compare/maven-project-info-reports-plugin-3.0.0...maven-project-info-reports-plugin-3.5.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-project-info-reports-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 69eea6f951..3ffcc5a40c 100644 --- a/pom.xml +++ b/pom.xml @@ -249,7 +249,7 @@ org.apache.maven.plugins maven-project-info-reports-plugin - 3.0.0 + 3.5.0 org.apache.maven.plugins From 039c5f8d42f52643ce4f2ec392ec3c7d97564f2c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 01:38:25 +0000 Subject: [PATCH 18/33] Bump ossf/scorecard-action from 2.3.1 to 2.3.3 Bumps [ossf/scorecard-action](https://github.com/ossf/scorecard-action) from 2.3.1 to 2.3.3. - [Release notes](https://github.com/ossf/scorecard-action/releases) - [Changelog](https://github.com/ossf/scorecard-action/blob/main/RELEASE.md) - [Commits](https://github.com/ossf/scorecard-action/compare/0864cf19026789058feabb7e87baa5f140aac736...dc50aa9510b46c811795eb24b2f1ba02a914e534) --- updated-dependencies: - dependency-name: ossf/scorecard-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/scorecards-analysis.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scorecards-analysis.yaml b/.github/workflows/scorecards-analysis.yaml index 6867c45cc7..2b55504fb2 100644 --- a/.github/workflows/scorecards-analysis.yaml +++ b/.github/workflows/scorecards-analysis.yaml @@ -45,7 +45,7 @@ jobs: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # 2.3.1 + uses: ossf/scorecard-action@dc50aa9510b46c811795eb24b2f1ba02a914e534 # 2.3.3 with: results_file: results.sarif results_format: sarif From 7c523ac33e3bdade323bc4e414b98de66f268c09 Mon Sep 17 00:00:00 2001 From: Kusal Kithul-Godage Date: Mon, 13 May 2024 12:37:04 +1000 Subject: [PATCH 19/33] WW-5415 Constructor members should be exempted as static members --- .../com/opensymphony/xwork2/ognl/SecurityMemberAccess.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java b/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java index 8a8c713269..f882b2c583 100644 --- a/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java +++ b/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java @@ -29,6 +29,7 @@ import org.apache.struts2.ognl.ThreadAllowlist; import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Member; import java.lang.reflect.Modifier; @@ -147,11 +148,11 @@ public boolean isAccessible(Map context, Object target, Member member, String pr if (target != null) { // Special case: Target is a Class object but not Class.class if (Class.class.equals(target.getClass()) && !Class.class.equals(target)) { - if (!isStatic(member) && Arrays.stream(((Class) target).getConstructors()).noneMatch(p -> p.getClass().equals(member.getClass()))) { - throw new IllegalArgumentException("Member expected to be static!"); + if (!isStatic(member) && !Constructor.class.equals(member.getClass())) { + throw new IllegalArgumentException("Member expected to be static or constructor!"); } if (!member.getDeclaringClass().equals(target)) { - throw new IllegalArgumentException("Target class does not match static member!"); + throw new IllegalArgumentException("Target class does not match member!"); } target = null; // This information is not useful to us and conflicts with following logic which expects target to be null or an instance containing the member // Standard case: Member should exist on target From 0a57cac6d6645789e36597fa0282d86f93455849 Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Sat, 11 May 2024 07:23:22 +0200 Subject: [PATCH 20/33] WW-5422 Adds dedicate unit test to cover DefaultLocaleProvider --- .../opensymphony/xwork2/ActionSupport.java | 5 + .../xwork2/DefaultLocaleProvider.java | 18 +- .../opensymphony/xwork2/LocaleProvider.java | 16 ++ .../validator/DelegatingValidatorContext.java | 12 +- .../struts2/interceptor/I18nInterceptor.java | 25 +-- .../xwork2/DefaultLocaleProviderTest.java | 174 ++++++++++++++++++ .../xwork2/LocaleProviderTest.java | 82 +++++++++ .../interceptor/I18nInterceptorTest.java | 20 ++ 8 files changed, 329 insertions(+), 23 deletions(-) create mode 100644 core/src/test/java/com/opensymphony/xwork2/DefaultLocaleProviderTest.java create mode 100644 core/src/test/java/com/opensymphony/xwork2/LocaleProviderTest.java diff --git a/core/src/main/java/com/opensymphony/xwork2/ActionSupport.java b/core/src/main/java/com/opensymphony/xwork2/ActionSupport.java index 8c7e15e609..ab1a18099a 100644 --- a/core/src/main/java/com/opensymphony/xwork2/ActionSupport.java +++ b/core/src/main/java/com/opensymphony/xwork2/ActionSupport.java @@ -90,6 +90,11 @@ public boolean isValidLocale(Locale locale) { return getLocaleProvider().isValidLocale(locale); } + @Override + public Locale toLocale(String localeStr) { + return getLocaleProvider().toLocale(localeStr); + } + @Override public boolean hasKey(String key) { return getTextProvider().hasKey(key); diff --git a/core/src/main/java/com/opensymphony/xwork2/DefaultLocaleProvider.java b/core/src/main/java/com/opensymphony/xwork2/DefaultLocaleProvider.java index da89306c0c..35f16191a8 100644 --- a/core/src/main/java/com/opensymphony/xwork2/DefaultLocaleProvider.java +++ b/core/src/main/java/com/opensymphony/xwork2/DefaultLocaleProvider.java @@ -46,17 +46,23 @@ public Locale getLocale() { @Override public boolean isValidLocaleString(String localeStr) { + Locale locale = this.toLocale(localeStr); + return isValidLocale(locale); + } + + @Override + public boolean isValidLocale(Locale locale) { + return locale != null && LocaleUtils.isAvailableLocale(locale); + } + + @Override + public Locale toLocale(String localeStr) { Locale locale = null; try { locale = LocaleUtils.toLocale(StringUtils.trimToNull(localeStr)); } catch (IllegalArgumentException e) { LOG.warn(new ParameterizedMessage("Cannot convert [{}] to proper locale", localeStr), e); } - return isValidLocale(locale); - } - - @Override - public boolean isValidLocale(Locale locale) { - return LocaleUtils.isAvailableLocale(locale); + return locale; } } diff --git a/core/src/main/java/com/opensymphony/xwork2/LocaleProvider.java b/core/src/main/java/com/opensymphony/xwork2/LocaleProvider.java index 67972af341..00a41a25b3 100644 --- a/core/src/main/java/com/opensymphony/xwork2/LocaleProvider.java +++ b/core/src/main/java/com/opensymphony/xwork2/LocaleProvider.java @@ -18,6 +18,9 @@ */ package com.opensymphony.xwork2; +import org.apache.commons.lang3.LocaleUtils; +import org.apache.commons.lang3.StringUtils; + import java.util.Locale; @@ -58,4 +61,17 @@ public interface LocaleProvider { */ boolean isValidLocale(Locale locale); + /** + * Tries to convert provided locale string into {@link Locale} or returns null + * @param localeStr a String representing locale, e.g.: en_EN + * @return instance of {@link Locale} or null + * @since Struts 6.5.0 + */ + default Locale toLocale(String localeStr) { + try { + return LocaleUtils.toLocale(StringUtils.trimToNull(localeStr)); + } catch (IllegalArgumentException e) { + return null; + } + } } diff --git a/core/src/main/java/com/opensymphony/xwork2/validator/DelegatingValidatorContext.java b/core/src/main/java/com/opensymphony/xwork2/validator/DelegatingValidatorContext.java index 5c7f2c136a..bc8c88875c 100644 --- a/core/src/main/java/com/opensymphony/xwork2/validator/DelegatingValidatorContext.java +++ b/core/src/main/java/com/opensymphony/xwork2/validator/DelegatingValidatorContext.java @@ -122,10 +122,15 @@ public boolean isValidLocale(Locale locale) { return localeProvider.isValidLocale(locale); } + @Override + public Locale toLocale(String localeStr) { + return localeProvider.toLocale(localeStr); + } + public boolean hasKey(String key) { return textProvider.hasKey(key); } - + public String getText(String aTextName) { return textProvider.getText(aTextName); } @@ -280,6 +285,11 @@ public boolean isValidLocaleString(String localeStr) { public boolean isValidLocale(Locale locale) { return getLocaleProvider().isValidLocale(locale); } + + @Override + public Locale toLocale(String localeStr) { + return getLocaleProvider().toLocale(localeStr); + } } /** diff --git a/core/src/main/java/org/apache/struts2/interceptor/I18nInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/I18nInterceptor.java index e0f978f6ea..6bce042446 100644 --- a/core/src/main/java/org/apache/struts2/interceptor/I18nInterceptor.java +++ b/core/src/main/java/org/apache/struts2/interceptor/I18nInterceptor.java @@ -24,7 +24,6 @@ import com.opensymphony.xwork2.inject.Inject; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; import com.opensymphony.xwork2.util.TextParseUtil; -import org.apache.commons.lang3.LocaleUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; @@ -85,7 +84,7 @@ public void setRequestCookieParameterName(String requestCookieParameterName) { } public void setLocaleStorage(String storageName) { - if (storageName == null || "".equals(storageName)) { + if (storageName == null || storageName.isEmpty()) { this.storage = Storage.ACCEPT_LANGUAGE; } else { try { @@ -169,27 +168,21 @@ protected LocaleHandler getLocaleHandler(ActionInvocation invocation) { } /** - * Creates a Locale object from the request param, which might - * be already a Local or a String + * Creates a Locale object from the request param * * @param requestedLocale the parameter from the request - * @return the Locale + * @return instance of {@link Locale} or null */ - protected Locale getLocaleFromParam(Object requestedLocale) { + protected Locale getLocaleFromParam(String requestedLocale) { LocaleProvider localeProvider = localeProviderFactory.createLocaleProvider(); Locale locale = null; if (requestedLocale != null) { - if (requestedLocale instanceof Locale) { - locale = (Locale) requestedLocale; - } else { - String localeStr = requestedLocale.toString(); - if (localeProvider.isValidLocaleString(localeStr)) { - locale = LocaleUtils.toLocale(localeStr); - } else { - locale = localeProvider.getLocale(); - } + locale = localeProvider.toLocale(requestedLocale); + if (locale == null) { + locale = localeProvider.getLocale(); } + if (locale != null) { LOG.debug("Found locale: {}", locale); } @@ -285,7 +278,7 @@ protected AcceptLanguageLocaleHandler(ActionInvocation invocation) { @Override @SuppressWarnings("rawtypes") public Locale find() { - if (supportedLocale.size() > 0) { + if (!supportedLocale.isEmpty()) { Enumeration locales = actionInvocation.getInvocationContext().getServletRequest().getLocales(); while (locales.hasMoreElements()) { Locale locale = (Locale) locales.nextElement(); diff --git a/core/src/test/java/com/opensymphony/xwork2/DefaultLocaleProviderTest.java b/core/src/test/java/com/opensymphony/xwork2/DefaultLocaleProviderTest.java new file mode 100644 index 0000000000..bb5178abd9 --- /dev/null +++ b/core/src/test/java/com/opensymphony/xwork2/DefaultLocaleProviderTest.java @@ -0,0 +1,174 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.opensymphony.xwork2; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.Locale; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +public class DefaultLocaleProviderTest { + + private DefaultLocaleProvider provider; + + @Before + public void setUp() throws Exception { + provider = new DefaultLocaleProvider(); + } + + @BeforeClass + public static void beforeClass() throws Exception { + ActionContext.of().bind(); + } + + @AfterClass + public static void afterClass() throws Exception { + ActionContext.clear(); + } + + @Test + public void getLocale() { + // given + ActionContext.getContext().withLocale(Locale.ITALY); + + // when + Locale actual = provider.getLocale(); + + // then + assertEquals(Locale.ITALY, actual); + } + + @Test + public void getLocaleNull() { + // given + ActionContext backup = ActionContext.getContext(); + ActionContext.clear(); + + // when + Locale actual = provider.getLocale(); + + // then + assertNull(actual); + ActionContext.bind(backup); + } + + @Test + public void toLocale() { + // given + ActionContext.getContext().withLocale(Locale.GERMAN); + + // when + Locale actual = provider.toLocale("it"); + + // then + assertEquals(Locale.ITALIAN, actual); + } + + @Test + public void toLocaleFull() { + // given + ActionContext.getContext().withLocale(Locale.GERMAN); + + // when + Locale actual = provider.toLocale("it_IT"); + + // then + assertEquals(Locale.ITALY, actual); + } + + @Test + public void toLocaleTrimEndOfLine() { + // given + ActionContext.getContext().withLocale(Locale.GERMAN); + + // when + Locale actual = provider.toLocale("it_IT\n"); + + // then + assertEquals(Locale.ITALY, actual); + } + + @Test + public void toLocaleTrimEmptySpace() { + // given + ActionContext.getContext().withLocale(Locale.GERMAN); + + // when + Locale actual = provider.toLocale(" it_IT "); + + // then + assertEquals(Locale.ITALY, actual); + } + + @Test + public void isValidLocaleNull() { + // given + ActionContext.getContext().withLocale(Locale.GERMAN); + + // when + boolean actual = provider.isValidLocale(null); + + // then + assertFalse(actual); + } + + @Test + public void isValidLocale() { + // given + ActionContext.getContext().withLocale(Locale.GERMAN); + + // when + boolean actual = provider.isValidLocale(Locale.ITALIAN); + + // then + assertTrue(actual); + } + + @Test + public void isValidLocaleString() { + // given + ActionContext.getContext().withLocale(Locale.GERMAN); + + // when + boolean actual = provider.isValidLocaleString("it"); + + // then + assertTrue(actual); + } + + @Test + public void isValidLocaleStringNot() { + // given + ActionContext.getContext().withLocale(Locale.GERMAN); + + // when + boolean actual = provider.isValidLocaleString("italy"); + + // then + assertFalse(actual); + } + +} diff --git a/core/src/test/java/com/opensymphony/xwork2/LocaleProviderTest.java b/core/src/test/java/com/opensymphony/xwork2/LocaleProviderTest.java new file mode 100644 index 0000000000..03e05f5c23 --- /dev/null +++ b/core/src/test/java/com/opensymphony/xwork2/LocaleProviderTest.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.opensymphony.xwork2; + +import org.junit.Test; + +import java.util.Locale; + +import static org.junit.Assert.*; + +public class LocaleProviderTest { + + @Test + public void toLocale() { + // given + DummyLocale locale = new DummyLocale(); + + // when + Locale actual = locale.toLocale("de"); + + // then + assertEquals(Locale.GERMAN, actual); + } + + @Test + public void toLocaleTrim() { + // given + DummyLocale locale = new DummyLocale(); + + // when + Locale actual = locale.toLocale(" de_DE "); + + // then + assertEquals(Locale.GERMANY, actual); + } + + @Test + public void toLocaleNull() { + // given + DummyLocale locale = new DummyLocale(); + + // when + Locale actual = locale.toLocale("germany"); + + // then + assertNull(actual); + } + +} + +class DummyLocale implements LocaleProvider { + @Override + public Locale getLocale() { + return null; + } + + @Override + public boolean isValidLocaleString(String localeStr) { + return false; + } + + @Override + public boolean isValidLocale(Locale locale) { + return false; + } +} diff --git a/core/src/test/java/org/apache/struts2/interceptor/I18nInterceptorTest.java b/core/src/test/java/org/apache/struts2/interceptor/I18nInterceptorTest.java index a8fd8420f7..604d61be69 100644 --- a/core/src/test/java/org/apache/struts2/interceptor/I18nInterceptorTest.java +++ b/core/src/test/java/org/apache/struts2/interceptor/I18nInterceptorTest.java @@ -147,6 +147,26 @@ public void testNotExistingLocale() throws Exception { assertEquals(Locale.getDefault(), session.get(I18nInterceptor.DEFAULT_SESSION_ATTRIBUTE)); // should create a locale object } + public void testTrimableLocaleString1() throws Exception { + prepare(I18nInterceptor.DEFAULT_PARAMETER, "de\n"); + + interceptor.intercept(mai); + + assertFalse(mai.getInvocationContext().getParameters().get(I18nInterceptor.DEFAULT_PARAMETER).isDefined()); // should have been removed + assertNotNull(session.get(I18nInterceptor.DEFAULT_SESSION_ATTRIBUTE)); // should be stored here + assertEquals(Locale.GERMAN, session.get(I18nInterceptor.DEFAULT_SESSION_ATTRIBUTE)); // should create a locale object + } + + public void testTrimableLocaleString2() throws Exception { + prepare(I18nInterceptor.DEFAULT_PARAMETER, "de "); + + interceptor.intercept(mai); + + assertFalse(mai.getInvocationContext().getParameters().get(I18nInterceptor.DEFAULT_PARAMETER).isDefined()); // should have been removed + assertNotNull(session.get(I18nInterceptor.DEFAULT_SESSION_ATTRIBUTE)); // should be stored here + assertEquals(Locale.GERMAN, session.get(I18nInterceptor.DEFAULT_SESSION_ATTRIBUTE)); // should create a locale object + } + public void testWithVariant() throws Exception { prepare(I18nInterceptor.DEFAULT_PARAMETER, "ja_JP_JP"); interceptor.intercept(mai); From 19252adea071b61d167dd2e22b6aa35b63302a5c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 May 2024 01:19:53 +0000 Subject: [PATCH 21/33] Bump org.jacoco:jacoco-maven-plugin from 0.8.11 to 0.8.12 Bumps [org.jacoco:jacoco-maven-plugin](https://github.com/jacoco/jacoco) from 0.8.11 to 0.8.12. - [Release notes](https://github.com/jacoco/jacoco/releases) - [Commits](https://github.com/jacoco/jacoco/compare/v0.8.11...v0.8.12) --- updated-dependencies: - dependency-name: org.jacoco:jacoco-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7db8b3effe..034112b612 100644 --- a/pom.xml +++ b/pom.xml @@ -215,7 +215,7 @@ org.jacoco jacoco-maven-plugin - 0.8.11 + 0.8.12 prepare-agent From ab89b8287ac385e9cab068bed1860d338c0bd644 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 May 2024 01:22:13 +0000 Subject: [PATCH 22/33] Bump jackson.version from 2.16.1 to 2.17.1 Bumps `jackson.version` from 2.16.1 to 2.17.1. Updates `com.fasterxml.jackson.core:jackson-core` from 2.16.1 to 2.17.1 - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.16.1...jackson-core-2.17.1) Updates `com.fasterxml.jackson.core:jackson-databind` from 2.16.1 to 2.17.1 - [Commits](https://github.com/FasterXML/jackson/commits) Updates `com.fasterxml.jackson.dataformat:jackson-dataformat-xml` from 2.16.1 to 2.17.1 - [Commits](https://github.com/FasterXML/jackson-dataformat-xml/compare/jackson-dataformat-xml-2.16.1...jackson-dataformat-xml-2.17.1) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.fasterxml.jackson.dataformat:jackson-dataformat-xml dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7db8b3effe..c8be68dbe2 100644 --- a/pom.xml +++ b/pom.xml @@ -110,7 +110,7 @@ 9.7 - 2.16.1 + 2.17.1 2.23.1 3.3.5 2.0.13 From 9ee39d0a2cae2585ff551152d36450a7ba777019 Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Mon, 20 May 2024 07:02:45 +0200 Subject: [PATCH 23/33] INFRA-25666 Disables review by code owners * this functionality requires to have public teams * currently all ASF teams are private --- .asf.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.asf.yaml b/.asf.yaml index 87706aa578..3f83d509ab 100644 --- a/.asf.yaml +++ b/.asf.yaml @@ -18,7 +18,8 @@ github: contexts: - build required_pull_request_reviews: - require_code_owner_reviews: true + # it does not work because our github teams are private/secret, see INFRA-25666 + require_code_owner_reviews: false required_approving_review_count: 1 autolink_jira: - WW From 4a8ff99b1cc04bc69927aa3508c64fcb9d95519e Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Sun, 2 Jun 2024 13:53:37 +0200 Subject: [PATCH 24/33] WW-5424 Fixes ClassCastException when using short var name in s:set tag --- .../org/apache/struts2/components/Set.java | 12 ++-- .../apache/struts2/views/jsp/SetTagTest.java | 58 ++++++++++++++++--- 2 files changed, 55 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/org/apache/struts2/components/Set.java b/core/src/main/java/org/apache/struts2/components/Set.java index cca990ea84..4d8f7c8981 100644 --- a/core/src/main/java/org/apache/struts2/components/Set.java +++ b/core/src/main/java/org/apache/struts2/components/Set.java @@ -104,17 +104,17 @@ public boolean end(Writer writer, String body) { body=""; if (DispatcherConstants.APPLICATION.equalsIgnoreCase(scope)) { - stack.setValue("#application['" + getVar() + "']", o); + stack.setValue(String.format("#application[\"%s\"]", getVar()), o); } else if (DispatcherConstants.SESSION.equalsIgnoreCase(scope)) { - stack.setValue("#session['" + getVar() + "']", o); + stack.setValue(String.format("#session[\"%s\"]", getVar()), o); } else if (DispatcherConstants.REQUEST.equalsIgnoreCase(scope)) { - stack.setValue("#request['" + getVar() + "']", o); + stack.setValue(String.format("#request[\"%s\"]", getVar()), o); } else if (DispatcherConstants.PAGE.equalsIgnoreCase(scope)) { - stack.setValue("#attr['" + getVar() + "']", o, false); + stack.setValue(String.format("#attr[\"%s\"]", getVar()), o, false); } else { // Default scope is action. Note: The action scope handling also adds the var to the page scope. - stack.getContext().put(getVar(), o); - stack.setValue("#attr['" + getVar() + "']", o, false); + putInContext(o); + stack.setValue(String.format("#attr[\"%s\"]", getVar()), o, false); } return super.end(writer, body); diff --git a/core/src/test/java/org/apache/struts2/views/jsp/SetTagTest.java b/core/src/test/java/org/apache/struts2/views/jsp/SetTagTest.java index 9e5c30eb50..f14e077355 100644 --- a/core/src/test/java/org/apache/struts2/views/jsp/SetTagTest.java +++ b/core/src/test/java/org/apache/struts2/views/jsp/SetTagTest.java @@ -22,14 +22,10 @@ import java.io.IOException; import javax.servlet.jsp.JspException; - -/** - */ public class SetTagTest extends AbstractUITagTest { - Chewbacca chewie; - SetTag tag; - + private Chewbacca chewie; + private SetTag tag; public void testApplicationScope() throws JspException { tag.setName("foo"); @@ -397,6 +393,50 @@ public void testEmptyBody_clearTagStateSet() throws JspException { strutsBodyTagsAreReflectionEqual(tag, freshTag)); } + public void testShortVarNameInPageScope() throws JspException { + tag.setName("f"); + tag.setValue("name"); + tag.setScope("page"); + + tag.doStartTag(); + tag.doEndTag(); + + assertEquals("chewie", pageContext.getAttribute("f")); + } + + public void testShortVarNameInRequestScope() throws JspException { + tag.setName("f"); + tag.setValue("name"); + tag.setScope("request"); + + tag.doStartTag(); + tag.doEndTag(); + + assertEquals("chewie", request.getAttribute("f")); + } + + public void testShortVarNameInSessionScope() throws JspException { + tag.setName("f"); + tag.setValue("name"); + tag.setScope("session"); + + tag.doStartTag(); + tag.doEndTag(); + + assertEquals("chewie", session.get("f")); + } + + public void testShortVarNameInApplicationScope() throws JspException { + tag.setName("f"); + tag.setValue("name"); + tag.setScope("application"); + + tag.doStartTag(); + tag.doEndTag(); + + assertEquals("chewie", servletContext.getAttribute("f")); + } + @Override protected void setUp() throws Exception { super.setUp(); @@ -408,9 +448,9 @@ protected void setUp() throws Exception { } - public class Chewbacca { - String name; - boolean furry; + public static class Chewbacca { + private String name; + private boolean furry; public Chewbacca(String name, boolean furry) { this.name = name; From 00752e33c4e9bc6e2924e40f03cedcda1905552c Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Sun, 2 Jun 2024 13:59:41 +0200 Subject: [PATCH 25/33] Disables required reviewers option This option doesn't play with Silence Consensus --- .asf.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.asf.yaml b/.asf.yaml index 3f83d509ab..5e259c7103 100644 --- a/.asf.yaml +++ b/.asf.yaml @@ -20,7 +20,7 @@ github: required_pull_request_reviews: # it does not work because our github teams are private/secret, see INFRA-25666 require_code_owner_reviews: false - required_approving_review_count: 1 + required_approving_review_count: 0 autolink_jira: - WW dependabot_alerts: true From 855b95e557340c10f6d36e7fe64e96650210e56b Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Sun, 2 Jun 2024 19:13:01 +0200 Subject: [PATCH 26/33] WW-5412 Upgrades struts-master to ver 15 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1e5e67e4ec..cf6bb206d9 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ org.apache.struts struts-master - 14 + 15 4.0.0 From e5fcc8f175fea081b569113c9a94156719de489a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 01:34:52 +0000 Subject: [PATCH 27/33] Bump org.apache.maven.plugins:maven-assembly-plugin from 3.6.0 to 3.7.1 Bumps [org.apache.maven.plugins:maven-assembly-plugin](https://github.com/apache/maven-assembly-plugin) from 3.6.0 to 3.7.1. - [Release notes](https://github.com/apache/maven-assembly-plugin/releases) - [Commits](https://github.com/apache/maven-assembly-plugin/compare/maven-assembly-plugin-3.6.0...maven-assembly-plugin-3.7.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-assembly-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- assembly/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assembly/pom.xml b/assembly/pom.xml index c59792f80d..8ea9075640 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -106,7 +106,7 @@ org.apache.maven.plugins maven-assembly-plugin - 3.6.0 + 3.7.1 make-assembly From 5c2be7cc7ddbe9096b140caa6d9d3b8eceea569e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 01:37:13 +0000 Subject: [PATCH 28/33] Bump org.freemarker:freemarker from 2.3.32 to 2.3.33 Bumps org.freemarker:freemarker from 2.3.32 to 2.3.33. --- updated-dependencies: - dependency-name: org.freemarker:freemarker dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1e5e67e4ec..59b5fdbbe8 100644 --- a/pom.xml +++ b/pom.xml @@ -119,7 +119,7 @@ 1.0.7 3.2.5 6.2.4.Final - 2.3.32 + 2.3.33 1.9 From 3a6ad5a5576f9a30957b5a93f06f950af5e616db Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Wed, 5 Jun 2024 07:37:36 +0200 Subject: [PATCH 29/33] WW-5400 Simplifies how CspSettings is created --- .../interceptor/csp/CspInterceptor.java | 45 ++++++++------- .../interceptor/CspInterceptorTest.java | 55 +++++++++++++------ 2 files changed, 61 insertions(+), 39 deletions(-) diff --git a/core/src/main/java/org/apache/struts2/interceptor/csp/CspInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/csp/CspInterceptor.java index 54d9eeab1c..49bc04d30f 100644 --- a/core/src/main/java/org/apache/struts2/interceptor/csp/CspInterceptor.java +++ b/core/src/main/java/org/apache/struts2/interceptor/csp/CspInterceptor.java @@ -19,7 +19,9 @@ package org.apache.struts2.interceptor.csp; import com.opensymphony.xwork2.ActionInvocation; +import com.opensymphony.xwork2.config.ConfigurationException; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; +import com.opensymphony.xwork2.util.ClassLoaderUtil; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.struts2.action.CspSettingsAware; @@ -48,7 +50,7 @@ public final class CspInterceptor extends AbstractInterceptor { private String reportUri; private String reportTo; - private String defaultCspSettingsClassName = DefaultCspSettings.class.getName(); + private String cspSettingsClassName = DefaultCspSettings.class.getName(); @Override public String intercept(ActionInvocation invocation) throws Exception { @@ -57,26 +59,28 @@ public String intercept(ActionInvocation invocation) throws Exception { LOG.trace("Using CspSettings provided by the action: {}", action); applySettings(invocation, ((CspSettingsAware) action).getCspSettings()); } else { - LOG.trace("Using {} with action: {}", defaultCspSettingsClassName, action); + LOG.trace("Using {} with action: {}", cspSettingsClassName, action); + CspSettings cspSettings = createCspSettings(invocation); + applySettings(invocation, cspSettings); + } + return invocation.invoke(); + } - // if the defaultCspSettingsClassName is not a real class, throw an exception - try { - Class.forName(defaultCspSettingsClassName, false, Thread.currentThread().getContextClassLoader()); - } - catch (ClassNotFoundException e) { - throw new IllegalArgumentException("The defaultCspSettingsClassName must be a real class."); - } + private CspSettings createCspSettings(ActionInvocation invocation) throws ClassNotFoundException { + Class cspSettingsClass; - // if defaultCspSettingsClassName does not implement CspSettings, throw an exception - if (!CspSettings.class.isAssignableFrom(Class.forName(defaultCspSettingsClassName))) { - throw new IllegalArgumentException("The defaultCspSettingsClassName must implement CspSettings."); - } + try { + cspSettingsClass = ClassLoaderUtil.loadClass(cspSettingsClassName, getClass()); + } catch (ClassNotFoundException e) { + throw new ConfigurationException(String.format("The class %s doesn't exist!", cspSettingsClassName)); + } - CspSettings cspSettings = (CspSettings) Class.forName(defaultCspSettingsClassName) - .getDeclaredConstructor().newInstance(); - applySettings(invocation, cspSettings); + if (!CspSettings.class.isAssignableFrom(Class.forName(cspSettingsClassName))) { + throw new ConfigurationException(String.format("The class %s doesn't implement %s!", + cspSettingsClassName, CspSettings.class.getName())); } - return invocation.invoke(); + + return (CspSettings) invocation.getInvocationContext().getContainer().inject(cspSettingsClass); } private void applySettings(ActionInvocation invocation, CspSettings cspSettings) { @@ -127,7 +131,6 @@ public void setReportUri(String reportUri) { * only be used if the reportUri is set. * * @param reportTo the report group where csp violation reports will be sent - * * @since Struts 6.5.0 */ public void setReportTo(String reportTo) { @@ -167,7 +170,7 @@ public void setPrependServletContext(boolean prependServletContext) { * * @since Struts 6.5.0 */ - public void setDefaultCspSettingsClassName(String defaultCspSettingsClassName) { - this.defaultCspSettingsClassName = defaultCspSettingsClassName; + public void setCspSettingsClassName(String cspSettingsClassName) { + this.cspSettingsClassName = cspSettingsClassName; } -} \ No newline at end of file +} diff --git a/core/src/test/java/org/apache/struts2/interceptor/CspInterceptorTest.java b/core/src/test/java/org/apache/struts2/interceptor/CspInterceptorTest.java index cd59c347da..221e725db2 100644 --- a/core/src/test/java/org/apache/struts2/interceptor/CspInterceptorTest.java +++ b/core/src/test/java/org/apache/struts2/interceptor/CspInterceptorTest.java @@ -19,6 +19,7 @@ package org.apache.struts2.interceptor; import com.opensymphony.xwork2.ActionContext; +import com.opensymphony.xwork2.config.ConfigurationException; import com.opensymphony.xwork2.mock.MockActionInvocation; import org.apache.logging.log4j.util.Strings; import org.apache.struts2.StrutsInternalTestCase; @@ -75,7 +76,7 @@ public void test_whenNonceAlreadySetInSession_andRequestReceived_thenNewNonceIsS public void testEnforcingCspHeadersSet() throws Exception { String reportUri = "/csp-reports"; - String reportTo = "csp-group"; + String reportTo = "csp-group"; boolean enforcingMode = true; interceptor.setReportUri(reportUri); interceptor.setReportTo(reportTo); @@ -92,7 +93,7 @@ public void testEnforcingCspHeadersSet() throws Exception { public void testReportingCspHeadersSet() throws Exception { String reportUri = "/csp-reports"; - String reportTo = "csp-group"; + String reportTo = "csp-group"; boolean enforcingMode = false; interceptor.setReportUri(reportUri); interceptor.setReportTo(reportTo); @@ -179,7 +180,7 @@ public void testNoPrependContext() throws Exception { checkHeader("/report-uri", enforcingMode); } - public void testInvalidDefaultCspSettingsClassName() throws Exception { + public void testNonExistingCspSettingsClassName() throws Exception { boolean enforcingMode = true; mai.setAction(new TestAction()); request.setContextPath("/app"); @@ -189,15 +190,15 @@ public void testInvalidDefaultCspSettingsClassName() throws Exception { interceptor.setPrependServletContext(false); try { - interceptor.setDefaultCspSettingsClassName("foo"); + interceptor.setCspSettingsClassName("foo"); interceptor.intercept(mai); - assert (false); - } catch (IllegalArgumentException e) { - assert (true); + fail("Expected exception"); + } catch (ConfigurationException e) { + assertEquals("The class foo doesn't exist!", e.getMessage()); } } - public void testCustomDefaultCspSettingsClassName() throws Exception { + public void testInvalidCspSettingsClassName() throws Exception { boolean enforcingMode = true; mai.setAction(new TestAction()); request.setContextPath("/app"); @@ -205,7 +206,25 @@ public void testCustomDefaultCspSettingsClassName() throws Exception { interceptor.setEnforcingMode(enforcingMode); interceptor.setReportUri("/report-uri"); interceptor.setPrependServletContext(false); - interceptor.setDefaultCspSettingsClassName(CustomDefaultCspSettings.class.getName()); + + try { + interceptor.setCspSettingsClassName(Integer.class.getName()); + interceptor.intercept(mai); + fail("Expected exception"); + } catch (ConfigurationException e) { + assertEquals("The class java.lang.Integer doesn't implement org.apache.struts2.interceptor.csp.CspSettings!", e.getMessage()); + } + } + + public void testCustomCspSettingsClassName() throws Exception { + boolean enforcingMode = true; + mai.setAction(new TestAction()); + request.setContextPath("/app"); + + interceptor.setEnforcingMode(enforcingMode); + interceptor.setReportUri("/report-uri"); + interceptor.setPrependServletContext(false); + interceptor.setCspSettingsClassName(CustomDefaultCspSettings.class.getName()); interceptor.intercept(mai); @@ -223,9 +242,9 @@ public void checkHeader(String reportUri, String reportTo, boolean enforcingMode String expectedCspHeader; if (Strings.isEmpty(reportUri)) { expectedCspHeader = String.format("%s '%s'; %s 'nonce-%s' '%s' %s %s; %s '%s'; ", - CspSettings.OBJECT_SRC, CspSettings.NONE, - CspSettings.SCRIPT_SRC, session.getAttribute("nonce"), CspSettings.STRICT_DYNAMIC, CspSettings.HTTP, CspSettings.HTTPS, - CspSettings.BASE_URI, CspSettings.NONE + CspSettings.OBJECT_SRC, CspSettings.NONE, + CspSettings.SCRIPT_SRC, session.getAttribute("nonce"), CspSettings.STRICT_DYNAMIC, CspSettings.HTTP, CspSettings.HTTPS, + CspSettings.BASE_URI, CspSettings.NONE ); } else { if (Strings.isEmpty(reportTo)) { @@ -235,8 +254,7 @@ public void checkHeader(String reportUri, String reportTo, boolean enforcingMode CspSettings.BASE_URI, CspSettings.NONE, CspSettings.REPORT_URI, reportUri ); - } - else { + } else { expectedCspHeader = String.format("%s '%s'; %s 'nonce-%s' '%s' %s %s; %s '%s'; %s %s; %s %s; ", CspSettings.OBJECT_SRC, CspSettings.NONE, CspSettings.SCRIPT_SRC, session.getAttribute("nonce"), CspSettings.STRICT_DYNAMIC, CspSettings.HTTP, CspSettings.HTTPS, @@ -263,10 +281,11 @@ protected void setUp() throws Exception { super.setUp(); container.inject(interceptor); ActionContext context = ActionContext.getContext() - .withServletRequest(request) - .withServletResponse(response) - .withSession(new SessionMap(request)) - .bind(); + .withContainer(container) + .withServletRequest(request) + .withServletResponse(response) + .withSession(new SessionMap(request)) + .bind(); mai.setInvocationContext(context); session = request.getSession(); } From 4a05653a557927742248dd7b7eedc2da2f9d27dd Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Thu, 6 Jun 2024 06:32:30 +0200 Subject: [PATCH 30/33] Removes duplication Co-authored-by: Kusal Kithul-Godage --- .../java/org/apache/struts2/interceptor/csp/CspInterceptor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/apache/struts2/interceptor/csp/CspInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/csp/CspInterceptor.java index 49bc04d30f..f28e6ec63b 100644 --- a/core/src/main/java/org/apache/struts2/interceptor/csp/CspInterceptor.java +++ b/core/src/main/java/org/apache/struts2/interceptor/csp/CspInterceptor.java @@ -75,7 +75,7 @@ private CspSettings createCspSettings(ActionInvocation invocation) throws ClassN throw new ConfigurationException(String.format("The class %s doesn't exist!", cspSettingsClassName)); } - if (!CspSettings.class.isAssignableFrom(Class.forName(cspSettingsClassName))) { + if (!CspSettings.class.isAssignableFrom(cspSettingsClass)) { throw new ConfigurationException(String.format("The class %s doesn't implement %s!", cspSettingsClassName, CspSettings.class.getName())); } From 03db4f1ab06c200b0478b069214d7579a08a3ef0 Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Fri, 7 Jun 2024 08:33:08 +0200 Subject: [PATCH 31/33] WW-5250 Addresses TODO in test and stops using Mock Objects --- .../validator/ActionValidatorManager.java | 4 +- .../DefaultActionValidatorManager.java | 18 +- .../DefaultActionValidatorManagerTest.java | 358 +++++++++--------- 3 files changed, 187 insertions(+), 193 deletions(-) diff --git a/core/src/main/java/com/opensymphony/xwork2/validator/ActionValidatorManager.java b/core/src/main/java/com/opensymphony/xwork2/validator/ActionValidatorManager.java index d78224b264..ea0de3a724 100644 --- a/core/src/main/java/com/opensymphony/xwork2/validator/ActionValidatorManager.java +++ b/core/src/main/java/com/opensymphony/xwork2/validator/ActionValidatorManager.java @@ -36,7 +36,7 @@ public interface ActionValidatorManager { * @param method the name of the method being invoked on the action - can be null. * @return a list of all validators for the given class and context. */ - List getValidators(Class clazz, String context, String method); + List getValidators(Class clazz, String context, String method); /** * Returns a list of validators for the given class and context. This is the primary @@ -46,7 +46,7 @@ public interface ActionValidatorManager { * @param context the context of the action class - can be null. * @return a list of all validators for the given class and context. */ - List getValidators(Class clazz, String context); + List getValidators(Class clazz, String context); /** * Validates the given object using action and its context. diff --git a/core/src/main/java/com/opensymphony/xwork2/validator/DefaultActionValidatorManager.java b/core/src/main/java/com/opensymphony/xwork2/validator/DefaultActionValidatorManager.java index 3af54669ef..f05b804860 100644 --- a/core/src/main/java/com/opensymphony/xwork2/validator/DefaultActionValidatorManager.java +++ b/core/src/main/java/com/opensymphony/xwork2/validator/DefaultActionValidatorManager.java @@ -125,7 +125,7 @@ public void validate(Object object, String context, ValidatorContext validatorCo * @param context context * @return a validator key which is the class name plus context. */ - protected String buildValidatorKey(Class clazz, String context) { + protected String buildValidatorKey(Class clazz, String context) { return clazz.getName() + "/" + context; } @@ -137,7 +137,7 @@ protected Validator getValidatorFromValidatorConfig(ValidatorConfig config, Valu } @Override - public synchronized List getValidators(Class clazz, String context, String method) { + public synchronized List getValidators(Class clazz, String context, String method) { String validatorKey = buildValidatorKey(clazz, context); if (!validatorCache.containsKey(validatorKey)) { @@ -158,7 +158,7 @@ public synchronized List getValidators(Class clazz, String context, S } @Override - public synchronized List getValidators(Class clazz, String context) { + public synchronized List getValidators(Class clazz, String context) { return getValidators(clazz, context, null); } @@ -277,7 +277,7 @@ public void validate(Object object, String context, ValidatorContext validatorCo * @param checked the set of previously checked class-contexts, null if none have been checked * @return a list of validator configs for the given class and context. */ - protected List buildValidatorConfigs(Class clazz, String context, boolean checkFile, Set checked) { + protected List buildValidatorConfigs(Class clazz, String context, boolean checkFile, Set checked) { List validatorConfigs = new ArrayList<>(); if (checked == null) { @@ -287,7 +287,7 @@ protected List buildValidatorConfigs(Class clazz, String contex } if (clazz.isInterface()) { - for (Class anInterface : clazz.getInterfaces()) { + for (Class anInterface : clazz.getInterfaces()) { validatorConfigs.addAll(buildValidatorConfigs(anInterface, context, checkFile, checked)); } } else { @@ -297,7 +297,7 @@ protected List buildValidatorConfigs(Class clazz, String contex } // look for validators for implemented interfaces - for (Class anInterface1 : clazz.getInterfaces()) { + for (Class anInterface1 : clazz.getInterfaces()) { if (checked.contains(anInterface1.getName())) { continue; } @@ -317,17 +317,17 @@ protected List buildValidatorConfigs(Class clazz, String contex return validatorConfigs; } - protected List buildAliasValidatorConfigs(Class aClass, String context, boolean checkFile) { + protected List buildAliasValidatorConfigs(Class aClass, String context, boolean checkFile) { String fileName = aClass.getName().replace('.', '/') + "-" + context + VALIDATION_CONFIG_SUFFIX; return loadFile(fileName, aClass, checkFile); } - protected List buildClassValidatorConfigs(Class aClass, boolean checkFile) { + protected List buildClassValidatorConfigs(Class aClass, boolean checkFile) { String fileName = aClass.getName().replace('.', '/') + VALIDATION_CONFIG_SUFFIX; return loadFile(fileName, aClass, checkFile); } - protected List loadFile(String fileName, Class clazz, boolean checkFile) { + protected List loadFile(String fileName, Class clazz, boolean checkFile) { List retList = Collections.emptyList(); URL fileUrl = ClassLoaderUtil.getResource(fileName, clazz); diff --git a/core/src/test/java/com/opensymphony/xwork2/validator/DefaultActionValidatorManagerTest.java b/core/src/test/java/com/opensymphony/xwork2/validator/DefaultActionValidatorManagerTest.java index c6f4b3d6f2..5014a374f7 100644 --- a/core/src/test/java/com/opensymphony/xwork2/validator/DefaultActionValidatorManagerTest.java +++ b/core/src/test/java/com/opensymphony/xwork2/validator/DefaultActionValidatorManagerTest.java @@ -18,161 +18,167 @@ */ package com.opensymphony.xwork2.validator; -import com.mockobjects.dynamic.C; -import com.mockobjects.dynamic.Mock; -import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.FileManagerFactory; import com.opensymphony.xwork2.SimpleAction; -import com.opensymphony.xwork2.StubValueStack; import com.opensymphony.xwork2.TestBean; +import com.opensymphony.xwork2.ValidationOrderAction; import com.opensymphony.xwork2.XWorkTestCase; -import com.opensymphony.xwork2.config.ConfigurationException; +import com.opensymphony.xwork2.interceptor.ValidationAware; import com.opensymphony.xwork2.test.DataAware2; -import com.opensymphony.xwork2.test.SimpleAction2; import com.opensymphony.xwork2.test.SimpleAction3; -import com.opensymphony.xwork2.util.ValueStack; -import com.opensymphony.xwork2.util.fs.DefaultFileManager; -import com.opensymphony.xwork2.util.fs.DefaultFileManagerFactory; +import com.opensymphony.xwork2.test.User; +import com.opensymphony.xwork2.validator.validators.DateRangeFieldValidator; +import com.opensymphony.xwork2.validator.validators.DoubleRangeFieldValidator; +import com.opensymphony.xwork2.validator.validators.ExpressionValidator; +import com.opensymphony.xwork2.validator.validators.IntRangeFieldValidator; +import com.opensymphony.xwork2.validator.validators.LongRangeFieldValidator; +import com.opensymphony.xwork2.validator.validators.RequiredFieldValidator; +import com.opensymphony.xwork2.validator.validators.RequiredStringValidator; +import com.opensymphony.xwork2.validator.validators.ShortRangeFieldValidator; import org.apache.struts2.StrutsException; +import org.xml.sax.SAXParseException; import java.util.ArrayList; -import java.util.HashMap; +import java.util.Iterator; import java.util.List; +import java.util.Map; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; -/** - * DefaultActionValidatorManagerTest - * - * @author Jason Carreira - * @author tm_jee - * @version $Date$ $Id$ - */ public class DefaultActionValidatorManagerTest extends XWorkTestCase { protected final String alias = "validationAlias"; DefaultActionValidatorManager actionValidatorManager; - Mock mockValidatorFileParser; - Mock mockValidatorFactory; - ValueStack stubValueStack; @Override protected void setUp() throws Exception { - actionValidatorManager = new DefaultActionValidatorManager(); super.setUp(); - mockValidatorFileParser = new Mock(ValidatorFileParser.class); - actionValidatorManager.setValidatorFileParser((ValidatorFileParser)mockValidatorFileParser.proxy()); - - mockValidatorFactory = new Mock(ValidatorFactory.class); - actionValidatorManager.setValidatorFactory((ValidatorFactory)mockValidatorFactory.proxy()); - - stubValueStack = new StubValueStack(); - ActionContext.of() - .withValueStack(stubValueStack) - .bind(); - - DefaultFileManagerFactory factory = new DefaultFileManagerFactory(); - factory.setContainer(container); - factory.setFileManager(new DefaultFileManager()); - actionValidatorManager.setFileManagerFactory(factory); + actionValidatorManager = container.inject(DefaultActionValidatorManager.class); } @Override protected void tearDown() throws Exception { - actionValidatorManager = null; super.tearDown(); - mockValidatorFactory = null; - mockValidatorFileParser = null; + actionValidatorManager = null; } - public void testBuildValidatorKey() { String validatorKey = actionValidatorManager.buildValidatorKey(SimpleAction.class, alias); assertEquals(SimpleAction.class.getName() + "/" + alias, validatorKey); } public void testBuildsValidatorsForAlias() { - mockValidatorFileParser.expectAndReturn("parseActionValidatorConfigs", - C.args(C.IS_NOT_NULL, C.IS_NOT_NULL, C.eq("com/opensymphony/xwork2/SimpleAction-validation.xml")), - new ArrayList()); - mockValidatorFileParser.expectAndReturn("parseActionValidatorConfigs", - C.args(C.IS_NOT_NULL, C.IS_NOT_NULL, C.eq("com/opensymphony/xwork2/SimpleAction-validationAlias-validation.xml")), - new ArrayList()); - actionValidatorManager.getValidators(SimpleAction.class, alias); - mockValidatorFileParser.verify(); + List validators = actionValidatorManager.getValidators(SimpleAction.class, alias); + + assertThat(validators).hasSize(11).map(Validator::getClass).containsExactly( + ExpressionValidator.class, + RequiredFieldValidator.class, + IntRangeFieldValidator.class, + DoubleRangeFieldValidator.class, + DateRangeFieldValidator.class, + IntRangeFieldValidator.class, + IntRangeFieldValidator.class, + LongRangeFieldValidator.class, + ShortRangeFieldValidator.class, + RequiredFieldValidator.class, + IntRangeFieldValidator.class + ); + assertThat(validators).hasSize(11).map(Validator::getDefaultMessage).containsExactly( + "Foo must be greater than Bar. Foo = ${foo}, Bar = ${bar}.", + "You must enter a value for bar.", + "bar must be between ${min} and ${max}, current value is ${bar}.", + "percentage must be between ${minExclusive} and ${maxExclusive}, current value is ${percentage}.", + "The date must be between 12-22-2002 and 12-25-2002.", + "Could not find foo.range!", + "Could not find baz.range!", + "Could not find foo.range!", + "Could not find foo.range!", + "You must enter a value for baz.", + "baz out of range." + ); } public void testBuildsValidatorsForAliasError() { - boolean pass = false; - try { - mockValidatorFileParser.expectAndReturn("parseActionValidatorConfigs", - C.args(C.IS_NOT_NULL, C.IS_NOT_NULL, C.eq("com/opensymphony/xwork2/TestBean-validation.xml")), - new ArrayList()); - mockValidatorFileParser.expectAndThrow("parseActionValidatorConfigs", - C.args(C.IS_NOT_NULL, C.IS_NOT_NULL, C.eq("com/opensymphony/xwork2/TestBean-badtest-validation.xml")), - new ConfigurationException()); - List validatorList = actionValidatorManager.getValidators(TestBean.class, "badtest"); - } catch (StrutsException ex) { - pass = true; - } - mockValidatorFileParser.verify(); - assertTrue("Didn't throw exception on load failure", pass); + assertThatThrownBy(() -> actionValidatorManager.getValidators(TestBean.class, "badtest")) + .isInstanceOf(StrutsException.class) + .hasCause(new SAXParseException("Attribute \"foo\" must be declared for element type \"field-validator\".", null)); } public void testGetValidatorsForInterface() { - mockValidatorFileParser.expectAndReturn("parseActionValidatorConfigs", - C.args(C.IS_NOT_NULL, C.IS_NOT_NULL, C.eq("com/opensymphony/xwork2/test/DataAware-validation.xml")), - new ArrayList()); - mockValidatorFileParser.expectAndReturn("parseActionValidatorConfigs", - C.args(C.IS_NOT_NULL, C.IS_NOT_NULL, C.eq("com/opensymphony/xwork2/test/DataAware-validationAlias-validation.xml")), - new ArrayList()); - mockValidatorFileParser.expectAndReturn("parseActionValidatorConfigs", - C.args(C.IS_NOT_NULL, C.IS_NOT_NULL, C.eq("com/opensymphony/xwork2/test/DataAware2-validation.xml")), - new ArrayList()); - actionValidatorManager.getValidators(DataAware2.class, alias); - mockValidatorFileParser.verify(); + List validators = actionValidatorManager.getValidators(DataAware2.class, alias); + + assertThat(validators).hasSize(3).map(Validator::getClass).containsExactly( + RequiredFieldValidator.class, + RequiredStringValidator.class, + RequiredStringValidator.class + ); + assertThat(validators).hasSize(3).map(Validator::getValidatorType).containsExactly( + "required", + "requiredstring", + "requiredstring" + ); + assertThat(validators).hasSize(3).map(Validator::getDefaultMessage).containsExactly( + "You must enter a value for data.", + "You must enter a value for data.", + "You must enter a value for data." + ); } public void testGetValidatorsFromInterface() { - mockValidatorFileParser.expectAndReturn("parseActionValidatorConfigs", - C.args(C.IS_NOT_NULL, C.IS_NOT_NULL, C.eq("com/opensymphony/xwork2/SimpleAction-validation.xml")), - new ArrayList()); - mockValidatorFileParser.expectAndReturn("parseActionValidatorConfigs", - C.args(C.IS_NOT_NULL, C.IS_NOT_NULL, C.eq("com/opensymphony/xwork2/SimpleAction-validationAlias-validation.xml")), - new ArrayList()); - mockValidatorFileParser.expectAndReturn("parseActionValidatorConfigs", - C.args(C.IS_NOT_NULL, C.IS_NOT_NULL, C.eq("com/opensymphony/xwork2/test/DataAware-validation.xml")), - new ArrayList()); - mockValidatorFileParser.expectAndReturn("parseActionValidatorConfigs", - C.args(C.IS_NOT_NULL, C.IS_NOT_NULL, C.eq("com/opensymphony/xwork2/test/DataAware-validationAlias-validation.xml")), - new ArrayList()); - actionValidatorManager.getValidators(SimpleAction3.class, alias); - mockValidatorFileParser.verify(); - } - - public void testSameAliasWithDifferentClass() { - mockValidatorFileParser.expectAndReturn("parseActionValidatorConfigs", - C.args(C.IS_NOT_NULL, C.IS_NOT_NULL, C.eq("com/opensymphony/xwork2/SimpleAction-validation.xml")), - new ArrayList()); - mockValidatorFileParser.expectAndReturn("parseActionValidatorConfigs", - C.args(C.IS_NOT_NULL, C.IS_NOT_NULL, C.eq("com/opensymphony/xwork2/SimpleAction-validationAlias-validation.xml")), - new ArrayList()); - mockValidatorFileParser.expectAndReturn("parseActionValidatorConfigs", - C.args(C.IS_NOT_NULL, C.IS_NOT_NULL, C.eq("com/opensymphony/xwork2/test/SimpleAction2-validation.xml")), - new ArrayList()); - mockValidatorFileParser.expectAndReturn("parseActionValidatorConfigs", - C.args(C.IS_NOT_NULL, C.IS_NOT_NULL, C.eq("com/opensymphony/xwork2/test/SimpleAction2-validationAlias-validation.xml")), - new ArrayList()); - actionValidatorManager.getValidators(SimpleAction.class, alias); - actionValidatorManager.getValidators(SimpleAction2.class, alias); - mockValidatorFileParser.verify(); + List validators = actionValidatorManager.getValidators(SimpleAction3.class, alias); + + assertThat(validators).hasSize(13).map(Validator::getClass).containsExactly( + ExpressionValidator.class, + RequiredFieldValidator.class, + IntRangeFieldValidator.class, + DoubleRangeFieldValidator.class, + DateRangeFieldValidator.class, + IntRangeFieldValidator.class, + IntRangeFieldValidator.class, + LongRangeFieldValidator.class, + ShortRangeFieldValidator.class, + RequiredFieldValidator.class, + IntRangeFieldValidator.class, + RequiredFieldValidator.class, + RequiredStringValidator.class + ); + assertThat(validators).hasSize(13).map(Validator::getValidatorType).containsExactly( + "expression", + "required", + "int", + "double", + "date", + "int", + "int", + "long", + "short", + "required", + "int", + "required", + "requiredstring" + ); + assertThat(validators).hasSize(13).map(Validator::getDefaultMessage).containsExactly( + "Foo must be greater than Bar. Foo = ${foo}, Bar = ${bar}.", + "You must enter a value for bar.", + "bar must be between ${min} and ${max}, current value is ${bar}.", + "percentage must be between ${minExclusive} and ${maxExclusive}, current value is ${percentage}.", + "The date must be between 12-22-2002 and 12-25-2002.", + "Could not find foo.range!", + "Could not find baz.range!", + "Could not find foo.range!", + "Could not find foo.range!", + "You must enter a value for baz.", + "baz out of range.", + "You must enter a value for data.", + "You must enter a value for data." + ); } /** * Test to verify WW-3850. - * - * @since 2.3.5 */ public void testBuildsValidatorsForClassError() { // for this test we need to have a file manager with reloadingConfigs to true @@ -188,12 +194,8 @@ public void testBuildsValidatorsForClassError() { } } - /* - // TODO: this all need to be converted to real unit tests - public void testSkipUserMarkerActionLevelShortCircuit() { - // get validators - List validatorList = actionValidatorManager.getValidators(User.class, null); + List validatorList = actionValidatorManager.getValidators(User.class, null); assertEquals(10, validatorList.size()); try { @@ -202,16 +204,17 @@ public void testSkipUserMarkerActionLevelShortCircuit() { user.setEmail("bad_email"); user.setEmail2("bad_email"); - ValidatorContext context = new GenericValidatorContext(user); + ValidationAware validationAware = new SimpleAction(); + ValidatorContext context = new DelegatingValidatorContext(validationAware, actionValidatorManager.textProviderFactory); actionValidatorManager.validate(user, null, context); assertTrue(context.hasFieldErrors()); // check field errors - List l = (List) context.getFieldErrors().get("email"); + List l = context.getFieldErrors().get("email"); assertNotNull(l); assertEquals(1, l.size()); assertEquals("Not a valid e-mail.", l.get(0)); - l = (List) context.getFieldErrors().get("email2"); + l = context.getFieldErrors().get("email2"); assertNotNull(l); assertEquals(2, l.size()); assertEquals("Not a valid e-mail2.", l.get(0)); @@ -219,19 +222,17 @@ public void testSkipUserMarkerActionLevelShortCircuit() { // check action errors assertTrue(context.hasActionErrors()); - l = (List) context.getActionErrors(); + l = new ArrayList<>(context.getActionErrors()); assertNotNull(l); assertEquals(2, l.size()); // both expression test failed see User-validation.xml assertEquals("Email does not start with mark", l.get(0)); } catch (ValidationException ex) { - ex.printStackTrace(); fail("Validation error: " + ex.getMessage()); } } public void testSkipAllActionLevelShortCircuit2() { - // get validators - List validatorList = actionValidatorManager.getValidators(User.class, null); + List validatorList = actionValidatorManager.getValidators(User.class, null); assertEquals(10, validatorList.size()); try { @@ -244,34 +245,30 @@ public void testSkipAllActionLevelShortCircuit2() { user.setEmail("mark_bad_email_for_field_val@foo.com"); user.setEmail2("mark_bad_email_for_field_val@foo.com"); - ValidatorContext context = new GenericValidatorContext(user); + ValidationAware validationAware = new SimpleAction(); + ValidatorContext context = new DelegatingValidatorContext(validationAware, actionValidatorManager.textProviderFactory); actionValidatorManager.validate(user, null, context); assertTrue(context.hasFieldErrors()); // check field errors // we have an error in this field level, email does not ends with mycompany.com - List l = (List) context.getFieldErrors().get("email"); + List l = context.getFieldErrors().get("email"); assertNotNull(l); assertEquals(1, l.size()); // because email-field-val is short-circuit assertEquals("Email not from the right company.", l.get(0)); - // check action errors - l = (List) context.getActionErrors(); + l = new ArrayList<>(context.getActionErrors()); assertFalse(context.hasActionErrors()); assertEquals(0, l.size()); - - } catch (ValidationException ex) { - ex.printStackTrace(); fail("Validation error: " + ex.getMessage()); } } public void testActionLevelShortCircuit() throws Exception { - - List validatorList = actionValidatorManager.getValidators(User.class, null); + List validatorList = actionValidatorManager.getValidators(User.class, null); assertEquals(10, validatorList.size()); User user = new User(); @@ -280,18 +277,18 @@ public void testActionLevelShortCircuit() throws Exception { user.setEmail("tmjee(at)yahoo.co.uk"); user.setEmail("tm_jee(at)yahoo.co.uk"); - ValidatorContext context = new GenericValidatorContext(user); + ValidationAware validationAware = new SimpleAction(); + ValidatorContext context = new DelegatingValidatorContext(validationAware, actionValidatorManager.textProviderFactory); actionValidatorManager.validate(user, null, context); - // check field level errors + // check field level errors // shouldn't have any because action error prevents validation of anything else - List l = (List) context.getFieldErrors().get("email2"); + List l = context.getFieldErrors().get("email2"); assertNull(l); - // check action errors assertTrue(context.hasActionErrors()); - l = (List) context.getActionErrors(); + l = new ArrayList<>(context.getActionErrors()); assertNotNull(l); // we only get one, because UserMarker-validation.xml action-level validator // already sc it :-) @@ -299,10 +296,8 @@ public void testActionLevelShortCircuit() throws Exception { assertEquals("Email not the same as email2", l.get(0)); } - public void testShortCircuitNoErrors() { - // get validators - List validatorList = actionValidatorManager.getValidators(User.class, null); + List validatorList = actionValidatorManager.getValidators(User.class, null); assertEquals(10, validatorList.size()); try { @@ -311,73 +306,72 @@ public void testShortCircuitNoErrors() { user.setEmail("mark@mycompany.com"); user.setEmail2("mark@mycompany.com"); - ValidatorContext context = new GenericValidatorContext(user); + ValidationAware validationAware = new SimpleAction(); + ValidatorContext context = new DelegatingValidatorContext(validationAware, actionValidatorManager.textProviderFactory); actionValidatorManager.validate(user, null, context); assertFalse(context.hasErrors()); } catch (ValidationException ex) { - ex.printStackTrace(); fail("Validation error: " + ex.getMessage()); } } public void testFieldErrorsOrder() throws Exception { - ValidationOrderAction action = new ValidationOrderAction(); - actionValidatorManager.validate(action, "actionContext"); - Map fieldErrors = action.getFieldErrors(); - Iterator i = fieldErrors.entrySet().iterator(); + ValidationOrderAction action = new ValidationOrderAction(); + actionValidatorManager.validate(action, "actionContext"); + Map> fieldErrors = action.getFieldErrors(); + Iterator>> i = fieldErrors.entrySet().iterator(); - assertNotNull(fieldErrors); - assertEquals(fieldErrors.size(), 12); + assertNotNull(fieldErrors); + assertEquals(fieldErrors.size(), 12); - Map.Entry e = (Map.Entry) i.next(); - assertEquals(e.getKey(), "username"); - assertEquals(((List)e.getValue()).get(0), "username required"); + Map.Entry> e = i.next(); + assertEquals(e.getKey(), "username"); + assertEquals(e.getValue().get(0), "username required"); - e = (Map.Entry) i.next(); - assertEquals(e.getKey(), "password"); - assertEquals(((List)e.getValue()).get(0), "password required"); + e = i.next(); + assertEquals(e.getKey(), "password"); + assertEquals((e.getValue()).get(0), "password required"); - e = (Map.Entry) i.next(); - assertEquals(e.getKey(), "confirmPassword"); - assertEquals(((List)e.getValue()).get(0), "confirm password required"); + e = i.next(); + assertEquals(e.getKey(), "confirmPassword"); + assertEquals((e.getValue()).get(0), "confirm password required"); - e = (Map.Entry) i.next(); - assertEquals(e.getKey(), "firstName"); - assertEquals(((List)e.getValue()).get(0), "first name required"); + e = i.next(); + assertEquals(e.getKey(), "firstName"); + assertEquals((e.getValue()).get(0), "first name required"); - e = (Map.Entry) i.next(); - assertEquals(e.getKey(), "lastName"); - assertEquals(((List)e.getValue()).get(0), "last name required"); + e = i.next(); + assertEquals(e.getKey(), "lastName"); + assertEquals((e.getValue()).get(0), "last name required"); - e = (Map.Entry) i.next(); - assertEquals(e.getKey(), "city"); - assertEquals(((List)e.getValue()).get(0), "city is required"); + e = i.next(); + assertEquals(e.getKey(), "city"); + assertEquals((e.getValue()).get(0), "city is required"); - e = (Map.Entry) i.next(); - assertEquals(e.getKey(), "province"); - assertEquals(((List)e.getValue()).get(0), "province is required"); + e = i.next(); + assertEquals(e.getKey(), "province"); + assertEquals((e.getValue()).get(0), "province is required"); - e = (Map.Entry) i.next(); - assertEquals(e.getKey(), "country"); - assertEquals(((List)e.getValue()).get(0), "country is required"); + e = i.next(); + assertEquals(e.getKey(), "country"); + assertEquals((e.getValue()).get(0), "country is required"); - e = (Map.Entry) i.next(); - assertEquals(e.getKey(), "postalCode"); - assertEquals(((List)e.getValue()).get(0), "postal code is required"); + e = i.next(); + assertEquals(e.getKey(), "postalCode"); + assertEquals((e.getValue()).get(0), "postal code is required"); - e = (Map.Entry) i.next(); - assertEquals(e.getKey(), "email"); - assertEquals(((List)e.getValue()).get(0), "email is required"); + e = i.next(); + assertEquals(e.getKey(), "email"); + assertEquals((e.getValue()).get(0), "email is required"); - e = (Map.Entry) i.next(); - assertEquals(e.getKey(), "website"); - assertEquals(((List)e.getValue()).get(0), "website is required"); - - e = (Map.Entry) i.next(); - assertEquals(e.getKey(), "passwordHint"); - assertEquals(((List)e.getValue()).get(0), "password hint is required"); + e = i.next(); + assertEquals(e.getKey(), "website"); + assertEquals((e.getValue()).get(0), "website is required"); + e = i.next(); + assertEquals(e.getKey(), "passwordHint"); + assertEquals((e.getValue()).get(0), "password hint is required"); } - */ + } From 86b14c706284e43239fc30367fbe5b1264855e61 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Jun 2024 01:57:34 +0000 Subject: [PATCH 32/33] Bump org.apache.commons:commons-compress from 1.26.0 to 1.26.2 Bumps org.apache.commons:commons-compress from 1.26.0 to 1.26.2. --- updated-dependencies: - dependency-name: org.apache.commons:commons-compress dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c0dba29803..697cccf2bd 100644 --- a/pom.xml +++ b/pom.xml @@ -1037,7 +1037,7 @@ org.apache.commons commons-compress - 1.26.0 + 1.26.2 From 9133d24f69ee7a7cef70d6c0d6df93c3eef3bff9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Jun 2024 01:57:52 +0000 Subject: [PATCH 33/33] Bump org.owasp:dependency-check-maven from 8.4.2 to 9.2.0 Bumps [org.owasp:dependency-check-maven](https://github.com/jeremylong/DependencyCheck) from 8.4.2 to 9.2.0. - [Release notes](https://github.com/jeremylong/DependencyCheck/releases) - [Changelog](https://github.com/jeremylong/DependencyCheck/blob/main/CHANGELOG.md) - [Commits](https://github.com/jeremylong/DependencyCheck/compare/v8.4.2...v9.2.0) --- updated-dependencies: - dependency-name: org.owasp:dependency-check-maven dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c0dba29803..197c8b3584 100644 --- a/pom.xml +++ b/pom.xml @@ -341,7 +341,7 @@ org.owasp dependency-check-maven - 8.4.2 + 9.2.0 src/etc/project-suppression.xml