Compare commits

...

119 Commits

Author SHA1 Message Date
Lukasz Lenart dbfb59cb7c [maven-release-plugin] prepare release STRUTS_6_5_0 2024-07-12 07:27:08 +02:00
Kusal Kithul-Godage d4a0f4fdde Merge pull request #982 from apache/WW-5428-stop-excessive-logging
WW-5428 Stop excessive logging in DevMode
2024-07-09 05:44:33 +10:00
Kusal Kithul-Godage 9fe1a4a031 WW-5428 Stop excessive logging in DevMode 2024-07-09 05:01:02 +10:00
Lukasz Lenart 3693ea2eec Merge pull request #978 from apache/dependabot/github_actions/actions/upload-artifact-4.3.4
Bump actions/upload-artifact from 4.3.3 to 4.3.4
2024-07-08 13:19:12 +02:00
Lukasz Lenart 7cb57087d3 Merge pull request #976 from apache/dependabot/maven/org.apache.maven.plugins-maven-failsafe-plugin-3.3.0
Bump org.apache.maven.plugins:maven-failsafe-plugin from 3.2.5 to 3.3.0
2024-07-08 13:18:59 +02:00
Kusal Kithul-Godage 398e104fe0 Merge pull request #979 from apache/WW-5439-fix-dev-mode
WW-5439 Move DevMode security configuration to SecurityMemberAccess
2024-07-08 20:18:40 +10:00
Kusal Kithul-Godage 7f57e895a1 Merge pull request #967 from apache/WW-5428-allowlist-hibernate
WW-5428 Allowlist capability should resolve Hibernate proxies when disableProxyObjects is not set
2024-07-08 20:15:38 +10:00
Kusal Kithul-Godage 81b4943176 WW-5439 Move Dev Mode security configuration 2024-07-08 20:01:53 +10:00
Kusal Kithul-Godage 05680d7827 WW-5428 Amend log warning for missing allowlist entry 2024-07-08 19:57:36 +10:00
Kusal Kithul-Godage 8555dc266e WW-5428 Add log warning for allowlist disabled 2024-07-08 19:52:22 +10:00
Kusal Kithul-Godage c6f394a0e8 WW-5428 Add log warning for Hibernate entities 2024-07-08 19:44:05 +10:00
Kusal Kithul-Godage c965812ffe WW-5428 Add unit test coverage for Hibernate proxy resolution 2024-07-08 16:58:07 +10:00
Kusal Kithul-Godage abf03fdccc WW-5428 Clean up SecurityMemberAccessProxyTest 2024-07-08 16:58:07 +10:00
Kusal Kithul-Godage 2f814186c8 WW-5428 Allowlist capability should resolve Hibernate proxies when disableProxyObjects is not set 2024-07-08 16:58:07 +10:00
dependabot[bot] 7762d7fba2 Bump actions/upload-artifact from 4.3.3 to 4.3.4
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.3 to 4.3.4.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/65462800fd760344b1a7b4382951275a0abb4808...0b2256b8c012f0828dc542b3febcab082c67f72b)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-08 01:40:34 +00:00
Lukasz Lenart 82b364d521 Merge pull request #977 from stefansielaff/fix-behavior-of-envsvaluesubstitutor
WW-5437 Swap order of sysStrSubstitutor and envStrSubstitutor in substitute method
2024-07-05 06:52:12 +02:00
stefansielaff 98f2e68e0b "Swap order of sysStrSubstitutor and envStrSubstitutor in substitute method" 2024-07-02 13:24:57 +02:00
dependabot[bot] 40ab7a776a Bump org.apache.maven.plugins:maven-failsafe-plugin from 3.2.5 to 3.3.0
Bumps [org.apache.maven.plugins:maven-failsafe-plugin](https://github.com/apache/maven-surefire) from 3.2.5 to 3.3.0.
- [Release notes](https://github.com/apache/maven-surefire/releases)
- [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.2.5...surefire-3.3.0)

---
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] <support@github.com>
2024-07-01 01:20:28 +00:00
Lukasz Lenart 8b22f7170f Merge pull request #966 from apache/dependabot/maven/org.codehaus.mojo-exec-maven-plugin-3.3.0
Bump org.codehaus.mojo:exec-maven-plugin from 3.2.0 to 3.3.0
2024-06-24 07:46:26 +02:00
Lukasz Lenart 688413a359 Merge pull request #965 from apache/dependabot/maven/org.apache.maven.plugins-maven-enforcer-plugin-3.5.0
Bump org.apache.maven.plugins:maven-enforcer-plugin from 3.4.1 to 3.5.0
2024-06-24 07:46:02 +02:00
Lukasz Lenart 53ed5f6dfa Merge pull request #960 from apache/dependabot/maven/org.apache.felix-org.apache.felix.main-7.0.5
Bump org.apache.felix:org.apache.felix.main from 6.0.3 to 7.0.5
2024-06-24 07:45:09 +02:00
Lukasz Lenart 100ef07a44 Merge pull request #958 from apache/dependabot/maven/commons-validator-commons-validator-1.9.0
Bump commons-validator:commons-validator from 1.8.0 to 1.9.0
2024-06-24 07:42:04 +02:00
Lukasz Lenart 4267bf0647 Merge pull request #971 from apache/feature/WW-5431-deprecated
WW-5431 Marks unused constants as deprecated
2024-06-24 07:40:13 +02:00
Kusal Kithul-Godage 898a8d9ef9 Merge pull request #969 from apache/WW-5429-param-anno-log
WW-5429 Log parameter annotation issues at ERROR level when in DevMode
2024-06-21 19:47:23 +10:00
Lukasz Lenart 75ebbf4367 WW-5431 Marks unused constants as deprecated
To be removed in Struts 7
2024-06-21 08:24:54 +02:00
Lukasz Lenart a8954508c6 Merge pull request #968 from apache/fix/WW-5310-fragment
WW-5310 Fixes broken support for Fragments in <s:url/> tag
2024-06-21 07:28:17 +02:00
Kusal Kithul-Godage ba46c18f07 WW-5429 Make DebugUtils final and remove @author JavaDoc tag 2024-06-21 11:04:10 +10:00
Kusal Kithul-Godage b96cf2c072 WW-5429 Log parameter annotation issues at ERROR level when in DevMode 2024-06-18 19:36:51 +10:00
Lukasz Lenart 13916c8b84 WW-5310 Fixes broken support for Fragments in <s:url/> tag 2024-06-18 09:39:17 +02:00
dependabot[bot] a99162a1a4 Bump org.codehaus.mojo:exec-maven-plugin from 3.2.0 to 3.3.0
Bumps [org.codehaus.mojo:exec-maven-plugin](https://github.com/mojohaus/exec-maven-plugin) from 3.2.0 to 3.3.0.
- [Release notes](https://github.com/mojohaus/exec-maven-plugin/releases)
- [Commits](https://github.com/mojohaus/exec-maven-plugin/compare/3.2.0...3.3.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] <support@github.com>
2024-06-17 01:28:53 +00:00
dependabot[bot] b07268d5bd Bump org.apache.maven.plugins:maven-enforcer-plugin from 3.4.1 to 3.5.0
Bumps [org.apache.maven.plugins:maven-enforcer-plugin](https://github.com/apache/maven-enforcer) from 3.4.1 to 3.5.0.
- [Release notes](https://github.com/apache/maven-enforcer/releases)
- [Commits](https://github.com/apache/maven-enforcer/compare/enforcer-3.4.1...enforcer-3.5.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-enforcer-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-17 01:28:48 +00:00
Lukasz Lenart 444e4d4c07 Merge pull request #957 from apache/fix/WW-5250-todo
WW-5250 Addresses TODO in test and stops using Mock Objects
2024-06-10 07:03:13 +02:00
Lukasz Lenart cf34f0dbc0 Merge pull request #956 from apache/feature/WW-5400-refactor
WW-5400 Simplifies how CspSettings is created
2024-06-10 07:02:49 +02:00
Lukasz Lenart cabc076e48 Merge pull request #962 from apache/dependabot/maven/org.owasp-dependency-check-maven-9.2.0
Bump org.owasp:dependency-check-maven from 8.4.2 to 9.2.0
2024-06-10 07:02:13 +02:00
Lukasz Lenart de2a8d9902 Merge pull request #961 from apache/dependabot/maven/org.apache.commons-commons-compress-1.26.2
Bump org.apache.commons:commons-compress from 1.26.0 to 1.26.2
2024-06-10 07:02:03 +02:00
dependabot[bot] 9133d24f69 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] <support@github.com>
2024-06-10 01:57:52 +00:00
dependabot[bot] 86b14c7062 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] <support@github.com>
2024-06-10 01:57:34 +00:00
dependabot[bot] 54bf309f88 Bump org.apache.felix:org.apache.felix.main from 6.0.3 to 7.0.5
Bumps org.apache.felix:org.apache.felix.main from 6.0.3 to 7.0.5.

---
updated-dependencies:
- dependency-name: org.apache.felix:org.apache.felix.main
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-10 01:57:32 +00:00
dependabot[bot] 63fcf0f14f Bump commons-validator:commons-validator from 1.8.0 to 1.9.0
Bumps commons-validator:commons-validator from 1.8.0 to 1.9.0.

---
updated-dependencies:
- dependency-name: commons-validator:commons-validator
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-10 01:57:26 +00:00
Lukasz Lenart 03db4f1ab0 WW-5250 Addresses TODO in test and stops using Mock Objects 2024-06-07 08:33:08 +02:00
Lukasz Lenart 4a05653a55 Removes duplication
Co-authored-by: Kusal Kithul-Godage <git@kusal.io>
2024-06-06 06:32:30 +02:00
Lukasz Lenart 3a6ad5a557 WW-5400 Simplifies how CspSettings is created 2024-06-05 07:39:59 +02:00
Lukasz Lenart dcaff0bf10 Merge pull request #948 from apache/feature/WW-5412-master
[WW-5412] Upgrades struts-master to ver 15
2024-06-05 07:06:01 +02:00
Lukasz Lenart a25173f65d Merge pull request #947 from apache/fix/no-reviewers-required
Disables required reviewers option
2024-06-05 07:05:33 +02:00
Lukasz Lenart 0cdce05745 Merge pull request #946 from apache/fix/WW-5424-class-cast-exception
[WW-5424] Fixes ClassCastException when using short var name in s:set tag
2024-06-05 07:05:11 +02:00
Lukasz Lenart ee040babd4 Merge pull request #953 from apache/dependabot/maven/org.freemarker-freemarker-2.3.33
WW-5426 Bump org.freemarker:freemarker from 2.3.32 to 2.3.33
2024-06-03 07:04:47 +02:00
Lukasz Lenart f6b25ac8aa Merge pull request #950 from apache/dependabot/maven/org.apache.maven.plugins-maven-assembly-plugin-3.7.1
Bump org.apache.maven.plugins:maven-assembly-plugin from 3.6.0 to 3.7.1
2024-06-03 07:02:27 +02:00
dependabot[bot] 5c2be7cc7d 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] <support@github.com>
2024-06-03 01:37:13 +00:00
dependabot[bot] e5fcc8f175 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] <support@github.com>
2024-06-03 01:34:52 +00:00
Lukasz Lenart 855b95e557 WW-5412 Upgrades struts-master to ver 15 2024-06-02 19:13:01 +02:00
Lukasz Lenart 00752e33c4 Disables required reviewers option
This option doesn't play with Silence Consensus
2024-06-02 13:59:41 +02:00
Lukasz Lenart 4a8ff99b1c WW-5424 Fixes ClassCastException when using short var name in s:set tag 2024-06-02 13:53:37 +02:00
Lukasz Lenart 9682b3b429 Merge pull request #944 from apache/dependabot/maven/jackson.version-2.17.1
WW-5425 Bump jackson.version from 2.16.1 to 2.17.1
2024-06-01 07:05:42 +02:00
Lukasz Lenart 8268d0b063 Merge pull request #940 from apache/dependabot/maven/org.jacoco-jacoco-maven-plugin-0.8.12
Bump org.jacoco:jacoco-maven-plugin from 0.8.11 to 0.8.12
2024-06-01 07:02:24 +02:00
Lukasz Lenart 36230dc5be Merge pull request #945 from apache/fix/disable-review
INFRA-25666 Disables review by code owners
2024-05-20 08:02:51 +02:00
Lukasz Lenart 9ee39d0a2c INFRA-25666 Disables review by code owners
* this functionality requires to have public teams
* currently all ASF teams are private
2024-05-20 07:02:45 +02:00
dependabot[bot] ab89b8287a 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] <support@github.com>
2024-05-20 01:22:13 +00:00
dependabot[bot] 19252adea0 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] <support@github.com>
2024-05-20 01:19:53 +00:00
Lukasz Lenart 40ccc74b8a Merge pull request #933 from apache/fix/WW-5415-constructor
WW-5415 Fixes accessing public constructors via expression
2024-05-14 19:56:27 +02:00
Lukasz Lenart d147543dcd Merge pull request #932 from apache/feature/WW-5414-after
WW-5414 Always call afterInvocation even in case of exception
2024-05-14 19:55:30 +02:00
Lukasz Lenart 49eda37f2d Merge pull request #931 from apache/fix/WW-5422-trimable
WW-5422 Fixes support for trimable locale string in request
2024-05-13 08:23:26 +02:00
Lukasz Lenart 0a57cac6d6 WW-5422 Adds dedicate unit test to cover DefaultLocaleProvider 2024-05-13 07:43:36 +02:00
Lukasz Lenart 195b0e59d0 Merge pull request #939 from apache/dependabot/github_actions/ossf/scorecard-action-2.3.3
Bump ossf/scorecard-action from 2.3.1 to 2.3.3
2024-05-13 06:50:39 +02:00
Lukasz Lenart 03126c01bf Merge pull request #938 from apache/dependabot/maven/org.apache.maven.plugins-maven-project-info-reports-plugin-3.5.0
Bump org.apache.maven.plugins:maven-project-info-reports-plugin from 3.0.0 to 3.5.0
2024-05-13 06:50:20 +02:00
Lukasz Lenart 73be382a96 Merge pull request #936 from apache/dependabot/maven/slf4j.version-2.0.13
Bump slf4j.version from 2.0.12 to 2.0.13
2024-05-13 06:49:43 +02:00
Lukasz Lenart f338fda6d8 Merge pull request #934 from apache/dependabot/maven/org.apache.maven.plugins-maven-source-plugin-3.3.1
Bump org.apache.maven.plugins:maven-source-plugin from 3.3.0 to 3.3.1
2024-05-13 06:48:23 +02:00
Kusal Kithul-Godage 7c523ac33e WW-5415 Constructor members should be exempted as static members 2024-05-13 12:37:04 +10:00
dependabot[bot] 039c5f8d42 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] <support@github.com>
2024-05-13 01:38:25 +00:00
dependabot[bot] 6e12c25ac0 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] <support@github.com>
2024-05-13 01:16:29 +00:00
dependabot[bot] c6b53d4887 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] <support@github.com>
2024-05-13 01:16:15 +00:00
dependabot[bot] 942fbd67e7 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] <support@github.com>
2024-05-13 01:15:24 +00:00
Lukasz Lenart b36e88ff4b WW-5415 Fixes accessing public constructors via expression 2024-05-12 09:47:38 +02:00
Lukasz Lenart 4dfbe09343 WW-5414 Always call afterInvocation even in case of exception 2024-05-11 10:22:27 +02:00
Lukasz Lenart 649760d8b0 Merge pull request #913 from eschulma/master
WW-5400 Extend default configuration options for the CSP interceptor.
2024-05-11 06:36:33 +02:00
Lukasz Lenart 49b8eedf1f Merge pull request #920 from apache/fix/WW-5419-tiles
[WW-5419] Fixes support for loading Tiles definitions
2024-05-06 18:20:54 +02:00
Lukasz Lenart c0d1217d7c Merge pull request #924 from apache/feature/WW-5420-text
WW-5420 Upgrades commons-text to ver. 1.12.0
2024-04-29 06:24:29 +02:00
Lukasz Lenart f2e93d017b Merge pull request #905 from apache/dependabot/maven/org.apache.maven.plugins-maven-failsafe-plugin-3.2.5
Bump org.apache.maven.plugins:maven-failsafe-plugin from 3.0.0-M6 to 3.2.5
2024-04-29 06:23:48 +02:00
Lukasz Lenart 3ed212a1f2 Merge pull request #907 from apache/dependabot/maven/asm.version-9.7
WW-5421 Bump asm.version from 9.6 to 9.7
2024-04-29 06:23:09 +02:00
Lukasz Lenart ed3786c6b0 Merge pull request #926 from apache/dependabot/github_actions/actions/upload-artifact-4.3.3
Bump actions/upload-artifact from 4.3.2 to 4.3.3
2024-04-29 06:21:16 +02:00
Lukasz Lenart 5cd9356049 Merge pull request #925 from apache/dependabot/maven/org.codehaus.mojo-exec-maven-plugin-3.2.0
Bump org.codehaus.mojo:exec-maven-plugin from 3.1.0 to 3.2.0
2024-04-29 06:20:52 +02:00
dependabot[bot] 2d9af47a90 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] <support@github.com>
2024-04-29 01:46:50 +00:00
dependabot[bot] c1c6a463a8 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] <support@github.com>
2024-04-29 01:15:19 +00:00
Lukasz Lenart 36275b9e04 WW-5420 Upgrades commons-text to ver. 1.12.0 2024-04-25 08:25:09 +02:00
Lukasz Lenart a88cb741d0 WW-5419 Fixes support for loading Tiles definitions 2024-04-22 06:49:44 +02:00
Lukasz Lenart 3cde668598 Merge pull request #922 from apache/dependabot/maven/org.codehaus.mojo-versions-maven-plugin-2.16.2
Bump org.codehaus.mojo:versions-maven-plugin from 2.16.1 to 2.16.2
2024-04-22 06:39:33 +02:00
Lukasz Lenart eca5b21088 Merge pull request #923 from apache/dependabot/github_actions/actions/upload-artifact-4.3.2
Bump actions/upload-artifact from 4.3.1 to 4.3.2
2024-04-22 06:36:59 +02:00
dependabot[bot] 856d98010a 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] <support@github.com>
2024-04-22 01:35:16 +00:00
dependabot[bot] b2b35f066c 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] <support@github.com>
2024-04-22 01:23:03 +00:00
Lukasz Lenart efdaf7e50f Merge pull request #909 from apache/dependabot/maven/org.assertj-assertj-core-3.25.3
Bump org.assertj:assertj-core from 3.25.2 to 3.25.3
2024-04-20 15:59:26 +02:00
Kusal Kithul-Godage 3c21e82290 Merge pull request #916 from apache/WW-5418-struts-sec
WW-5418 Forbid Enums and Jasper classes
2024-04-20 20:01:06 +10:00
Kusal Kithul-Godage f5cfb88f1f Merge pull request #915 from atlassian-forks/issue/WW-5417-bump-ongl-version-to-fix-security-issue
WW-5417 bump ognl version to fix security issue
2024-04-19 17:47:40 +10:00
Kusal Kithul-Godage 63267a8e06 Merge pull request #914 from atlassian-forks/issue/WW-5409-introduce-final-attribute-to-package-element
WW-5409 introduce final attribute to package element which make them unextendable
2024-04-19 17:47:02 +10:00
Jeffery Xie 62b4b65c4c WW-5417 update ognl.version as 3.3.5 2024-04-19 14:58:25 +10:00
Kusal Kithul-Godage 4310536799 Merge pull request #917 from apache/WW-5406-injection-order 2024-04-19 08:37:14 +10:00
Kusal Kithul-Godage ad49ea866a WW-5406 Fix injection order issue for excluded patterns 2024-04-18 22:28:30 +10:00
Kusal Kithul-Godage 7e9f6e84f2 WW-5418 Exclude Tomcat Jasper classes 2024-04-18 11:14:10 +10:00
Kusal Kithul-Godage 100f5052d4 WW-5418 Forbid enums 2024-04-18 11:13:56 +10:00
Jeffery Xie 0a720971c7 WW-5417 bump ognl version to fix security issue 2024-04-18 11:02:16 +10:00
Erica S. Kane 6ac8b04ad2 WW-5400 Added @since Struts 6.5.0 to new properties as requested 2024-04-12 13:40:53 -04:00
Kusal Kithul-Godage 1562e66a89 Merge pull request #912 from atlassian-forks/issue/WW-5408-add-option-to-not-fallback-to-empty-namespace-when-unresolved
WW-5408 add option to not fallback to empty namespace when unresolved
2024-04-12 17:58:06 +10:00
Jeffery Xie 85783a0cc7 WW-5409 rename 6.5.0.dtd to 6.5.dtd to follow the naming pattern 2024-04-12 16:04:34 +10:00
Erica S. Kane 0151bdeb9c WW-5400 Better toString formatting 2024-04-11 16:52:44 -04:00
Kusal Kithul-Godage 0aa2f269f2 Merge pull request #911 from atlassian-forks/issue/WW-5407-extend-SecurityMemberAccess-proxy-detection-to-proxies
WW-5407 Extend SecurityMemberAccess proxy detection to other proxies
2024-04-11 19:16:36 +10:00
Jeffery Xie 4088f2ee2b WW-5409 update new dtd from 6.4.0 to 6.5.0 2024-04-11 15:07:25 +10:00
Jeffery Xie 1d51d00ecb WW-5408 add struts.actionConfig.fallbackToEmptyNamespace as true in default.properties 2024-04-11 14:57:57 +10:00
Kusal Kithul-Godage 929a60184b Merge pull request #910 from apache/WW-5406-excluded-patterns
WW-5406 Ensure Action excluded patterns are reinjected
2024-04-11 14:27:31 +10:00
Jeffery Xie bf5f29132b WW-5409 introduce final attribute to package element which make them unextendable 2024-04-11 12:44:53 +10:00
Erica S. Kane 758174c52e 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.
2024-04-10 18:42:09 -04:00
Jeffery Xie f9f6327570 /WW-5408 rename struts.disableActionConfigFallbackToEmptyNamespace to struts.actionConfig.fallbackToEmptyNamespace 2024-04-10 17:30:02 +10:00
Kusal Kithul-Godage ed0c7287d9 WW-5406 Revert breaking API change 2024-04-10 16:01:47 +10:00
Kusal Kithul-Godage 1a6e2fef7a WW-5406 Add deprecation JavaDocs 2024-04-10 15:55:02 +10:00
Jeffery Xie 5f717cdb89 WW-5407 tweak ProxyUtil#isHibernateProxyMember to make it neat 2024-04-10 15:00:50 +10:00
Jeffery Xie e2ec114577 WW-5408 add option to not fallback to empty namespace when unresolved 2024-04-09 15:33:10 +10:00
Jeffery Xie 0074b70286 WW-5407 extend SecurityMemberAccess proxy detection to other proxies 2024-04-09 14:46:45 +10:00
Kusal Kithul-Godage efa20426c9 WW-5406 Ensure Action excluded patterns are reinjected 2024-04-09 10:42:12 +10:00
dependabot[bot] 268ed789ee 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] <support@github.com>
2024-04-08 01:22:47 +00:00
dependabot[bot] ccd5f5d534 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] <support@github.com>
2024-04-08 01:22:36 +00:00
dependabot[bot] ac409d6cba 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] <support@github.com>
2024-04-08 01:20:48 +00:00
Lukasz Lenart c6d13f1016 [maven-release-plugin] prepare for next development iteration 2024-04-07 11:22:16 +02:00
111 changed files with 2496 additions and 714 deletions
+3 -2
View File
@@ -18,8 +18,9 @@ github:
contexts:
- build
required_pull_request_reviews:
require_code_owner_reviews: true
required_approving_review_count: 1
# it does not work because our github teams are private/secret, see INFRA-25666
require_code_owner_reviews: false
required_approving_review_count: 0
autolink_jira:
- WW
dependabot_alerts: true
+2 -2
View File
@@ -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
@@ -57,7 +57,7 @@ jobs:
publish_results: true
- name: "Upload artifact"
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # 4.3.1
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # 4.3.4
with:
name: SARIF file
path: results.sarif
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-apps</artifactId>
<packaging>pom</packaging>
+2 -2
View File
@@ -24,12 +24,12 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-apps</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-rest-showcase</artifactId>
<packaging>war</packaging>
<version>6.4.0</version>
<version>6.5.0</version>
<name>Struts 2 Rest Showcase Webapp</name>
<description>Struts 2 Rest Showcase Example</description>
+2 -2
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-apps</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-showcase</artifactId>
@@ -157,7 +157,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.0.0-M6</version>
<version>3.3.0</version>
<configuration>
<includes>
<include>it.org.apache.struts2.showcase.*Test</include>
+2 -2
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-assembly</artifactId>
@@ -106,7 +106,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.6.0</version>
<version>3.7.1</version>
<executions>
<execution>
<id>make-assembly</id>
+4 -4
View File
@@ -25,11 +25,11 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-bom</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
<packaging>pom</packaging>
<name>Struts 2 Bill of Materials</name>
@@ -44,7 +44,7 @@
</licenses>
<properties>
<struts-version.version>6.4.0</struts-version.version>
<struts-version.version>6.5.0</struts-version.version>
<maven.site.skip>true</maven.site.skip>
<maven.site.deploy.skip>true</maven.site.deploy.skip>
</properties>
@@ -190,7 +190,7 @@
</dependencyManagement>
<scm>
<tag>STRUTS_6_4_0</tag>
<tag>STRUTS_6_5_0</tag>
<connection>scm:git:https://gitbox.apache.org/repos/asf/struts.git</connection>
<developerConnection>scm:git:https://gitbox.apache.org/repos/asf/struts.git</developerConnection>
<url>https://github.com/apache/struts/</url>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-osgi-bundles</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-osgi-admin-bundle</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-osgi-bundles</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-osgi-demo-bundle</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-osgi-bundles</artifactId>
+16 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-core</artifactId>
<packaging>jar</packaging>
@@ -230,6 +230,15 @@
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
</dependency>
<!-- Optional used in com.opensymphony.xwork2.util.ProxyUtil to detect if object is HibernateProxy -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.6.15.Final</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
@@ -251,6 +260,12 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
@@ -90,6 +90,11 @@ public class ActionSupport implements Action, Validateable, ValidationAware, Tex
return getLocaleProvider().isValidLocale(locale);
}
@Override
public Locale toLocale(String localeStr) {
return getLocaleProvider().toLocale(localeStr);
}
@Override
public boolean hasKey(String key) {
return getTextProvider().hasKey(key);
@@ -46,17 +46,23 @@ public class DefaultLocaleProvider implements LocaleProvider {
@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;
}
}
@@ -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;
}
}
}
@@ -36,6 +36,8 @@ import org.apache.commons.lang3.ClassUtils;
import java.util.Locale;
import java.util.Map;
import static java.util.Collections.singletonMap;
/**
* Base JUnit TestCase to extend for XWork specific JUnit tests. Uses
* the generic test setup for logic.
@@ -56,9 +58,7 @@ public abstract class XWorkTestCase extends TestCase {
@Override
protected void setUp() throws Exception {
configurationManager = XWorkTestCaseHelper.setUp();
configuration = configurationManager.getConfiguration();
container = configuration.getContainer();
actionProxyFactory = container.getInstance(ActionProxyFactory.class);
reloadConfiguration(configurationManager);
}
@Override
@@ -66,13 +66,17 @@ public abstract class XWorkTestCase extends TestCase {
XWorkTestCaseHelper.tearDown(configurationManager);
}
protected void loadConfigurationProviders(ConfigurationProvider... providers) {
configurationManager = XWorkTestCaseHelper.loadConfigurationProviders(configurationManager, providers);
private void reloadConfiguration(ConfigurationManager configurationManager) {
configuration = configurationManager.getConfiguration();
container = configuration.getContainer();
actionProxyFactory = container.getInstance(ActionProxyFactory.class);
}
protected void loadConfigurationProviders(ConfigurationProvider... providers) {
configurationManager = XWorkTestCaseHelper.loadConfigurationProviders(configurationManager, providers);
reloadConfiguration(configurationManager);
}
protected void loadButSet(Map<String, ?> properties) {
loadConfigurationProviders(new StubConfigurationProvider() {
@Override
@@ -115,4 +119,25 @@ public abstract class XWorkTestCase extends TestCase {
.getContextMap();
}
protected void setStrutsConstant(String constant, String value) {
setStrutsConstant(singletonMap(constant, value));
}
protected void setStrutsConstant(final Map<String, String> overwritePropeties) {
configurationManager.addContainerProvider(new StubConfigurationProvider() {
@Override
public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {
for (Map.Entry<String, String> stringStringEntry : overwritePropeties.entrySet()) {
props.setProperty(stringStringEntry.getKey(), stringStringEntry.getValue(), null);
}
}
@Override
public void destroy() {
}
});
configurationManager.reload();
reloadConfiguration(configurationManager);
}
}
@@ -47,6 +47,7 @@ public class PackageConfig extends Located implements Comparable<PackageConfig>,
protected String name;
protected String namespace = "";
protected boolean isAbstract = false;
protected boolean isFinal = false; // a final package is unextendable
protected boolean needsRefresh;
protected boolean strictMethodInvocation = true;
@@ -69,6 +70,7 @@ public class PackageConfig extends Located implements Comparable<PackageConfig>,
this.name = orig.name;
this.namespace = orig.namespace;
this.isAbstract = orig.isAbstract;
this.isFinal = orig.isFinal;
this.needsRefresh = orig.needsRefresh;
this.actionConfigs = new LinkedHashMap<>(orig.actionConfigs);
this.globalResultConfigs = new LinkedHashMap<>(orig.globalResultConfigs);
@@ -85,6 +87,10 @@ public class PackageConfig extends Located implements Comparable<PackageConfig>,
return isAbstract;
}
public boolean isFinal() {
return isFinal;
}
public Map<String, ActionConfig> getActionConfigs() {
return actionConfigs;
}
@@ -360,6 +366,7 @@ public class PackageConfig extends Located implements Comparable<PackageConfig>,
PackageConfig that = (PackageConfig) o;
if (isAbstract != that.isAbstract) return false;
if (isFinal != that.isFinal) return false;
if (needsRefresh != that.needsRefresh) return false;
if (strictMethodInvocation != that.strictMethodInvocation) return false;
if (actionConfigs != null ? !actionConfigs.equals(that.actionConfigs) : that.actionConfigs != null)
@@ -404,6 +411,7 @@ public class PackageConfig extends Located implements Comparable<PackageConfig>,
result = 31 * result + name.hashCode();
result = 31 * result + (namespace != null ? namespace.hashCode() : 0);
result = 31 * result + (isAbstract ? 1 : 0);
result = 31 * result + (isFinal ? 1 : 0);
result = 31 * result + (needsRefresh ? 1 : 0);
result = 31 * result + (strictMethodInvocation ? 1 : 0);
return result;
@@ -453,6 +461,11 @@ public class PackageConfig extends Located implements Comparable<PackageConfig>,
return this;
}
public Builder isFinal(boolean isFinal) {
target.isFinal = isFinal;
return this;
}
public Builder defaultInterceptorRef(String name) {
target.defaultInterceptorRef = name;
return this;
@@ -120,6 +120,7 @@ import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
@@ -459,9 +460,12 @@ public class DefaultConfiguration implements Configuration {
boolean appendNamedParameters = Boolean.parseBoolean(
container.getInstance(String.class, StrutsConstants.STRUTS_MATCHER_APPEND_NAMED_PARAMETERS)
);
boolean fallbackToEmptyNamespace = Boolean.parseBoolean(
Optional.ofNullable(container.getInstance(String.class, StrutsConstants.STRUTS_ACTION_CONFIG_FALLBACK_TO_EMPTY_NAMESPACE)).orElse("true")
);
return new RuntimeConfigurationImpl(Collections.unmodifiableMap(namespaceActionConfigs),
Collections.unmodifiableMap(namespaceConfigs), matcher, appendNamedParameters);
Collections.unmodifiableMap(namespaceConfigs), matcher, appendNamedParameters, fallbackToEmptyNamespace);
}
private void setDefaultResults(Map<String, ResultConfig> results, PackageConfig packageContext) {
@@ -536,14 +540,17 @@ public class DefaultConfiguration implements Configuration {
private final Map<String, ActionConfigMatcher> namespaceActionConfigMatchers;
private final NamespaceMatcher namespaceMatcher;
private final Map<String, String> namespaceConfigs;
private final boolean fallbackToEmptyNamespace;
public RuntimeConfigurationImpl(Map<String, Map<String, ActionConfig>> namespaceActionConfigs,
Map<String, String> namespaceConfigs,
PatternMatcher<int[]> matcher,
boolean appendNamedParameters)
boolean appendNamedParameters,
boolean fallbackToEmptyNamespace)
{
this.namespaceActionConfigs = namespaceActionConfigs;
this.namespaceConfigs = namespaceConfigs;
this.fallbackToEmptyNamespace = fallbackToEmptyNamespace;
this.namespaceActionConfigMatchers = new LinkedHashMap<>();
this.namespaceMatcher = new NamespaceMatcher(matcher, namespaceActionConfigs.keySet(), appendNamedParameters);
@@ -583,14 +590,17 @@ public class DefaultConfiguration implements Configuration {
}
// fail over to empty namespace
if (config == null && StringUtils.isNotBlank(namespace)) {
if (config == null && shouldFallbackToEmptyNamespace(namespace)) {
config = findActionConfigInNamespace("", name);
}
return config;
}
private boolean shouldFallbackToEmptyNamespace(String namespace) {
return StringUtils.isNotBlank(namespace) && ("/".equals(namespace) || fallbackToEmptyNamespace);
}
private ActionConfig findActionConfigInNamespace(String namespace, String name) {
ActionConfig config = null;
if (namespace == null) {
@@ -46,7 +46,7 @@ public class EnvsValueSubstitutor implements ValueSubstitutor {
public String substitute(String value) {
LOG.debug("Substituting value {} with proper System variable or environment variable", value);
String substituted = sysStrSubstitutor.replace(value);
return envStrSubstitutor.replace(substituted);
String substituted = envStrSubstitutor.replace(value);
return sysStrSubstitutor.replace(substituted);
}
}
@@ -603,8 +603,8 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
*/
protected PackageConfig.Builder buildPackageContext(Element packageElement) {
String parent = packageElement.getAttribute("extends");
String abstractVal = packageElement.getAttribute("abstract");
boolean isAbstract = parseBoolean(abstractVal);
boolean isAbstract = parseBoolean(packageElement.getAttribute("abstract"));
boolean isFinal = parseBoolean(packageElement.getAttribute("final"));
String name = defaultString(packageElement.getAttribute("name"));
String namespace = defaultString(packageElement.getAttribute("namespace"));
@@ -617,6 +617,7 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
PackageConfig.Builder cfg = new PackageConfig.Builder(name)
.namespace(namespace)
.isAbstract(isAbstract)
.isFinal(isFinal)
.strictMethodInvocation(strictDMI)
.location(DomHelper.getLocationObject(packageElement));
@@ -627,17 +628,23 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
// has parents, let's look it up
List<PackageConfig> parents = new ArrayList<>();
for (String parentPackageName : ConfigurationUtil.buildParentListFromString(parent)) {
if (configuration.getPackageConfigNames().contains(parentPackageName)) {
parents.add(configuration.getPackageConfig(parentPackageName));
} else if (declaredPackages.containsKey(parentPackageName)) {
if (configuration.getPackageConfig(parentPackageName) == null) {
addPackage(declaredPackages.get(parentPackageName));
boolean isParentPackageConfigDefined = false;
if (configuration.getPackageConfigNames().contains(parentPackageName)) { // parent package already added to configuration
isParentPackageConfigDefined = true;
} else if (declaredPackages.containsKey(parentPackageName)) { // parent package declared but yet added to configuration
addPackage(declaredPackages.get(parentPackageName));
isParentPackageConfigDefined = true;
}
if (isParentPackageConfigDefined) {
PackageConfig parentPackageConfig = configuration.getPackageConfig(parentPackageName);
if (parentPackageConfig.isFinal()) {
throw new ConfigurationException("Parent package is final and unextendable: " + parentPackageName);
}
parents.add(configuration.getPackageConfig(parentPackageName));
parents.add(parentPackageConfig);
} else {
throw new ConfigurationException("Parent package is not defined: " + parentPackageName);
}
}
if (parents.isEmpty()) {
@@ -25,8 +25,6 @@ import java.util.Map;
/**
* ValidationAware classes can accept Action (class level) or field level error messages. Action level messages are kept
* in a Collection. Field level error messages are kept in a Map from String field name to a List of field error msgs.
*
* @author plightbo
*/
public interface ValidationAware {
@@ -119,7 +117,9 @@ public interface ValidationAware {
*
* @return <code>(hasActionErrors() || hasFieldErrors())</code>
*/
boolean hasErrors();
default boolean hasErrors() {
return hasActionErrors() || hasFieldErrors();
}
/**
* Check whether there are any field errors associated with this action.
@@ -32,6 +32,7 @@ public class DefaultOgnlCacheFactory<Key, Value> implements OgnlCacheFactory<Key
private CacheType defaultCacheType;
private int cacheMaxSize;
private final int initialCapacity;
/**
* @deprecated since 6.4.0, use {@link #DefaultOgnlCacheFactory(int, CacheType)}
@@ -42,13 +43,18 @@ public class DefaultOgnlCacheFactory<Key, Value> implements OgnlCacheFactory<Key
}
public DefaultOgnlCacheFactory(int cacheMaxSize, CacheType defaultCacheType) {
this(cacheMaxSize, defaultCacheType, DEFAULT_INIT_CAPACITY);
}
public DefaultOgnlCacheFactory(int cacheMaxSize, CacheType defaultCacheType, int initialCapacity) {
this.cacheMaxSize = cacheMaxSize;
this.defaultCacheType = defaultCacheType;
this.initialCapacity = initialCapacity;
}
@Override
public OgnlCache<Key, Value> buildOgnlCache() {
return buildOgnlCache(getCacheMaxSize(), DEFAULT_INIT_CAPACITY, DEFAULT_LOAD_FACTOR, defaultCacheType);
return buildOgnlCache(getCacheMaxSize(), initialCapacity, DEFAULT_LOAD_FACTOR, defaultCacheType);
}
@Override
@@ -33,7 +33,7 @@ public class ErrorMessageBuilder {
}
public ErrorMessageBuilder errorSettingExpressionWithValue(String expr, Object value) {
appenExpression(expr);
appendExpression(expr);
if (value instanceof Object[]) {
appendValueAsArray((Object[]) value, message);
} else {
@@ -42,7 +42,7 @@ public class ErrorMessageBuilder {
return this;
}
private void appenExpression(String expr) {
private void appendExpression(String expr) {
message.append("Error setting expression '");
message.append(expr);
message.append("' with value ");
@@ -47,7 +47,6 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Pattern;
import static com.opensymphony.xwork2.util.ConfigParseUtil.toClassesSet;
@@ -68,9 +67,6 @@ public class OgnlUtil {
private static final Logger LOG = LogManager.getLogger(OgnlUtil.class);
// Flag used to reduce flooding logs with WARNs about using DevMode excluded packages
private final AtomicBoolean warnReported = new AtomicBoolean(false);
private final OgnlCache<String, Object> expressionCache;
private final OgnlCache<Class<?>, BeanInfo> beanInfoCache;
private TypeConverter defaultConverter;
@@ -80,11 +76,6 @@ public class OgnlUtil {
private boolean enableExpressionCache = true;
private boolean enableEvalExpression;
private String devModeExcludedClasses = "";
private String devModeExcludedPackageNamePatterns = "";
private String devModeExcludedPackageNames = "";
private String devModeExcludedPackageExemptClasses = "";
private Container container;
/**
@@ -164,9 +155,12 @@ public class OgnlUtil {
// Must be set directly on SecurityMemberAccess
}
@Inject(value = StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_CLASSES, required = false)
/**
* @deprecated since 6.5.0, no replacement.
*/
@Deprecated
protected void setDevModeExcludedClasses(String commaDelimitedClasses) {
this.devModeExcludedClasses = commaDelimitedClasses;
// Must be set directly on SecurityMemberAccess
}
/**
@@ -177,9 +171,12 @@ public class OgnlUtil {
// Must be set directly on SecurityMemberAccess
}
@Inject(value = StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_PACKAGE_NAME_PATTERNS, required = false)
/**
* @deprecated since 6.5.0, no replacement.
*/
@Deprecated
protected void setDevModeExcludedPackageNamePatterns(String commaDelimitedPackagePatterns) {
this.devModeExcludedPackageNamePatterns = commaDelimitedPackagePatterns;
// Must be set directly on SecurityMemberAccess
}
/**
@@ -190,9 +187,12 @@ public class OgnlUtil {
// Must be set directly on SecurityMemberAccess
}
@Inject(value = StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_PACKAGE_NAMES, required = false)
/**
* @deprecated since 6.5.0, no replacement.
*/
@Deprecated
protected void setDevModeExcludedPackageNames(String commaDelimitedPackageNames) {
this.devModeExcludedPackageNames = commaDelimitedPackageNames;
// Must be set directly on SecurityMemberAccess
}
/**
@@ -203,9 +203,12 @@ public class OgnlUtil {
// Must be set directly on SecurityMemberAccess
}
@Inject(value = StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_PACKAGE_EXEMPT_CLASSES, required = false)
/**
* @deprecated since 6.5.0, no replacement.
*/
@Deprecated
public void setDevModeExcludedPackageExemptClasses(String commaDelimitedClasses) {
this.devModeExcludedPackageExemptClasses = commaDelimitedClasses;
// Must be set directly on SecurityMemberAccess
}
/**
@@ -856,6 +859,11 @@ public class OgnlUtil {
return createDefaultContext(root, null);
}
/**
* Note that the allowlist capability is not enforced by the {@link OgnlContext} returned by this method. Currently,
* this context is only leveraged by some public methods on {@link OgnlUtil} which are called by
* {@link OgnlReflectionProvider}.
*/
protected Map<String, Object> createDefaultContext(Object root, ClassResolver resolver) {
if (resolver == null) {
resolver = container.getInstance(RootAccessor.class);
@@ -867,17 +875,6 @@ public class OgnlUtil {
SecurityMemberAccess memberAccess = container.getInstance(SecurityMemberAccess.class);
memberAccess.useEnforceAllowlistEnabled(Boolean.FALSE.toString());
if (devMode) {
if (!warnReported.get()) {
warnReported.set(true);
LOG.warn("Working in devMode, using devMode excluded classes and packages!");
}
memberAccess.useExcludedClasses(devModeExcludedClasses);
memberAccess.useExcludedPackageNamePatterns(devModeExcludedPackageNamePatterns);
memberAccess.useExcludedPackageNames(devModeExcludedPackageNames);
memberAccess.useExcludedPackageExemptClasses(devModeExcludedPackageExemptClasses);
}
return Ognl.createDefaultContext(root, memberAccess, resolver, defaultConverter);
}
@@ -29,9 +29,9 @@ import org.apache.struts2.ognl.ProviderAllowlist;
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.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashSet;
@@ -51,6 +51,8 @@ import static java.text.MessageFormat.format;
import static java.util.Collections.emptySet;
import static java.util.Collections.singletonList;
import static java.util.Collections.unmodifiableSet;
import static org.apache.struts2.StrutsConstants.STRUTS_ALLOWLIST_CLASSES;
import static org.apache.struts2.StrutsConstants.STRUTS_ALLOWLIST_PACKAGE_NAMES;
/**
* Allows access decisions to be made on the basis of whether a member is static or not.
@@ -77,16 +79,29 @@ public class SecurityMemberAccess implements MemberAccess {
private final ProviderAllowlist providerAllowlist;
private final ThreadAllowlist threadAllowlist;
private boolean allowStaticFieldAccess = true;
private Set<Pattern> excludeProperties = emptySet();
private Set<Pattern> acceptProperties = emptySet();
private Set<String> excludedClasses = unmodifiableSet(new HashSet<>(singletonList(Object.class.getName())));
private Set<Pattern> excludedPackageNamePatterns = emptySet();
private Set<String> excludedPackageNames = emptySet();
private Set<String> excludedPackageExemptClasses = emptySet();
private volatile boolean isDevModeInit;
private boolean isDevMode;
private Set<String> devModeExcludedClasses = unmodifiableSet(new HashSet<>(singletonList(Object.class.getName())));
private Set<Pattern> devModeExcludedPackageNamePatterns = emptySet();
private Set<String> devModeExcludedPackageNames = emptySet();
private Set<String> devModeExcludedPackageExemptClasses = emptySet();
private boolean enforceAllowlistEnabled = false;
private Set<Class<?>> allowlistClasses = emptySet();
private Set<String> allowlistPackageNames = emptySet();
private boolean disallowProxyObjectAccess = false;
private boolean disallowProxyMemberAccess = false;
private boolean disallowDefaultPackageAccess = false;
@@ -147,11 +162,11 @@ public class SecurityMemberAccess implements MemberAccess {
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)) {
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
@@ -160,6 +175,11 @@ public class SecurityMemberAccess implements MemberAccess {
}
}
if (!checkProxyObjectAccess(target)) {
LOG.warn("Access to proxy is blocked! Target [{}], proxy class [{}]", target, target.getClass().getName());
return false;
}
if (!checkProxyMemberAccess(target, member)) {
LOG.warn("Access to proxy is blocked! Member class [{}] of target [{}], member [{}]", member.getDeclaringClass(), target, member);
return false;
@@ -203,12 +223,27 @@ public class SecurityMemberAccess implements MemberAccess {
* @return {@code true} if member access is allowed
*/
protected boolean checkAllowlist(Object target, Member member) {
Class<?> memberClass = member.getDeclaringClass();
if (!enforceAllowlistEnabled) {
return true;
}
if (!disallowProxyObjectAccess && target != null && ProxyUtil.isProxy(target)) {
// If `disallowProxyObjectAccess` is not set, allow resolving Hibernate entities to their underlying
// classes/members. This allows the allowlist capability to continue working and offer some level of
// protection in applications where the developer has accepted the risk of allowing OGNL access to Hibernate
// entities. This is preferred to having to disable the allowlist capability entirely.
Object newTarget = ProxyUtil.getHibernateProxyTarget(target);
if (newTarget != target) {
logAllowlistHibernateEntity(target, newTarget);
target = newTarget;
member = ProxyUtil.resolveTargetMember(member, newTarget);
}
}
Class<?> memberClass = member.getDeclaringClass();
if (!isClassAllowlisted(memberClass)) {
LOG.warn(format("Declaring class [{0}] of member type [{1}] is not allowlisted!", memberClass, member));
LOG.warn("Declaring class [{}] of member type [{}] is not allowlisted! Add to '{}' or '{}' configuration.",
memberClass, member, STRUTS_ALLOWLIST_CLASSES, STRUTS_ALLOWLIST_PACKAGE_NAMES);
return false;
}
if (target == null || target.getClass() == memberClass) {
@@ -216,12 +251,27 @@ public class SecurityMemberAccess implements MemberAccess {
}
Class<?> targetClass = target.getClass();
if (!isClassAllowlisted(targetClass)) {
LOG.warn(format("Target class [{0}] of target [{1}] is not allowlisted!", targetClass, target));
LOG.warn("Target class [{}] of target [{}] is not allowlisted! Add to '{}' or '{}' configuration.",
targetClass, target, STRUTS_ALLOWLIST_CLASSES, STRUTS_ALLOWLIST_PACKAGE_NAMES);
return false;
}
return true;
}
private void logAllowlistHibernateEntity(Object original, Object resolved) {
if (!isDevMode && !LOG.isDebugEnabled()) {
return;
}
String msg = "Hibernate entity [{}] resolved to [{}] for purpose of OGNL allowlisting." +
" We don't recommend executing OGNL expressions against Hibernate entities, you may disallow this behaviour using the configuration `{}=true`.";
Object[] args = {original, resolved, StrutsConstants.STRUTS_DISALLOW_PROXY_OBJECT_ACCESS};
if (isDevMode) {
LOG.warn(msg, args);
} else {
LOG.debug(msg, args);
}
}
protected boolean isClassAllowlisted(Class<?> clazz) {
return allowlistClasses.contains(clazz)
|| ALLOWLIST_REQUIRED_CLASSES.contains(clazz)
@@ -235,6 +285,7 @@ public class SecurityMemberAccess implements MemberAccess {
* @return {@code true} if member access is allowed
*/
protected boolean checkExclusionList(Object target, Member member) {
useDevModeConfiguration();
Class<?> memberClass = member.getDeclaringClass();
if (isClassExcluded(memberClass)) {
LOG.warn("Declaring class of member type [{}] is excluded!", memberClass);
@@ -286,7 +337,14 @@ public class SecurityMemberAccess implements MemberAccess {
}
/**
* @return {@code true} if member access is allowed
* @return {@code true} if proxy object access is allowed
*/
protected boolean checkProxyObjectAccess(Object target) {
return !(disallowProxyObjectAccess && ProxyUtil.isProxy(target));
}
/**
* @return {@code true} if proxy member access is allowed
*/
protected boolean checkProxyMemberAccess(Object target, Member member) {
return !(disallowProxyMemberAccess && ProxyUtil.isProxyMember(member, target));
@@ -300,10 +358,6 @@ public class SecurityMemberAccess implements MemberAccess {
* @return {@code true} if member access is allowed
*/
protected boolean checkStaticMethodAccess(Member member) {
if (checkEnumAccess(member)) {
LOG.trace("Exempting Enum#values from static method check: class [{}]", member.getDeclaringClass());
return true;
}
return member instanceof Field || !isStatic(member);
}
@@ -334,17 +388,6 @@ public class SecurityMemberAccess implements MemberAccess {
return Modifier.isPublic(member.getModifiers());
}
/**
* @return {@code true} if member access is allowed
*/
protected boolean checkEnumAccess(Member member) {
return member.getDeclaringClass().isEnum()
&& isStatic(member)
&& member instanceof Method
&& member.getName().equals("values")
&& ((Method) member).getParameterCount() == 0;
}
protected boolean isPackageExcluded(Class<?> clazz) {
return !excludedPackageExemptClasses.contains(clazz.getName()) && (isExcludedPackageNames(clazz) || isExcludedPackageNamePatterns(clazz));
}
@@ -438,16 +481,21 @@ public class SecurityMemberAccess implements MemberAccess {
this.enforceAllowlistEnabled = BooleanUtils.toBoolean(enforceAllowlistEnabled);
}
@Inject(value = StrutsConstants.STRUTS_ALLOWLIST_CLASSES, required = false)
@Inject(value = STRUTS_ALLOWLIST_CLASSES, required = false)
public void useAllowlistClasses(String commaDelimitedClasses) {
this.allowlistClasses = toClassObjectsSet(commaDelimitedClasses);
}
@Inject(value = StrutsConstants.STRUTS_ALLOWLIST_PACKAGE_NAMES, required = false)
@Inject(value = STRUTS_ALLOWLIST_PACKAGE_NAMES, required = false)
public void useAllowlistPackageNames(String commaDelimitedPackageNames) {
this.allowlistPackageNames = toPackageNamesSet(commaDelimitedPackageNames);
}
@Inject(value = StrutsConstants.STRUTS_DISALLOW_PROXY_OBJECT_ACCESS, required = false)
public void useDisallowProxyObjectAccess(String disallowProxyObjectAccess) {
this.disallowProxyObjectAccess = BooleanUtils.toBoolean(disallowProxyObjectAccess);
}
@Inject(value = StrutsConstants.STRUTS_DISALLOW_PROXY_MEMBER_ACCESS, required = false)
public void useDisallowProxyMemberAccess(String disallowProxyMemberAccess) {
this.disallowProxyMemberAccess = BooleanUtils.toBoolean(disallowProxyMemberAccess);
@@ -457,4 +505,41 @@ public class SecurityMemberAccess implements MemberAccess {
public void useDisallowDefaultPackageAccess(String disallowDefaultPackageAccess) {
this.disallowDefaultPackageAccess = BooleanUtils.toBoolean(disallowDefaultPackageAccess);
}
@Inject(StrutsConstants.STRUTS_DEVMODE)
protected void useDevMode(String devMode) {
this.isDevMode = BooleanUtils.toBoolean(devMode);
}
@Inject(value = StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_CLASSES, required = false)
public void useDevModeExcludedClasses(String commaDelimitedClasses) {
this.devModeExcludedClasses = toNewClassesSet(devModeExcludedClasses, commaDelimitedClasses);
}
@Inject(value = StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_PACKAGE_NAME_PATTERNS, required = false)
public void useDevModeExcludedPackageNamePatterns(String commaDelimitedPackagePatterns) {
this.devModeExcludedPackageNamePatterns = toNewPatternsSet(devModeExcludedPackageNamePatterns, commaDelimitedPackagePatterns);
}
@Inject(value = StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_PACKAGE_NAMES, required = false)
public void useDevModeExcludedPackageNames(String commaDelimitedPackageNames) {
this.devModeExcludedPackageNames = toNewPackageNamesSet(devModeExcludedPackageNames, commaDelimitedPackageNames);
}
@Inject(value = StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_PACKAGE_EXEMPT_CLASSES, required = false)
public void useDevModeExcludedPackageExemptClasses(String commaDelimitedClasses) {
this.devModeExcludedPackageExemptClasses = toClassesSet(commaDelimitedClasses);
}
private void useDevModeConfiguration() {
if (!isDevMode || isDevModeInit) {
return;
}
isDevModeInit = true;
LOG.warn("Working in devMode, using devMode excluded classes and packages!");
excludedClasses = devModeExcludedClasses;
excludedPackageNamePatterns = devModeExcludedPackageNamePatterns;
excludedPackageNames = devModeExcludedPackageNames;
excludedPackageExemptClasses = devModeExcludedPackageExemptClasses;
}
}
@@ -0,0 +1,42 @@
/*
* 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.util;
import com.opensymphony.xwork2.TextProvider;
import com.opensymphony.xwork2.interceptor.ValidationAware;
import org.apache.logging.log4j.Logger;
/**
* @since 6.5.0
*/
public final class DebugUtils {
public static void notifyDeveloperOfError(Logger log, Object action, String message) {
if (action instanceof TextProvider) {
TextProvider tp = (TextProvider) action;
message = tp.getText("devmode.notification", "Developer Notification:\n{0}", new String[]{message});
}
log.error(message);
if (action instanceof ValidationAware) {
ValidationAware validationAware = (ValidationAware) action;
validationAware.addActionError(message);
}
}
}
@@ -18,13 +18,23 @@
*/
package com.opensymphony.xwork2.util;
import com.opensymphony.xwork2.ognl.DefaultOgnlCacheFactory;
import com.opensymphony.xwork2.ognl.OgnlCache;
import com.opensymphony.xwork2.ognl.OgnlCacheFactory;
import org.apache.commons.lang3.reflect.ConstructorUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.hibernate.Hibernate;
import org.hibernate.proxy.HibernateProxy;
import java.lang.reflect.*;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import static java.lang.reflect.Modifier.isPublic;
/**
* <code>ProxyUtil</code>
@@ -38,11 +48,13 @@ public class ProxyUtil {
private static final String SPRING_SPRINGPROXY_CLASS_NAME = "org.springframework.aop.SpringProxy";
private static final String SPRING_SINGLETONTARGETSOURCE_CLASS_NAME = "org.springframework.aop.target.SingletonTargetSource";
private static final String SPRING_TARGETCLASSAWARE_CLASS_NAME = "org.springframework.aop.TargetClassAware";
private static final Map<Class<?>, Boolean> isProxyCache =
new ConcurrentHashMap<>(256);
private static final Map<Member, Boolean> isProxyMemberCache =
new ConcurrentHashMap<>(256);
private static final String HIBERNATE_HIBERNATEPROXY_CLASS_NAME = "org.hibernate.proxy.HibernateProxy";
private static final int CACHE_MAX_SIZE = 10000;
private static final int CACHE_INITIAL_CAPACITY = 256;
private static final OgnlCache<Class<?>, Boolean> isProxyCache = new DefaultOgnlCacheFactory<Class<?>, Boolean>(
CACHE_MAX_SIZE, OgnlCacheFactory.CacheType.WTLFU, CACHE_INITIAL_CAPACITY).buildOgnlCache();
private static final OgnlCache<Member, Boolean> isProxyMemberCache = new DefaultOgnlCacheFactory<Member, Boolean>(
CACHE_MAX_SIZE, OgnlCacheFactory.CacheType.WTLFU, CACHE_INITIAL_CAPACITY).buildOgnlCache();
/**
* Determine the ultimate target class of the given instance, traversing
@@ -75,7 +87,7 @@ public class ProxyUtil {
return flag;
}
boolean isProxy = isSpringAopProxy(object);
boolean isProxy = isSpringAopProxy(object) || isHibernateProxy(object);
isProxyCache.put(clazz, isProxy);
return isProxy;
@@ -87,7 +99,7 @@ public class ProxyUtil {
* @param object the object to check
*/
public static boolean isProxyMember(Member member, Object object) {
if (!Modifier.isStatic(member.getModifiers()) && !isProxy(object)) {
if (!Modifier.isStatic(member.getModifiers()) && !isProxy(object) && !isHibernateProxy(object)) {
return false;
}
@@ -96,12 +108,40 @@ public class ProxyUtil {
return flag;
}
boolean isProxyMember = isSpringProxyMember(member);
boolean isProxyMember = isSpringProxyMember(member) || isHibernateProxyMember(member);
isProxyMemberCache.put(member, isProxyMember);
return isProxyMember;
}
/**
* Check whether the given object is a Hibernate proxy.
*
* @param object the object to check
*/
public static boolean isHibernateProxy(Object object) {
try {
return HibernateProxy.class.isAssignableFrom(object.getClass());
} catch (NoClassDefFoundError ignored) {
return false;
}
}
/**
* Check whether the given member is a member of a Hibernate proxy.
*
* @param member the member to check
*/
public static boolean isHibernateProxyMember(Member member) {
try {
Class<?> clazz = ClassLoaderUtil.loadClass(HIBERNATE_HIBERNATEPROXY_CLASS_NAME, ProxyUtil.class);
return hasMember(clazz, member);
} catch (ClassNotFoundException ignored) {
}
return false;
}
/**
* Determine the ultimate target class of the given spring bean instance, traversing
* not only a top-level spring proxy but any number of nested spring proxies as well &mdash;
@@ -218,4 +258,34 @@ public class ProxyUtil {
return false;
}
/**
* @return the target instance of the given object if it is a Hibernate proxy object, otherwise the given object
*/
public static Object getHibernateProxyTarget(Object object) {
try {
return Hibernate.unproxy(object);
} catch (NoClassDefFoundError ignored) {
return object;
}
}
/**
* @return matching member on target object if one exists, otherwise the same member
*/
public static Member resolveTargetMember(Member proxyMember, Object target) {
int mod = proxyMember.getModifiers();
if (proxyMember instanceof Method) {
if (isPublic(mod)) {
return MethodUtils.getMatchingAccessibleMethod(target.getClass(), proxyMember.getName(), ((Method) proxyMember).getParameterTypes());
} else {
return MethodUtils.getMatchingMethod(target.getClass(), proxyMember.getName(), ((Method) proxyMember).getParameterTypes());
}
} else if (proxyMember instanceof Field) {
return FieldUtils.getField(target.getClass(), proxyMember.getName(), isPublic(mod));
} else if (proxyMember instanceof Constructor && isPublic(mod)) {
return ConstructorUtils.getMatchingAccessibleConstructor(target.getClass(), ((Constructor<?>) proxyMember).getParameterTypes());
}
return proxyMember;
}
}
@@ -36,7 +36,7 @@ public interface ActionValidatorManager {
* @param method the name of the method being invoked on the action - can be <tt>null</tt>.
* @return a list of all validators for the given class and context.
*/
List<Validator> getValidators(Class clazz, String context, String method);
List<Validator> 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 <tt>null</tt>.
* @return a list of all validators for the given class and context.
*/
List<Validator> getValidators(Class clazz, String context);
List<Validator> getValidators(Class<?> clazz, String context);
/**
* Validates the given object using action and its context.
@@ -125,7 +125,7 @@ public class DefaultActionValidatorManager implements ActionValidatorManager {
* @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 @@ public class DefaultActionValidatorManager implements ActionValidatorManager {
}
@Override
public synchronized List<Validator> getValidators(Class clazz, String context, String method) {
public synchronized List<Validator> getValidators(Class<?> clazz, String context, String method) {
String validatorKey = buildValidatorKey(clazz, context);
if (!validatorCache.containsKey(validatorKey)) {
@@ -158,7 +158,7 @@ public class DefaultActionValidatorManager implements ActionValidatorManager {
}
@Override
public synchronized List<Validator> getValidators(Class clazz, String context) {
public synchronized List<Validator> getValidators(Class<?> clazz, String context) {
return getValidators(clazz, context, null);
}
@@ -277,7 +277,7 @@ public class DefaultActionValidatorManager implements ActionValidatorManager {
* @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<ValidatorConfig> buildValidatorConfigs(Class clazz, String context, boolean checkFile, Set<String> checked) {
protected List<ValidatorConfig> buildValidatorConfigs(Class<?> clazz, String context, boolean checkFile, Set<String> checked) {
List<ValidatorConfig> validatorConfigs = new ArrayList<>();
if (checked == null) {
@@ -287,7 +287,7 @@ public class DefaultActionValidatorManager implements ActionValidatorManager {
}
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 @@ public class DefaultActionValidatorManager implements ActionValidatorManager {
}
// 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 @@ public class DefaultActionValidatorManager implements ActionValidatorManager {
return validatorConfigs;
}
protected List<ValidatorConfig> buildAliasValidatorConfigs(Class aClass, String context, boolean checkFile) {
protected List<ValidatorConfig> buildAliasValidatorConfigs(Class<?> aClass, String context, boolean checkFile) {
String fileName = aClass.getName().replace('.', '/') + "-" + context + VALIDATION_CONFIG_SUFFIX;
return loadFile(fileName, aClass, checkFile);
}
protected List<ValidatorConfig> buildClassValidatorConfigs(Class aClass, boolean checkFile) {
protected List<ValidatorConfig> buildClassValidatorConfigs(Class<?> aClass, boolean checkFile) {
String fileName = aClass.getName().replace('.', '/') + VALIDATION_CONFIG_SUFFIX;
return loadFile(fileName, aClass, checkFile);
}
protected List<ValidatorConfig> loadFile(String fileName, Class clazz, boolean checkFile) {
protected List<ValidatorConfig> loadFile(String fileName, Class<?> clazz, boolean checkFile) {
List<ValidatorConfig> retList = Collections.emptyList();
URL fileUrl = ClassLoaderUtil.getResource(fileName, clazz);
@@ -122,10 +122,15 @@ public class DelegatingValidatorContext implements ValidatorContext {
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 class DelegatingValidatorContext implements ValidatorContext {
public boolean isValidLocale(Locale locale) {
return getLocaleProvider().isValidLocale(locale);
}
@Override
public Locale toLocale(String localeStr) {
return getLocaleProvider().toLocale(localeStr);
}
}
/**
@@ -230,6 +230,8 @@ public final class StrutsConstants {
public static final String STRUTS_XWORKCONVERTER = "struts.xworkConverter";
public static final String STRUTS_ALWAYS_SELECT_FULL_NAMESPACE = "struts.mapper.alwaysSelectFullNamespace";
/** Fallback to empty namespace when request namespace didn't match any in action configuration */
public static final String STRUTS_ACTION_CONFIG_FALLBACK_TO_EMPTY_NAMESPACE = "struts.actionConfig.fallbackToEmptyNamespace";
/** The {@link com.opensymphony.xwork2.LocaleProviderFactory} implementation class */
public static final String STRUTS_LOCALE_PROVIDER_FACTORY = "struts.localeProviderFactory";
@@ -480,6 +482,7 @@ public final class StrutsConstants {
public static final String STRUTS_TEXT_PROVIDER_FACTORY = "struts.textProviderFactory";
public static final String STRUTS_LOCALIZED_TEXT_PROVIDER = "struts.localizedTextProvider";
public static final String STRUTS_DISALLOW_PROXY_OBJECT_ACCESS = "struts.disallowProxyObjectAccess";
public static final String STRUTS_DISALLOW_PROXY_MEMBER_ACCESS = "struts.disallowProxyMemberAccess";
public static final String STRUTS_DISALLOW_DEFAULT_PACKAGE_ACCESS = "struts.disallowDefaultPackageAccess";
@@ -103,9 +103,9 @@ public class ServletUrlRenderer implements UrlRenderer {
}
result = urlHelper.buildUrl(_value, urlComponent.getHttpServletRequest(), urlComponent.getHttpServletResponse(), urlComponent.getParameters(), scheme, urlComponent.isIncludeContext(), urlComponent.isEncode(), urlComponent.isForceAddSchemeHostAndPort(), urlComponent.isEscapeAmp());
}
String anchor = urlComponent.getAnchor();
if (StringUtils.isNotEmpty(anchor)) {
result += '#' + urlComponent.findString(anchor);
if (StringUtils.isNotEmpty(urlComponent.getAnchor())) {
String anchor = urlComponent.findString(urlComponent.getAnchor());
result += '#' + anchor;
}
if (urlComponent.isPutInContext()) {
@@ -292,7 +292,7 @@ public class ServletUrlRenderer implements UrlRenderer {
private void includeGetParameters(UrlProvider urlComponent) {
String query = extractQueryString(urlComponent);
QueryStringParser.Result result = queryStringParser.parse(query);
mergeRequestParameters(urlComponent.getValue(), urlComponent.getParameters(), result.getQueryParams());
result = mergeRequestParameters(urlComponent.getValue(), urlComponent.getParameters(), result.getQueryParams());
if (!result.getQueryFragment().isEmpty()) {
urlComponent.setAnchor(result.getQueryFragment());
}
@@ -331,10 +331,11 @@ public class ServletUrlRenderer implements UrlRenderer {
* @param value the value attribute (URL to be generated by this component)
* @param parameters component parameters
* @param contextParameters request parameters
* @return {@link QueryStringParser.Result} of value's ?query-string or empty()
*/
protected void mergeRequestParameters(String value, Map<String, Object> parameters, Map<String, ?> contextParameters) {
protected QueryStringParser.Result mergeRequestParameters(String value, Map<String, Object> parameters, Map<String, ?> contextParameters) {
Map<String, Object> mergedParams = new LinkedHashMap<>(contextParameters);
QueryStringParser.Result result = queryStringParser.empty();
// Merge contextParameters (from current request) with parameters specified in value attribute
// eg. value="someAction.action?id=someId&venue=someVenue"
@@ -343,7 +344,8 @@ public class ServletUrlRenderer implements UrlRenderer {
if (StringUtils.contains(value, "?")) {
String queryString = value.substring(value.indexOf('?') + 1);
mergedParams = new LinkedHashMap<>(queryStringParser.parse(queryString).getQueryParams());
result = queryStringParser.parse(queryString);
mergedParams = new LinkedHashMap<>(result.getQueryParams());
for (Map.Entry<String, ?> entry : contextParameters.entrySet()) {
if (!mergedParams.containsKey(entry.getKey())) {
mergedParams.put(entry.getKey(), entry.getValue());
@@ -362,6 +364,8 @@ public class ServletUrlRenderer implements UrlRenderer {
parameters.put(entry.getKey(), entry.getValue());
}
}
return result;
}
}
@@ -104,17 +104,17 @@ public class Set extends ContextBean {
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);
@@ -54,6 +54,7 @@ public class StrutsXmlConfigurationProvider extends XmlConfigurationProvider {
put("-//Apache Software Foundation//DTD Struts Configuration 2.3//EN", "struts-2.3.dtd");
put("-//Apache Software Foundation//DTD Struts Configuration 2.5//EN", "struts-2.5.dtd");
put("-//Apache Software Foundation//DTD Struts Configuration 6.0//EN", "struts-6.0.dtd");
put("-//Apache Software Foundation//DTD Struts Configuration 6.5//EN", "struts-6.5.dtd");
}});
private File baseDir = null;
private final String filename;
@@ -90,6 +90,7 @@ public class ConstantConfig {
private Boolean freemarkerWrapperAltMap;
private BeanConfig xworkConverter;
private Boolean mapperAlwaysSelectFullNamespace;
private Boolean actionConfigFallbackToEmptyNamespace;
private BeanConfig localeProviderFactory;
private String mapperIdParameterName;
private Boolean ognlAllowStaticFieldAccess;
@@ -145,6 +146,7 @@ public class ConstantConfig {
private String strictMethodInvocationMethodRegex;
private BeanConfig textProviderFactory;
private BeanConfig localizedTextProvider;
private Boolean disallowProxyObjectAccess;
private Boolean disallowProxyMemberAccess;
private Integer ognlAutoGrowthCollectionLimit;
private String staticContentPath;
@@ -225,6 +227,7 @@ public class ConstantConfig {
map.put(StrutsConstants.STRUTS_FREEMARKER_WRAPPER_ALT_MAP, Objects.toString(freemarkerWrapperAltMap, null));
map.put(StrutsConstants.STRUTS_XWORKCONVERTER, beanConfToString(xworkConverter));
map.put(StrutsConstants.STRUTS_ALWAYS_SELECT_FULL_NAMESPACE, Objects.toString(mapperAlwaysSelectFullNamespace, null));
map.put(StrutsConstants.STRUTS_ACTION_CONFIG_FALLBACK_TO_EMPTY_NAMESPACE, Objects.toString(actionConfigFallbackToEmptyNamespace, null));
map.put(StrutsConstants.STRUTS_LOCALE_PROVIDER_FACTORY, beanConfToString(localeProviderFactory));
map.put(StrutsConstants.STRUTS_ID_PARAMETER_NAME, mapperIdParameterName);
map.put(StrutsConstants.STRUTS_ALLOW_STATIC_FIELD_ACCESS, Objects.toString(ognlAllowStaticFieldAccess, null));
@@ -279,6 +282,7 @@ public class ConstantConfig {
map.put(StrutsConstants.STRUTS_SMI_METHOD_REGEX, strictMethodInvocationMethodRegex);
map.put(StrutsConstants.STRUTS_TEXT_PROVIDER_FACTORY, beanConfToString(textProviderFactory));
map.put(StrutsConstants.STRUTS_LOCALIZED_TEXT_PROVIDER, beanConfToString(localizedTextProvider));
map.put(StrutsConstants.STRUTS_DISALLOW_PROXY_OBJECT_ACCESS, Objects.toString(disallowProxyObjectAccess, null));
map.put(StrutsConstants.STRUTS_DISALLOW_PROXY_MEMBER_ACCESS, Objects.toString(disallowProxyMemberAccess, null));
map.put(StrutsConstants.STRUTS_OGNL_AUTO_GROWTH_COLLECTION_LIMIT, Objects.toString(ognlAutoGrowthCollectionLimit, null));
map.put(StrutsConstants.STRUTS_UI_STATIC_CONTENT_PATH, Objects.toString(staticContentPath, StaticContentLoader.DEFAULT_STATIC_CONTENT_PATH));
@@ -812,6 +816,14 @@ public class ConstantConfig {
this.mapperAlwaysSelectFullNamespace = mapperAlwaysSelectFullNamespace;
}
public Boolean getActionConfigFallbackToEmptyNamespace() {
return actionConfigFallbackToEmptyNamespace;
}
public void setActionConfigFallbackToEmptyNamespace(Boolean actionConfigFallbackToEmptyNamespace) {
this.actionConfigFallbackToEmptyNamespace = actionConfigFallbackToEmptyNamespace;
}
public BeanConfig getLocaleProviderFactory() {
return localeProviderFactory;
}
@@ -1360,6 +1372,14 @@ public class ConstantConfig {
this.localizedTextProvider = new BeanConfig(clazz, clazz.getName());
}
public Boolean getDisallowProxyObjectAccess() {
return disallowProxyObjectAccess;
}
public void setDisallowProxyObjectAccess(Boolean disallowProxyObjectAccess) {
this.disallowProxyObjectAccess = disallowProxyObjectAccess;
}
public Boolean getDisallowProxyMemberAccess() {
return disallowProxyMemberAccess;
}
@@ -78,6 +78,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
@@ -88,6 +89,10 @@ import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.regex.Pattern;
import static java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableList;
import static java.util.stream.Collectors.toList;
/**
* A utility class the actual dispatcher delegates most of its tasks to. Each instance
* of the primary dispatcher holds an instance of this dispatcher to be shared for
@@ -162,6 +167,10 @@ public class Dispatcher {
*/
private Pattern multipartValidationPattern = Pattern.compile(MULTIPART_FORM_DATA_REGEX);
private String actionExcludedPatternsStr;
private String actionExcludedPatternsSeparator = ",";
private List<Pattern> actionExcludedPatterns;
/**
* Provide list of default configuration files.
*/
@@ -340,6 +349,33 @@ public class Dispatcher {
this.multipartValidationPattern = Pattern.compile(multipartValidationRegex);
}
@Inject(value = StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN_SEPARATOR, required = false)
public void setActionExcludedPatternsSeparator(String separator) {
this.actionExcludedPatternsSeparator = separator;
}
@Inject(value = StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN, required = false)
public void setActionExcludedPatterns(String excludedPatterns) {
this.actionExcludedPatternsStr = excludedPatterns;
}
public List<Pattern> getActionExcludedPatterns() {
if (actionExcludedPatterns == null) {
initActionExcludedPatterns();
}
return actionExcludedPatterns;
}
private void initActionExcludedPatterns() {
if (actionExcludedPatternsStr == null || actionExcludedPatternsStr.trim().isEmpty()) {
actionExcludedPatterns = emptyList();
return;
}
actionExcludedPatterns = unmodifiableList(
Arrays.stream(actionExcludedPatternsStr.split(actionExcludedPatternsSeparator))
.map(String::trim).map(Pattern::compile).collect(toList()));
}
@Inject
public void setValueStackFactory(ValueStackFactory valueStackFactory) {
this.valueStackFactory = valueStackFactory;
@@ -19,10 +19,7 @@
package org.apache.struts2.dispatcher;
import com.opensymphony.xwork2.ActionContext;
import org.apache.struts2.StrutsConstants;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -100,27 +97,11 @@ public class InitOperations {
* @param dispatcher The dispatcher to check for exclude pattern configuration
* @return a List of Patterns for request to exclude if apply, or <tt>null</tt>
* @see org.apache.struts2.StrutsConstants#STRUTS_ACTION_EXCLUDE_PATTERN
* @deprecated since 6.4.0, use {@link Dispatcher#getActionExcludedPatterns()} instead.
*/
@Deprecated
public List<Pattern> buildExcludedPatternsList(Dispatcher dispatcher) {
String excludePatterns = dispatcher.getContainer().getInstance(String.class, StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN);
String separator = dispatcher.getContainer().getInstance(String.class, StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN_SEPARATOR);
if (separator == null) {
separator = ",";
}
return buildExcludedPatternsList(excludePatterns, separator);
}
private List<Pattern> buildExcludedPatternsList(String patterns, String separator) {
if (null != patterns && patterns.trim().length() != 0) {
List<Pattern> list = new ArrayList<>();
String[] tokens = patterns.split(separator);
for (String token : tokens) {
list.add(Pattern.compile(token.trim()));
}
return Collections.unmodifiableList(list);
} else {
return null;
}
return dispatcher.getActionExcludedPatterns();
}
}
@@ -223,21 +223,23 @@ public class PrepareOperations {
* Check whether the request matches a list of exclude patterns.
*
* @param request The request to check patterns against
* @param excludedPatterns list of patterns for exclusion
*
* @return <tt>true</tt> if the request URI matches one of the given patterns
*/
public boolean isUrlExcluded(HttpServletRequest request) {
String uri = RequestUtils.getUri(request);
return dispatcher.getActionExcludedPatterns().stream().anyMatch(pattern -> pattern.matcher(uri).matches());
}
/**
* @deprecated since 6.4.0, use {@link #isUrlExcluded(HttpServletRequest)} instead.
*/
@Deprecated
public boolean isUrlExcluded(HttpServletRequest request, List<Pattern> excludedPatterns) {
if (excludedPatterns == null) {
return false;
}
String uri = RequestUtils.getUri(request);
for (Pattern pattern : excludedPatterns) {
if (pattern.matcher(uri).matches()) {
return true;
}
}
return false;
return excludedPatterns.stream().anyMatch(pattern -> pattern.matcher(uri).matches());
}
/**
@@ -50,6 +50,12 @@ public class StrutsPrepareAndExecuteFilter implements StrutsStatics, Filter {
protected PrepareOperations prepare;
protected ExecuteOperations execute;
/**
* @deprecated since 6.4.0, use {@link Dispatcher#getActionExcludedPatterns} or
* {@link PrepareOperations#isUrlExcluded(HttpServletRequest)} instead.
*/
@Deprecated
protected List<Pattern> excludedPatterns;
public void init(FilterConfig filterConfig) throws ServletException {
@@ -62,7 +68,7 @@ public class StrutsPrepareAndExecuteFilter implements StrutsStatics, Filter {
prepare = createPrepareOperations(dispatcher);
execute = createExecuteOperations(dispatcher);
// Note: Currently, excluded patterns are not refreshed following an XWork config reload
this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
postInit(dispatcher, filterConfig);
@@ -121,7 +127,7 @@ public class StrutsPrepareAndExecuteFilter implements StrutsStatics, Filter {
try {
prepare.trackRecursion(request);
String uri = RequestUtils.getUri(request);
if (prepare.isUrlExcluded(request, excludedPatterns)) {
if (prepare.isUrlExcluded(request)) {
LOG.trace("Request: {} is excluded from handling by Struts, passing request to other filters", uri);
chain.doFilter(request, response);
} else {
@@ -43,6 +43,12 @@ public class StrutsPrepareFilter implements StrutsStatics, Filter {
protected static final String REQUEST_EXCLUDED_FROM_ACTION_MAPPING = StrutsPrepareFilter.class.getName() + ".REQUEST_EXCLUDED_FROM_ACTION_MAPPING";
protected PrepareOperations prepare;
/**
* @deprecated since 6.4.0, use {@link Dispatcher#getActionExcludedPatterns} or
* {@link PrepareOperations#isUrlExcluded(HttpServletRequest)} instead.
*/
@Deprecated
protected List<Pattern> excludedPatterns;
public void init(FilterConfig filterConfig) throws ServletException {
@@ -53,7 +59,7 @@ public class StrutsPrepareFilter implements StrutsStatics, Filter {
dispatcher = init.initDispatcher(config);
prepare = createPrepareOperations(dispatcher);
// Note: Currently, excluded patterns are not refreshed following an XWork config reload
this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
postInit(dispatcher, filterConfig);
@@ -102,7 +108,7 @@ public class StrutsPrepareFilter implements StrutsStatics, Filter {
boolean didWrap = false;
try {
prepare.trackRecursion(request);
if (prepare.isUrlExcluded(request, excludedPatterns)) {
if (prepare.isUrlExcluded(request)) {
request.setAttribute(REQUEST_EXCLUDED_FROM_ACTION_MAPPING, true);
} else {
request.setAttribute(REQUEST_EXCLUDED_FROM_ACTION_MAPPING, false);
@@ -24,7 +24,6 @@ import com.opensymphony.xwork2.LocaleProviderFactory;
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 class I18nInterceptor extends AbstractInterceptor {
}
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 @@ public class I18nInterceptor extends AbstractInterceptor {
}
/**
* 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 @@ public class I18nInterceptor extends AbstractInterceptor {
@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();
@@ -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;
@@ -46,6 +48,9 @@ public final class CspInterceptor extends AbstractInterceptor {
private boolean prependServletContext = true;
private boolean enforcingMode;
private String reportUri;
private String reportTo;
private String cspSettingsClassName = DefaultCspSettings.class.getName();
@Override
public String intercept(ActionInvocation invocation) throws Exception {
@@ -54,12 +59,30 @@ public final class CspInterceptor extends AbstractInterceptor {
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: {}", cspSettingsClassName, action);
CspSettings cspSettings = createCspSettings(invocation);
applySettings(invocation, cspSettings);
}
return invocation.invoke();
}
private CspSettings createCspSettings(ActionInvocation invocation) throws ClassNotFoundException {
Class<?> cspSettingsClass;
try {
cspSettingsClass = ClassLoaderUtil.loadClass(cspSettingsClassName, getClass());
} catch (ClassNotFoundException e) {
throw new ConfigurationException(String.format("The class %s doesn't exist!", cspSettingsClassName));
}
if (!CspSettings.class.isAssignableFrom(cspSettingsClass)) {
throw new ConfigurationException(String.format("The class %s doesn't implement %s!",
cspSettingsClassName, CspSettings.class.getName()));
}
return (CspSettings) invocation.getInvocationContext().getContainer().inject(cspSettingsClass);
}
private void applySettings(ActionInvocation invocation, CspSettings cspSettings) {
HttpServletRequest request = invocation.getInvocationContext().getServletRequest();
HttpServletResponse response = invocation.getInvocationContext().getServletResponse();
@@ -76,6 +99,12 @@ public final class CspInterceptor extends AbstractInterceptor {
}
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 +126,17 @@ public final class CspInterceptor extends AbstractInterceptor {
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;
}
private Optional<URI> buildUri(String reportUri) {
try {
return Optional.of(URI.create(reportUri));
@@ -124,4 +164,13 @@ public final class CspInterceptor extends AbstractInterceptor {
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}.
*
* @since Struts 6.5.0
*/
public void setCspSettingsClassName(String cspSettingsClassName) {
this.cspSettingsClassName = cspSettingsClassName;
}
}
@@ -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,13 @@ public interface CspSettings {
*/
void setReportUri(String uri);
/**
* Sets the report group where csp violation reports will be sent
*
* @since Struts 6.5.0
*/
void setReportTo(String group);
/**
* Sets CSP headers in enforcing mode when true, and report-only when false
*/
@@ -20,6 +20,7 @@ package org.apache.struts2.interceptor.csp;
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 @@ import static java.lang.String.format;
/**
* Default implementation of {@link CspSettings}.
* The default policy implements strict CSP with a nonce based approach and follows the guide: <a href="https://csp.withgoogle.com/docs/index.html">https://csp.withgoogle.com/docs/index.html/</a>
* The default policy implements strict CSP with a nonce based approach and follows the guide:
* <a href="https://csp.withgoogle.com/docs/index.html">https://csp.withgoogle.com/docs/index.html/</a>
* 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 @@ public class DefaultCspSettings implements CspSettings {
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 @@ public class DefaultCspSettings implements CspSettings {
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 @@ public class DefaultCspSettings implements CspSettings {
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 + '\'' +
'}';
}
@@ -20,6 +20,8 @@ package org.apache.struts2.interceptor.exec;
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 class StrutsBackgroundProcess implements BackgroundProcess, Serializable
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);
@@ -20,10 +20,8 @@ package org.apache.struts2.interceptor.parameter;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.TextProvider;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
import com.opensymphony.xwork2.interceptor.ValidationAware;
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
import com.opensymphony.xwork2.security.DefaultAcceptedPatternsChecker;
import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
@@ -56,7 +54,6 @@ import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Map;
@@ -67,6 +64,8 @@ import java.util.regex.Pattern;
import static com.opensymphony.xwork2.security.DefaultAcceptedPatternsChecker.NESTING_CHARS;
import static com.opensymphony.xwork2.security.DefaultAcceptedPatternsChecker.NESTING_CHARS_STR;
import static com.opensymphony.xwork2.util.DebugUtils.notifyDeveloperOfError;
import static java.lang.String.format;
import static java.util.Collections.unmodifiableSet;
import static java.util.stream.Collectors.joining;
import static org.apache.commons.lang3.StringUtils.indexOfAny;
@@ -317,19 +316,8 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
}
protected void notifyDeveloperParameterException(Object action, String property, String message) {
String logMsg = "Unexpected Exception caught setting '" + property + "' on '" + action.getClass() + ": " + message;
if (action instanceof TextProvider) {
TextProvider tp = (TextProvider) action;
logMsg = tp.getText("devmode.notification", "Developer Notification:\n{0}", new String[]{logMsg});
}
LOG.error(logMsg);
if (action instanceof ValidationAware) {
ValidationAware validationAware = (ValidationAware) action;
Collection<String> messages = validationAware.getActionMessages();
messages.add(message);
validationAware.setActionMessages(messages);
}
String logMsg = format("Unexpected Exception caught setting '%s' on '%s: %s", property, action.getClass(), message);
notifyDeveloperOfError(LOG, action, logMsg);
}
/**
@@ -388,23 +376,37 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
return hasValidAnnotatedField(action, rootProperty, paramDepth);
}
if (hasValidAnnotatedPropertyDescriptor(propDescOpt.get(), paramDepth)) {
if (hasValidAnnotatedPropertyDescriptor(action, propDescOpt.get(), paramDepth)) {
return true;
}
return hasValidAnnotatedField(action, rootProperty, paramDepth);
}
/**
* @deprecated since 6.5.0, use {@link #hasValidAnnotatedPropertyDescriptor(Object, PropertyDescriptor, long)}
* instead.
*/
@Deprecated
protected boolean hasValidAnnotatedPropertyDescriptor(PropertyDescriptor propDesc, long paramDepth) {
return hasValidAnnotatedPropertyDescriptor(null, propDesc, paramDepth);
}
protected boolean hasValidAnnotatedPropertyDescriptor(Object action, PropertyDescriptor propDesc, long paramDepth) {
Method relevantMethod = paramDepth == 0 ? propDesc.getWriteMethod() : propDesc.getReadMethod();
if (relevantMethod == null) {
return false;
}
if (getPermittedInjectionDepth(relevantMethod) < paramDepth) {
LOG.debug(
"Parameter injection for method [{}] on action [{}] rejected. Ensure it is annotated with @StrutsParameter with an appropriate 'depth'.",
String logMessage = format(
"Parameter injection for method [%s] on action [%s] rejected. Ensure it is annotated with @StrutsParameter with an appropriate 'depth'.",
relevantMethod.getName(),
relevantMethod.getDeclaringClass().getName());
if (devMode) {
notifyDeveloperOfError(LOG, action, logMessage);
} else {
LOG.debug(logMessage);
}
return false;
}
if (paramDepth >= 1) {
@@ -455,10 +457,15 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
return false;
}
if (getPermittedInjectionDepth(field) < paramDepth) {
LOG.debug(
"Parameter injection for field [{}] on action [{}] rejected. Ensure it is annotated with @StrutsParameter with an appropriate 'depth'.",
String logMessage = format(
"Parameter injection for field [%s] on action [%s] rejected. Ensure it is annotated with @StrutsParameter with an appropriate 'depth'.",
fieldName,
action.getClass().getName());
if (devMode) {
notifyDeveloperOfError(LOG, action, logMessage);
} else {
LOG.debug(logMessage);
}
return false;
}
if (paramDepth >= 1) {
@@ -533,7 +540,7 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
return "NONE";
}
return parameters.entrySet().stream()
.map(entry -> String.format("%s => %s ", entry.getKey(), entry.getValue().getValue()))
.map(entry -> format("%s => %s ", entry.getKey(), entry.getValue().getValue()))
.collect(joining());
}
@@ -118,9 +118,6 @@ public class FreemarkerManager {
public static final String INITPARAM_DEBUG = "Debug";
public static final String KEY_REQUEST = "Request";
public static final String KEY_INCLUDE = "include_page";
public static final String KEY_REQUEST_PRIVATE = "__FreeMarkerServlet.Request__";
public static final String KEY_REQUEST_PARAMETERS = "RequestParameters";
public static final String KEY_SESSION = "Session";
public static final String KEY_APPLICATION = "Application";
public static final String KEY_APPLICATION_PRIVATE = "__FreeMarkerServlet.Application__";
@@ -138,10 +135,29 @@ public class FreemarkerManager {
// for Struts
public static final String KEY_REQUEST_PARAMETERS_STRUTS = "Parameters";
public static final String KEY_HASHMODEL_PRIVATE = "__FreeMarkerManager.Request__";
public static final String EXPIRATION_DATE;
/**
* @deprecated since Struts 6.5.0, do not use as it will be removed in Struts 7.0.0
*/
@Deprecated
public static final String KEY_INCLUDE = "include_page";
/**
* @deprecated since Struts 6.5.0, do not use as it will be removed in Struts 7.0.0
*/
@Deprecated
public static final String KEY_REQUEST_PRIVATE = "__FreeMarkerServlet.Request__";
/**
* @deprecated since Struts 6.5.0, do not use as it will be removed in Struts 7.0.0
*/
@Deprecated
public static final String KEY_REQUEST_PARAMETERS = "RequestParameters";
/**
* @deprecated since Struts 6.5.0, do not use as it will be removed in Struts 7.0.0
*/
@Deprecated
public static final String KEY_HASHMODEL_PRIVATE = "__FreeMarkerManager.Request__";
/**
* Adds individual settings.
*
@@ -215,6 +215,9 @@ struts.xslt.nocache=false
### Whether to always select the namespace to be everything before the last slash or not
struts.mapper.alwaysSelectFullNamespace=false
### Whether to fallback to empty namespace when request namespace does not match any in configuration
struts.actionConfig.fallbackToEmptyNamespace=true
### Whether to allow static field access in OGNL expressions or not
struts.ognl.allowStaticFieldAccess=true
+158
View File
@@ -0,0 +1,158 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
-->
<!-- START SNIPPET: strutsDtd -->
<!--
Struts configuration DTD.
Use the following DOCTYPE
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.5//EN"
"https://struts.apache.org/dtds/struts-6.5.dtd">
-->
<!ELEMENT struts ((package|include|bean|constant)*,bean-selection?, unknown-handler-stack?)>
<!ATTLIST struts
order CDATA #IMPLIED
>
<!ELEMENT package (result-types?, interceptors?, default-interceptor-ref?, default-action-ref?, default-class-ref?, global-results?, global-allowed-methods?, global-exception-mappings?, action*)>
<!ATTLIST package
name CDATA #REQUIRED
extends CDATA #IMPLIED
namespace CDATA #IMPLIED
abstract CDATA #IMPLIED
final CDATA #IMPLIED
strict-method-invocation (true|false) "true"
>
<!ELEMENT result-types (result-type+)>
<!ELEMENT result-type (param*)>
<!ATTLIST result-type
name CDATA #REQUIRED
class CDATA #REQUIRED
default (true|false) "false"
>
<!ELEMENT interceptors (interceptor|interceptor-stack)+>
<!ELEMENT interceptor (param*)>
<!ATTLIST interceptor
name CDATA #REQUIRED
class CDATA #REQUIRED
>
<!ELEMENT interceptor-stack (interceptor-ref*)>
<!ATTLIST interceptor-stack
name CDATA #REQUIRED
>
<!ELEMENT interceptor-ref (param*)>
<!ATTLIST interceptor-ref
name CDATA #REQUIRED
>
<!ELEMENT default-interceptor-ref (#PCDATA)>
<!ATTLIST default-interceptor-ref
name CDATA #REQUIRED
>
<!ELEMENT default-action-ref (#PCDATA)>
<!ATTLIST default-action-ref
name CDATA #REQUIRED
>
<!ELEMENT default-class-ref (#PCDATA)>
<!ATTLIST default-class-ref
class CDATA #REQUIRED
>
<!ELEMENT global-results (result+)>
<!ELEMENT global-allowed-methods (#PCDATA)>
<!ELEMENT global-exception-mappings (exception-mapping+)>
<!ELEMENT action ((param|result|interceptor-ref|exception-mapping)*,allowed-methods?)>
<!ATTLIST action
name CDATA #REQUIRED
class CDATA #IMPLIED
method CDATA #IMPLIED
converter CDATA #IMPLIED
>
<!ELEMENT param (#PCDATA)>
<!ATTLIST param
name CDATA #REQUIRED
>
<!ELEMENT result (#PCDATA|param)*>
<!ATTLIST result
name CDATA #IMPLIED
type CDATA #IMPLIED
>
<!ELEMENT exception-mapping (#PCDATA|param)*>
<!ATTLIST exception-mapping
name CDATA #IMPLIED
exception CDATA #REQUIRED
result CDATA #REQUIRED
>
<!ELEMENT allowed-methods (#PCDATA)>
<!ELEMENT include (#PCDATA)>
<!ATTLIST include
file CDATA #REQUIRED
>
<!ELEMENT bean (#PCDATA)>
<!ATTLIST bean
type CDATA #IMPLIED
name CDATA #IMPLIED
class CDATA #REQUIRED
scope CDATA #IMPLIED
static CDATA #IMPLIED
optional CDATA #IMPLIED
>
<!ELEMENT bean-selection (#PCDATA)>
<!ATTLIST bean-selection
name CDATA #IMPLIED
class CDATA #IMPLIED
>
<!ELEMENT constant (#PCDATA)>
<!ATTLIST constant
name CDATA #REQUIRED
value CDATA #REQUIRED
>
<!ELEMENT unknown-handler-stack (unknown-handler-ref*)>
<!ELEMENT unknown-handler-ref (#PCDATA)>
<!ATTLIST unknown-handler-ref
name CDATA #REQUIRED
>
<!-- END SNIPPET: strutsDtd -->
@@ -92,6 +92,7 @@
org.apache.catalina.core,
org.apache.commons.beanutils,
org.apache.commons.collections,
org.apache.jasper,
org.apache.struts2.ognl,
org.apache.tomcat,
org.apache.velocity,
@@ -124,6 +125,7 @@
org.apache.catalina.core,
org.apache.commons.beanutils,
org.apache.commons.collections,
org.apache.jasper,
org.apache.struts2.ognl,
org.apache.tomcat,
org.apache.velocity,
@@ -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);
}
}
@@ -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;
}
}
@@ -31,6 +31,7 @@ import com.opensymphony.xwork2.inject.ContainerBuilder;
import com.opensymphony.xwork2.mock.MockInterceptor;
import com.opensymphony.xwork2.test.StubConfigurationProvider;
import com.opensymphony.xwork2.util.location.LocatableProperties;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.config.StrutsXmlConfigurationProvider;
import org.apache.struts2.dispatcher.HttpParameters;
@@ -239,6 +240,41 @@ public class ConfigurationTest extends XWorkTestCase {
mockContainerProvider.verify();
}
public void testGetActionConfigFallbackToEmptyNamespaceWhenNamespaceDontMatchAndEmptyNamespaceFallbackIsEnabled() {
// struts.actionConfig.fallbackToEmptyNamespace default to true, so it is enabled
RuntimeConfiguration configuration = configurationManager.getConfiguration().getRuntimeConfiguration();
// check namespace that doesn't match fallback to empty namespace
ActionConfig actionConfig = configuration.getActionConfig("/something/that/is/not/in/the/namespace/config", "LazyFoo");
assertEquals("default", actionConfig.getPackageName()); // fallback to empty namespace (package name is default)
assertEquals("LazyFoo", actionConfig.getName());
// check non-empty namespace and name in config still matches
assertNotNull(configuration.getActionConfig("includeTest", "Foo"));
// check root namespace and name in config still matches
actionConfig = configuration.getActionConfig("/", "LazyFoo");
assertEquals("default", actionConfig.getPackageName());
assertEquals("LazyFoo", actionConfig.getName());
}
public void testGetActionConfigReturnNullWhenNamespaceDontMatchAndEmptyNamespaceFallbackIsDisabled() {
// set the struts.actionConfig.fallbackToEmptyNamespace to false and reload the configuration
setStrutsConstant(StrutsConstants.STRUTS_ACTION_CONFIG_FALLBACK_TO_EMPTY_NAMESPACE, "false");
RuntimeConfiguration configuration = configurationManager.getConfiguration().getRuntimeConfiguration();
// check namespace that doesn't match NOT fallback to empty namespace and return null
assertNull(configuration.getActionConfig("/something/that/is/not/in/the/namespace/config", "LazyFoo"));
// check non-empty namespace and name in config still matches
assertNotNull(configuration.getActionConfig("includeTest", "Foo"));
// check root namespace and name in config still matches
ActionConfig actionConfig = configuration.getActionConfig("/", "LazyFoo");
assertEquals("default", actionConfig.getPackageName());
assertEquals("LazyFoo", actionConfig.getName());
}
public void testInitForPackageProviders() {
loadConfigurationProviders(new StubConfigurationProvider() {
@@ -36,10 +36,9 @@ import java.util.List;
public class XmlConfigurationProviderPackagesTest extends ConfigurationTestBase {
public void testBadInheritance() throws ConfigurationException {
final String filename = "com/opensymphony/xwork2/config/providers/xwork-test-bad-inheritance.xml";
ConfigurationProvider provider = null;
try {
provider = buildConfigurationProvider(filename);
provider = buildConfigurationProvider(getXmlConfigFilePath("xwork-test-bad-inheritance.xml"));
fail("Should have thrown a ConfigurationException");
provider.init(configuration);
provider.loadPackages();
@@ -49,8 +48,7 @@ public class XmlConfigurationProviderPackagesTest extends ConfigurationTestBase
}
public void testBasicPackages() throws ConfigurationException {
final String filename = "com/opensymphony/xwork2/config/providers/xwork-test-basic-packages.xml";
ConfigurationProvider provider = buildConfigurationProvider(filename);
ConfigurationProvider provider = buildConfigurationProvider(getXmlConfigFilePath("xwork-test-basic-packages.xml"));
provider.init(configuration);
provider.loadPackages();
@@ -70,8 +68,7 @@ public class XmlConfigurationProviderPackagesTest extends ConfigurationTestBase
}
public void testDefaultPackage() throws ConfigurationException {
final String filename = "com/opensymphony/xwork2/config/providers/xwork-test-default-package.xml";
ConfigurationProvider provider = buildConfigurationProvider(filename);
ConfigurationProvider provider = buildConfigurationProvider(getXmlConfigFilePath("xwork-test-default-package.xml"));
provider.init(configuration);
provider.loadPackages();
@@ -84,8 +81,7 @@ public class XmlConfigurationProviderPackagesTest extends ConfigurationTestBase
}
public void testPackageInheritance() throws ConfigurationException {
final String filename = "com/opensymphony/xwork2/config/providers/xwork-test-package-inheritance.xml";
ConfigurationProvider provider = buildConfigurationProvider(filename);
ConfigurationProvider provider = buildConfigurationProvider(getXmlConfigFilePath("xwork-test-package-inheritance.xml"));
provider.init(configuration);
provider.loadPackages();
@@ -111,7 +107,7 @@ public class XmlConfigurationProviderPackagesTest extends ConfigurationTestBase
assertTrue(multipleParents.contains(defaultPackage));
assertTrue(multipleParents.contains(abstractPackage));
assertTrue(multipleParents.contains(singlePackage));
PackageConfig parentBelow = configuration.getPackageConfig("testParentBelow");
assertEquals(1, parentBelow.getParents().size());
List<PackageConfig> parentBelowParents = parentBelow.getParents();
@@ -129,7 +125,7 @@ public class XmlConfigurationProviderPackagesTest extends ConfigurationTestBase
assertNull(runtimeConfiguration.getActionConfig("/single", "abstract"));
assertNotNull(runtimeConfiguration.getActionConfig("/single", "single"));
assertNull(runtimeConfiguration.getActionConfig("/single", "multiple"));
assertNotNull(runtimeConfiguration.getActionConfig("/parentBelow", "default"));
assertNotNull(runtimeConfiguration.getActionConfig("/parentBelow", "abstract"));
assertNotNull(runtimeConfiguration.getActionConfig("/parentBelow", "single"));
@@ -138,13 +134,57 @@ public class XmlConfigurationProviderPackagesTest extends ConfigurationTestBase
}
public void testPackageWithFinalAttributeLoads() throws ConfigurationException {
ConfigurationProvider provider = buildConfigurationProvider(getXmlConfigFilePath("xwork-test-package-final.xml"));
provider.init(configuration);
provider.loadPackages();
// test expectations
assertEquals(3, configuration.getPackageConfigs().size());
PackageConfig defaultPackage = configuration.getPackageConfig("default");
assertNotNull(defaultPackage);
assertEquals("default", defaultPackage.getName());
// final package extends default
PackageConfig finalPackage = configuration.getPackageConfig("finalPackage");
assertNotNull(finalPackage);
assertEquals("finalPackage", finalPackage.getName());
assertEquals(1, finalPackage.getParents().size());
assertEquals(defaultPackage, finalPackage.getParents().get(0));
// normal package extends default
PackageConfig normalPackage = configuration.getPackageConfig("normalPackage");
assertNotNull(normalPackage);
assertEquals("normalPackage", normalPackage.getName());
assertEquals(1, normalPackage.getParents().size());
assertEquals(defaultPackage, normalPackage.getParents().get(0));
configurationManager.addContainerProvider(provider);
configurationManager.reload();
RuntimeConfiguration runtimeConfiguration = configurationManager.getConfiguration().getRuntimeConfiguration();
assertNotNull(runtimeConfiguration.getActionConfig("/final", "default"));
assertNotNull(runtimeConfiguration.getActionConfig("/final", "actionFinal"));
assertNotNull(runtimeConfiguration.getActionConfig("/normal", "default"));
assertNotNull(runtimeConfiguration.getActionConfig("/normal", "actionNormal"));
}
public void testExtendsFinalPackageThrowsConfigurationException() throws ConfigurationException {
try {
buildConfigurationProvider(getXmlConfigFilePath("xwork-test-package-extends-final.xml"));
} catch (ConfigurationException e) {
assertEquals("Parent package is final and unextendable: parentLevelTwo", e.getMessage());
}
}
public void testDefaultClassRef() throws ConfigurationException {
final String filename = "com/opensymphony/xwork2/config/providers/xwork-test-defaultclassref-package.xml";
final String hasDefaultClassRefPkgName = "hasDefaultClassRef";
final String noDefaultClassRefPkgName = "noDefaultClassRef";
final String testDefaultClassRef = "com.opensymphony.xwork2.ActionSupport";
ConfigurationProvider provider = buildConfigurationProvider(filename);
ConfigurationProvider provider = buildConfigurationProvider(getXmlConfigFilePath("xwork-test-defaultclassref-package.xml"));
provider.init(configuration);
// setup our expectations
@@ -157,4 +197,8 @@ public class XmlConfigurationProviderPackagesTest extends ConfigurationTestBase
assertEquals(expectedDefaultClassRefPackage, configuration.getPackageConfig(hasDefaultClassRefPkgName));
assertEquals(expectedNoDefaultClassRefPackage, configuration.getPackageConfig(noDefaultClassRefPkgName));
}
private String getXmlConfigFilePath(String fileName) {
return "com/opensymphony/xwork2/config/providers/" + fileName;
}
}
@@ -80,6 +80,11 @@ public class OgnlUtilTest extends XWorkTestCase {
ognlUtil = container.getInstance(OgnlUtil.class);
}
private void resetOgnlUtil(Map<String, ?> properties) {
loadButSet(properties);
ognlUtil = container.getInstance(OgnlUtil.class);
}
public void testCanSetADependentObject() {
String dogName = "fido";
@@ -1152,8 +1157,8 @@ public class OgnlUtilTest extends XWorkTestCase {
Exception expected = null;
try {
ognlUtil.setExcludedClasses(Object.class.getName());
ognlUtil.setValue("class.classLoader.defaultAssertionStatus", ognlUtil.createDefaultContext(foo), foo, true);
// Object.class is excluded by default
ognlUtil.setValue("class.classLoader", ognlUtil.createDefaultContext(foo), foo, true);
fail();
} catch (OgnlException e) {
expected = e;
@@ -1166,9 +1171,11 @@ public class OgnlUtilTest extends XWorkTestCase {
public void testAllowCallingMethodsOnObjectClassInDevModeTrue() {
Exception expected = null;
try {
ognlUtil.setExcludedClasses(Foo.class.getName());
ognlUtil.setDevModeExcludedClasses("");
ognlUtil.setDevMode(Boolean.TRUE.toString());
Map<String, String> properties = new HashMap<>();
properties.put(StrutsConstants.STRUTS_EXCLUDED_CLASSES, Foo.class.getName());
properties.put(StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_CLASSES, "");
properties.put(StrutsConstants.STRUTS_DEVMODE, Boolean.TRUE.toString());
resetOgnlUtil(properties);
Foo foo = new Foo();
String result = (String) ognlUtil.getValue("toString", ognlUtil.createDefaultContext(foo), foo, String.class);
@@ -1180,14 +1187,18 @@ public class OgnlUtilTest extends XWorkTestCase {
}
public void testExclusionListDevModeOnOff() throws Exception {
ognlUtil.setDevModeExcludedClasses(Foo.class.getName());
Foo foo = new Foo();
ognlUtil.setDevMode(Boolean.TRUE.toString());
Map<String, String> properties = new HashMap<>();
properties.put(StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_CLASSES, Foo.class.getName());
properties.put(StrutsConstants.STRUTS_DEVMODE, Boolean.TRUE.toString());
resetOgnlUtil(properties);
OgnlException e = assertThrows(OgnlException.class, () -> ognlUtil.getValue("toString", ognlUtil.createDefaultContext(foo), foo, String.class));
assertThat(e).hasMessageContaining("com.opensymphony.xwork2.util.Foo.toString");
ognlUtil.setDevMode(Boolean.FALSE.toString());
properties.put(StrutsConstants.STRUTS_DEVMODE, Boolean.FALSE.toString());
resetOgnlUtil(properties);
assertEquals("Foo", (String) ognlUtil.getValue("toString", ognlUtil.createDefaultContext(foo), foo, String.class));
}
@@ -1196,7 +1207,7 @@ public class OgnlUtilTest extends XWorkTestCase {
Exception expected = null;
try {
ognlUtil.setExcludedClasses(Object.class.getName());
// Object.class is excluded by default
ognlUtil.setValue("Class.ClassLoader.DefaultAssertionStatus", ognlUtil.createDefaultContext(foo), foo, true);
fail();
} catch (OgnlException e) {
@@ -1212,7 +1223,7 @@ public class OgnlUtilTest extends XWorkTestCase {
Exception expected = null;
try {
ognlUtil.setExcludedClasses(Object.class.getName());
// Object.class is excluded by default
ognlUtil.setValue("class['classLoader']['defaultAssertionStatus']", ognlUtil.createDefaultContext(foo), foo, true);
fail();
} catch (OgnlException e) {
@@ -1243,7 +1254,7 @@ public class OgnlUtilTest extends XWorkTestCase {
Exception expected = null;
try {
ognlUtil.setExcludedClasses(Object.class.getName());
// Object.class is excluded by default
ognlUtil.setValue("class[\"classLoader\"]['defaultAssertionStatus']", ognlUtil.createDefaultContext(foo), foo, true);
fail();
} catch (OgnlException e) {
@@ -1284,12 +1295,11 @@ public class OgnlUtilTest extends XWorkTestCase {
assertEquals(expected.getMessage(), "Inappropriate OGNL expression: toString()");
}
public void testAvoidCallingSomeClasses() {
public void testStaticMethodBlocked() {
Foo foo = new Foo();
Exception expected = null;
try {
ognlUtil.setExcludedClasses(Runtime.class.getName());
ognlUtil.setValue("@java.lang.Runtime@getRuntime().exec('mate')", ognlUtil.createDefaultContext(foo), foo, true);
fail();
} catch (OgnlException e) {
@@ -437,12 +437,12 @@ public class OgnlValueStackTest extends XWorkTestCase {
}
/**
* Allow access Enums without enabling access to static methods
* Enum methods should also be banned alongside static methods
*/
public void testEnum() throws Exception {
assertEquals("ONE", vs.findValue("@com.opensymphony.xwork2.ognl.MyNumbers@values()[0]", String.class));
assertEquals("TWO", vs.findValue("@com.opensymphony.xwork2.ognl.MyNumbers@values()[1]", String.class));
assertEquals("THREE", vs.findValue("@com.opensymphony.xwork2.ognl.MyNumbers@values()[2]", String.class));
assertNull("ONE", vs.findValue("@com.opensymphony.xwork2.ognl.MyNumbers@values()[0]", String.class));
assertNull("TWO", vs.findValue("@com.opensymphony.xwork2.ognl.MyNumbers@values()[1]", String.class));
assertNull("THREE", vs.findValue("@com.opensymphony.xwork2.ognl.MyNumbers@values()[2]", String.class));
}
public void testStaticMethodDisallow() {
@@ -26,12 +26,16 @@ import ognl.MemberAccess;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.struts2.ognl.ProviderAllowlist;
import org.apache.struts2.ognl.ThreadAllowlist;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer;
import org.junit.Before;
import org.junit.Test;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
@@ -413,7 +417,7 @@ public class SecurityMemberAccessTest {
boolean actual = sma.isAccessible(context, MyValues.class, values, null);
// then
assertTrue("Access to enums is blocked!", actual);
assertFalse("Access to enums is allowed!", actual);
}
@Test
@@ -853,9 +857,11 @@ public class SecurityMemberAccessTest {
assertTrue("package java.lang. is accessible!", actual);
}
/**
* Test that the allowlist is enforced correctly for classes.
*/
@Test
public void classInclusion() throws Exception {
sma.useEnforceAllowlistEnabled(Boolean.TRUE.toString());
TestBean2 bean = new TestBean2();
@@ -868,6 +874,9 @@ public class SecurityMemberAccessTest {
assertTrue(sma.checkAllowlist(bean, method));
}
/**
* Test that the allowlist is enforced correctly for packages.
*/
@Test
public void packageInclusion() throws Exception {
sma.useEnforceAllowlistEnabled(Boolean.TRUE.toString());
@@ -882,6 +891,9 @@ public class SecurityMemberAccessTest {
assertTrue(sma.checkAllowlist(bean, method));
}
/**
* Test that the allowlist doesn't allow inherited methods unless the declaring class is also allowlisted.
*/
@Test
public void classInclusion_subclass() throws Exception {
sma.useEnforceAllowlistEnabled(Boolean.TRUE.toString());
@@ -893,6 +905,9 @@ public class SecurityMemberAccessTest {
assertFalse(sma.checkAllowlist(bean, method));
}
/**
* Test that the allowlist allows inherited methods when both the target and declaring class are allowlisted.
*/
@Test
public void classInclusion_subclass_both() throws Exception {
sma.useEnforceAllowlistEnabled(Boolean.TRUE.toString());
@@ -904,6 +919,10 @@ public class SecurityMemberAccessTest {
assertTrue(sma.checkAllowlist(bean, method));
}
/**
* Test that the allowlist doesn't allow inherited methods unless the package of the declaring class is also
* allowlisted.
*/
@Test
public void packageInclusion_subclass() throws Exception {
sma.useEnforceAllowlistEnabled(Boolean.TRUE.toString());
@@ -915,6 +934,37 @@ public class SecurityMemberAccessTest {
assertFalse(sma.checkAllowlist(bean, method));
}
/**
* When the allowlist is enabled and proxy object access is disallowed, Hibernate proxies should not be allowed.
*/
@Test
public void classInclusion_hibernateProxy_disallowProxyObjectAccess() throws Exception {
FooBarInterface proxyObject = mockHibernateProxy(new FooBar(), FooBarInterface.class);
Method proxyMethod = proxyObject.getClass().getMethod("fooLogic");
sma.useEnforceAllowlistEnabled(Boolean.TRUE.toString());
sma.useDisallowProxyObjectAccess(Boolean.TRUE.toString());
sma.useAllowlistClasses(FooBar.class.getName());
assertFalse(sma.checkAllowlist(proxyObject, proxyMethod));
}
/**
* When the allowlist is enabled and proxy object access is allowed, Hibernate proxies should be allowlisted based
* on their underlying target object. Class allowlisting should work as expected.
*/
@Test
public void classInclusion_hibernateProxy_allowProxyObjectAccess() throws Exception {
FooBarInterface proxyObject = mockHibernateProxy(new FooBar(), FooBarInterface.class);
Method proxyMethod = proxyObject.getClass().getMethod("fooLogic");
sma.useEnforceAllowlistEnabled(Boolean.TRUE.toString());
sma.useDisallowProxyObjectAccess(Boolean.FALSE.toString());
sma.useAllowlistClasses(FooBar.class.getName());
assertTrue(sma.checkAllowlist(proxyObject, proxyMethod));
}
@Test
public void packageInclusion_subclass_both() throws Exception {
sma.useEnforceAllowlistEnabled(Boolean.TRUE.toString());
@@ -931,6 +981,15 @@ public class SecurityMemberAccessTest {
private static String formGetterName(String propertyName) {
return "get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
}
@SuppressWarnings("unchecked")
private static <T> T mockHibernateProxy(T originalObject, Class<T> proxyInterface) {
return (T) Proxy.newProxyInstance(
proxyInterface.getClassLoader(),
new Class<?>[]{proxyInterface, HibernateProxy.class},
new DummyHibernateProxyHandler(originalObject)
);
}
}
class FooBar implements FooBarInterface {
@@ -1042,10 +1101,28 @@ class StaticTester {
}
protected static Field getFieldByName(String fieldName) throws NoSuchFieldException {
if (fieldName != null && fieldName.length() > 0) {
if (fieldName != null && !fieldName.isEmpty()) {
return StaticTester.class.getDeclaredField(fieldName);
} else {
throw new NoSuchFieldException("field: " + fieldName + " does not exist");
}
}
}
class DummyHibernateProxyHandler implements InvocationHandler {
private final Object instance;
public DummyHibernateProxyHandler(Object instance) {
this.instance = instance;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (HibernateProxy.class.getMethod("getHibernateLazyInitializer").equals(method)) {
LazyInitializer initializer = mock(LazyInitializer.class);
when(initializer.getImplementation()).thenReturn(instance);
return initializer;
}
return method.invoke(instance, args);
}
}
@@ -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<Validator> 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<Validator> 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();
}
List<Validator> validators = actionValidatorManager.getValidators(SimpleAction3.class, alias);
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();
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 class DefaultActionValidatorManagerTest extends XWorkTestCase {
}
}
/*
// TODO: this all need to be converted to real unit tests
public void testSkipUserMarkerActionLevelShortCircuit() {
// get validators
List validatorList = actionValidatorManager.getValidators(User.class, null);
List<Validator> validatorList = actionValidatorManager.getValidators(User.class, null);
assertEquals(10, validatorList.size());
try {
@@ -202,16 +204,17 @@ public class DefaultActionValidatorManagerTest extends XWorkTestCase {
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<String> 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 class DefaultActionValidatorManagerTest extends XWorkTestCase {
// 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<Validator> validatorList = actionValidatorManager.getValidators(User.class, null);
assertEquals(10, validatorList.size());
try {
@@ -244,34 +245,30 @@ public class DefaultActionValidatorManagerTest extends XWorkTestCase {
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<String> 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<Validator> validatorList = actionValidatorManager.getValidators(User.class, null);
assertEquals(10, validatorList.size());
User user = new User();
@@ -280,18 +277,18 @@ public class DefaultActionValidatorManagerTest extends XWorkTestCase {
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<String> 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 class DefaultActionValidatorManagerTest extends XWorkTestCase {
assertEquals("Email not the same as email2", l.get(0));
}
public void testShortCircuitNoErrors() {
// get validators
List validatorList = actionValidatorManager.getValidators(User.class, null);
List<Validator> validatorList = actionValidatorManager.getValidators(User.class, null);
assertEquals(10, validatorList.size());
try {
@@ -311,73 +306,72 @@ public class DefaultActionValidatorManagerTest extends XWorkTestCase {
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<String, List<String>> fieldErrors = action.getFieldErrors();
Iterator<Map.Entry<String, List<String>>> 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<String, List<String>> 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");
}
*/
}
@@ -28,6 +28,8 @@ import com.opensymphony.xwork2.config.entities.ActionConfig;
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 class VisitorFieldValidatorTest extends XWorkTestCase {
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());
@@ -22,6 +22,7 @@ import com.opensymphony.xwork2.ActionSupport;
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 class VisitorValidatorTestAction extends ActionSupport {
public List<TestBean> getTestBeanList() {
return testBeanList;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
@@ -54,10 +54,12 @@ import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonMap;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -588,6 +590,30 @@ public class DispatcherTest extends StrutsJUnit4InternalTestCase {
assertEquals(Locale.CANADA_FRENCH, dispatcher.getLocale(request));
}
@Test
public void testExcludePatterns() {
initDispatcher(singletonMap(StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN, "/ns1/.*\\.json,/ns2/.*\\.json"));
assertThat(dispatcher.getActionExcludedPatterns()).extracting(Pattern::toString).containsOnly(
"/ns1/.*\\.json",
"/ns2/.*\\.json"
);
}
@Test
public void testExcludePatternsUsingCustomSeparator() {
Map<String, String> props = new HashMap<>();
props.put(StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN, "/ns1/[a-z]{1,10}.json///ns2/[a-z]{1,10}.json");
props.put(StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN_SEPARATOR, "//");
initDispatcher(props);
assertThat(dispatcher.getActionExcludedPatterns()).extracting(Pattern::toString).containsOnly(
"/ns1/[a-z]{1,10}.json",
"/ns2/[a-z]{1,10}.json"
);
}
public static Dispatcher spyDispatcherWithConfigurationManager(Dispatcher dispatcher, ConfigurationManager configurationManager) {
Dispatcher spiedDispatcher = spy(dispatcher);
doReturn(configurationManager).when(spiedDispatcher).createConfigurationManager(any());
@@ -1,86 +0,0 @@
/*
* 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.dispatcher;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.inject.ContainerBuilder;
import com.opensymphony.xwork2.util.location.LocatableProperties;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.StrutsInternalTestCase;
import org.apache.struts2.config.PropertiesConfigurationProvider;
import java.util.List;
import java.util.regex.Pattern;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class InitOperationsTest extends StrutsInternalTestCase {
public void testExcludePatterns() {
// given
loadConfigurationProviders(new PropertiesConfigurationProvider() {
@Override
public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {
props.setProperty(StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN, "/ns1/.*\\.json,/ns2/.*\\.json");
}
});
Dispatcher mockDispatcher = mock(Dispatcher.class);
when(mockDispatcher.getContainer()).thenReturn(container);
// when
InitOperations init = new InitOperations();
List<Pattern> patterns = init.buildExcludedPatternsList(mockDispatcher);
// then
assertThat(patterns).extracting(Pattern::toString).containsOnly(
"/ns1/.*\\.json",
"/ns2/.*\\.json"
);
}
public void testExcludePatternsUsingCustomSeparator() {
// given
loadConfigurationProviders(new PropertiesConfigurationProvider() {
@Override
public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {
props.setProperty(StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN, "/ns1/[a-z]{1,10}.json///ns2/[a-z]{1,10}.json");
props.setProperty(StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN_SEPARATOR, "//");
}
});
Dispatcher mockDispatcher = mock(Dispatcher.class);
when(mockDispatcher.getContainer()).thenReturn(container);
// when
InitOperations init = new InitOperations();
String separator = container.getInstance(String.class, StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN_SEPARATOR);
List<Pattern> patterns = init.buildExcludedPatternsList(mockDispatcher);
// then
assertThat(separator).isNotBlank().isEqualTo("//");
assertThat(patterns).extracting(Pattern::toString).containsOnly(
"/ns1/[a-z]{1,10}.json",
"/ns2/[a-z]{1,10}.json"
);
}
}
@@ -26,13 +26,10 @@ import org.springframework.mock.web.MockFilterConfig;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.regex.Pattern;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -127,6 +124,7 @@ public class StrutsPrepareAndExecuteFilterIntegrationTest {
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
MockFilterConfig filterConfig = new MockFilterConfig();
filterConfig.addInitParameter("struts.action.excludePattern", ".*hello.*");
MockFilterChain filterChain = new MockFilterChain() {
@Override
public void doFilter(ServletRequest req, ServletResponse res) {
@@ -135,14 +133,7 @@ public class StrutsPrepareAndExecuteFilterIntegrationTest {
};
request.setRequestURI("/hello.action");
StrutsPrepareAndExecuteFilter filter = new StrutsPrepareAndExecuteFilter() {
@Override
public void init( FilterConfig filterConfig ) throws ServletException {
super.init(filterConfig);
excludedPatterns = new ArrayList<>();
excludedPatterns.add(Pattern.compile(".*hello.*"));
}
};
StrutsPrepareAndExecuteFilter filter = new StrutsPrepareAndExecuteFilter();
filter.init(filterConfig);
filter.doFilter(request, response, filterChain);
assertEquals(200, response.getStatus());
@@ -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;
@@ -31,6 +32,7 @@ import org.apache.struts2.interceptor.csp.DefaultCspSettings;
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 +76,10 @@ public class CspInterceptorTest extends StrutsInternalTestCase {
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 +88,15 @@ public class CspInterceptorTest extends StrutsInternalTestCase {
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 +104,7 @@ public class CspInterceptorTest extends StrutsInternalTestCase {
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,21 +180,89 @@ public class CspInterceptorTest extends StrutsInternalTestCase {
checkHeader("/report-uri", enforcingMode);
}
public void testNonExistingCspSettingsClassName() throws Exception {
boolean enforcingMode = true;
mai.setAction(new TestAction());
request.setContextPath("/app");
interceptor.setEnforcingMode(enforcingMode);
interceptor.setReportUri("/report-uri");
interceptor.setPrependServletContext(false);
try {
interceptor.setCspSettingsClassName("foo");
interceptor.intercept(mai);
fail("Expected exception");
} catch (ConfigurationException e) {
assertEquals("The class foo doesn't exist!", e.getMessage());
}
}
public void testInvalidCspSettingsClassName() throws Exception {
boolean enforcingMode = true;
mai.setAction(new TestAction());
request.setContextPath("/app");
interceptor.setEnforcingMode(enforcingMode);
interceptor.setReportUri("/report-uri");
interceptor.setPrependServletContext(false);
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);
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'; ",
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 {
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;
@@ -207,10 +281,11 @@ public class CspInterceptorTest extends StrutsInternalTestCase {
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();
}
@@ -230,4 +305,15 @@ public class CspInterceptorTest extends StrutsInternalTestCase {
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";
}
}
}
@@ -147,6 +147,26 @@ public class I18nInterceptorTest extends TestCase {
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);
@@ -26,6 +26,7 @@ import org.apache.struts2.StrutsInternalTestCase;
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.TimeUnit;
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 class StrutsBackgroundProcessTest extends StrutsInternalTestCase {
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 class StrutsBackgroundProcessTest extends StrutsInternalTestCase {
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 class StrutsBackgroundProcessTest extends StrutsInternalTestCase {
}
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 @@ class LockBackgroundProcess extends StrutsBackgroundProcess {
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;
}
}
@@ -116,15 +116,17 @@ public class ParametersInterceptorTest extends XWorkTestCase {
pi.setParameters(action, vs, HttpParameters.create(params).build());
// then
assertEquals(3, action.getActionMessages().size());
assertEquals(3, action.getActionErrors().size());
String msg1 = action.getActionMessage(0);
String msg2 = action.getActionMessage(1);
String msg3 = action.getActionMessage(2);
List<String> actionErrors = new ArrayList<>(action.getActionErrors());
assertEquals("Error setting expression 'expression' with value '#f=#_memberAccess.getClass().getDeclaredField('allowStaticMethodAccess'),#f.setAccessible(true),#f.set(#_memberAccess,true),#req=@org.apache.struts2.ServletActionContext@getRequest(),#resp=@org.apache.struts2.ServletActionContext@getResponse().getWriter(),#resp.println(#req.getRealPath('/')),#resp.close()'", msg1);
assertEquals("Error setting expression 'name' with value '(#context[\"xwork.MethodAccessor.denyMethodExecution\"]= new java.lang.Boolean(false), #_memberAccess[\"allowStaticMethodAccess\"]= new java.lang.Boolean(true), @java.lang.Runtime@getRuntime().exec('mkdir /tmp/PWNAGE'))(meh)'", msg2);
assertEquals("Error setting expression 'top['name'](0)' with value 'true'", msg3);
String msg1 = actionErrors.get(0);
String msg2 = actionErrors.get(1);
String msg3 = actionErrors.get(2);
assertEquals("Unexpected Exception caught setting 'expression' on 'class org.apache.struts2.interceptor.parameter.ValidateAction: Error setting expression 'expression' with value '#f=#_memberAccess.getClass().getDeclaredField('allowStaticMethodAccess'),#f.setAccessible(true),#f.set(#_memberAccess,true),#req=@org.apache.struts2.ServletActionContext@getRequest(),#resp=@org.apache.struts2.ServletActionContext@getResponse().getWriter(),#resp.println(#req.getRealPath('/')),#resp.close()'", msg1);
assertEquals("Unexpected Exception caught setting 'name' on 'class org.apache.struts2.interceptor.parameter.ValidateAction: Error setting expression 'name' with value '(#context[\"xwork.MethodAccessor.denyMethodExecution\"]= new java.lang.Boolean(false), #_memberAccess[\"allowStaticMethodAccess\"]= new java.lang.Boolean(true), @java.lang.Runtime@getRuntime().exec('mkdir /tmp/PWNAGE'))(meh)'", msg2);
assertEquals("Unexpected Exception caught setting 'top['name'](0)' on 'class org.apache.struts2.interceptor.parameter.ValidateAction: Error setting expression 'top['name'](0)' with value 'true'", msg3);
assertNull(action.getName());
}
@@ -201,15 +203,16 @@ public class ParametersInterceptorTest extends XWorkTestCase {
pi.setParameters(action, vs, HttpParameters.create(params).build());
// then
assertEquals(3, action.getActionMessages().size());
assertEquals(3, action.getActionErrors().size());
String msg1 = action.getActionMessage(0);
String msg2 = action.getActionMessage(1);
String msg3 = action.getActionMessage(2);
List<String> actionErrors = new ArrayList<>(action.getActionErrors());
String msg1 = actionErrors.get(0);
String msg2 = actionErrors.get(1);
String msg3 = actionErrors.get(2);
assertEquals("Error setting expression 'class.classLoader.defaultAssertionStatus' with value 'true'", msg1);
assertEquals("Error setting expression 'class.classLoader.jarPath' with value 'bad'", msg2);
assertEquals("Error setting expression 'model.class.classLoader.jarPath' with value 'very bad'", msg3);
assertEquals("Unexpected Exception caught setting 'class.classLoader.defaultAssertionStatus' on 'class org.apache.struts2.interceptor.parameter.ValidateAction: Error setting expression 'class.classLoader.defaultAssertionStatus' with value 'true'", msg1);
assertEquals("Unexpected Exception caught setting 'class.classLoader.jarPath' on 'class org.apache.struts2.interceptor.parameter.ValidateAction: Error setting expression 'class.classLoader.jarPath' with value 'bad'", msg2);
assertEquals("Unexpected Exception caught setting 'model.class.classLoader.jarPath' on 'class org.apache.struts2.interceptor.parameter.ValidateAction: Error setting expression 'model.class.classLoader.jarPath' with value 'very bad'", msg3);
assertFalse(excluded.get(pollution1));
assertFalse(excluded.get(pollution2));
@@ -582,8 +585,8 @@ public class ParametersInterceptorTest extends XWorkTestCase {
container.inject(config.getInterceptors().get(0).getInterceptor());
ActionProxy proxy = actionProxyFactory.createActionProxy("", MockConfigurationProvider.PARAM_INTERCEPTOR_ACTION_NAME, null, extraContext.getContextMap());
proxy.execute();
final String actionMessage = "" + ((SimpleAction) proxy.getAction()).getActionMessages().toArray()[0];
assertTrue(actionMessage.contains("Error setting expression 'not_a_property' with value 'There is no action property named like this'"));
final String actionError = "" + ((SimpleAction) proxy.getAction()).getActionErrors().toArray()[0];
assertTrue(actionError.contains("Error setting expression 'not_a_property' with value 'There is no action property named like this'"));
}
public void testNonexistentParametersAreIgnoredInProductionMode() throws Exception {
@@ -1014,59 +1017,65 @@ public class ParametersInterceptorTest extends XWorkTestCase {
class ValidateAction implements ValidationAware {
private final List<String> messages = new LinkedList<>();
private final List<String> errors = new LinkedList<>();
private String name;
@Override
public void setActionErrors(Collection<String> errorMessages) {
}
@Override
public Collection<String> getActionErrors() {
return null;
return errors;
}
@Override
public void setActionMessages(Collection<String> messages) {
}
@Override
public Collection<String> getActionMessages() {
return messages;
}
@Override
public void setFieldErrors(Map<String, List<String>> errorMap) {
}
@Override
public Map<String, List<String>> getFieldErrors() {
return null;
}
@Override
public void addActionError(String anErrorMessage) {
errors.add(anErrorMessage);
}
@Override
public void addActionMessage(String aMessage) {
messages.add(aMessage);
}
@Override
public void addFieldError(String fieldName, String errorMessage) {
}
@Override
public boolean hasActionErrors() {
return false;
return !errors.isEmpty();
}
@Override
public boolean hasActionMessages() {
return !messages.isEmpty();
}
public boolean hasErrors() {
return false;
}
@Override
public boolean hasFieldErrors() {
return false;
}
public String getActionMessage(int index) {
return messages.get(index);
}
public String getName() {
return name;
}
@@ -0,0 +1,240 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.struts2.ognl;
import com.opensymphony.xwork2.XWorkTestCase;
import com.opensymphony.xwork2.ognl.OgnlValueStack;
import com.opensymphony.xwork2.util.ValueStackFactory;
import ognl.OgnlRuntime;
import org.apache.struts2.StrutsConstants;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.assertNotEquals;
public class OgnlSetPossiblePropertyTest extends XWorkTestCase {
private OgnlValueStack vs;
public <T> T setUpClass(Class<T> holderClass) throws Exception {
Map<String, String> properties = new HashMap<>();
properties.put(StrutsConstants.STRUTS_EXCLUDED_CLASSES, holderClass.getName() + "$ExcludedField");
loadButSet(properties);
vs = (OgnlValueStack) container.getInstance(ValueStackFactory.class).createValueStack();
T nonExcludedHolder = holderClass.getDeclaredConstructor().newInstance();
vs.push(nonExcludedHolder);
return nonExcludedHolder;
}
public void testSetFieldValueDontAssignWhenHolderClassAndFieldClassHaveOnlyPublicFields() throws Exception {
/* Case: to test setFieldValue without having set method
*
* NonExcludedHolder class
* - field: public
* ExcludeField class
* - field: public
*/
HolderWithPublicField holder = setUpClass(HolderWithPublicField.class);
vs.setValue("excludedField.excludedFieldString", "EXPLOITED");
assertNotEquals("EXPLOITED", holder.excludedField.excludedFieldString);
}
public void testSetMethodValueDontAssignWhenHolderAndFieldClassWithPublicMethodsAndPrivateFields() throws Exception {
/* Case: to test setMethodValue, so to make fields as private
*
* NonExcludedHolder class
* - field: private
* - method: public
* ExcludeField class
* - field: private
* - method: public
*/
HolderWithPublicMethod holder = setUpClass(HolderWithPublicMethod.class);
vs.setValue("excludedField.excludedFieldString", "EXPLOITED");
assertNotEquals("EXPLOITED", holder.excludedField.excludedFieldString);
}
public void testSetFieldValueDontAssignWhenHolderClassWithGetMethodAndFieldClassWithPublicField() throws Exception {
/* Case: to test setFieldValue when holder get method is public and field class set method is private so fallback to set field
*
* NonExcludedHolder class
* - field: private
* - method: public
* ExcludeField class
* - field: public
* - method: private
*/
HolderWhoseFieldWithPrivateMethod holder = setUpClass(HolderWhoseFieldWithPrivateMethod.class);
vs.setValue("excludedField.excludedFieldString", "EXPLOITED");
assertNotEquals("EXPLOITED", holder.excludedField.excludedFieldString);
}
public void testSetMethodValueDontAssignWhenHolderClassWithGetMethodAndFieldClassWithPublicMethod() throws Exception {
/* Case: to test setMethodValue when holder get method is public and field class field is private so only call to set method
*
* NonExcludedHolder class
* - field: private
* - method: public
* ExcludeField class
* - field: private
* - method: public
*/
HolderWhoseFieldWithPublicMethod holder = setUpClass(HolderWhoseFieldWithPublicMethod.class);
vs.setValue("excludedField.excludedFieldString", "EXPLOITED");
assertNotEquals("EXPLOITED", holder.excludedField.excludedFieldString);
}
public void testWriteMethodValueDontAssignWhenWriteMethodIsNotAccessible() throws Exception {
/* Case: to test invoke method from getWriteMethod when holder get method is public and field class field / set method is private so fallback to write method
*
* NonExcludedHolder class
* - field: private
* - method: public
* ExcludeField class
* - field: private
* - set method: private
* - write method: public
*/
HolderWhoseFieldWithPublicWriteMethod holder = setUpClass(HolderWhoseFieldWithPublicWriteMethod.class);
Method writeMethod = OgnlRuntime.getWriteMethod(HolderWhoseFieldWithPublicWriteMethod.ExcludedField.class, "excludedFieldString");
vs.setValue("excludedField.excludedFieldString", "EXPLOITED");
assertEquals("setexcludedfieldstring", writeMethod.getName());
assertNotEquals("EXPLOITED", holder.excludedField.excludedFieldString);
}
public void testWriteMethodValueDontAssignWhenPublicSetterDifferentFieldName() throws Exception {
/* Case: to test invoke method from getWriteMethod when holder get method is public and field class field / set method is of different name
*
* NonExcludedHolder class
* - field: private
* - method: public
* ExcludeField class
* - field: private
* - set method: public (but not matching with field name)
*/
HolderWhoseFieldWithPublicSetterDifferentFieldName holder = setUpClass(HolderWhoseFieldWithPublicSetterDifferentFieldName.class);
vs.setValue("excludedField.excludedFieldString", "EXPLOITED");
assertNotEquals("EXPLOITED", holder.excludedField.excludedFieldStringInternal);
}
public static class HolderWithPublicField {
public ExcludedField excludedField = new ExcludedField();
public static class ExcludedField {
public String excludedFieldString = "defaultValue";
}
}
public static class HolderWithPublicMethod {
private ExcludedField excludedField = new ExcludedField();
public ExcludedField getExcludedField() {
return excludedField;
}
public static class ExcludedField {
private String excludedFieldString = "defaultValue";
public void setExcludedFieldString(String value) {
this.excludedFieldString = value;
}
}
}
public static class HolderWhoseFieldWithPrivateMethod {
private ExcludedField excludedField = new ExcludedField();
public ExcludedField getExcludedField() {
return excludedField;
}
public static class ExcludedField {
public String excludedFieldString = "defaultValue";
private void setExcludedFieldString(String value) {
this.excludedFieldString = value;
}
}
}
public static class HolderWhoseFieldWithPublicMethod {
private ExcludedField excludedField = new ExcludedField();
public ExcludedField getExcludedField() {
return excludedField;
}
public static class ExcludedField {
public String excludedFieldString = "defaultValue";
private void setExcludedFieldString(String value) {
this.excludedFieldString = value;
}
}
}
public static class HolderWhoseFieldWithPublicWriteMethod {
private ExcludedField excludedField = new ExcludedField();
public ExcludedField getExcludedField() {
return excludedField;
}
public static class ExcludedField {
private String excludedFieldString = "defaultValue";
private void setExcludedFieldString(String value) {
this.excludedFieldString = value;
}
public void setexcludedfieldstring(String value) {
this.excludedFieldString = value;
}
}
}
public static class HolderWhoseFieldWithPublicSetterDifferentFieldName {
private ExcludedField excludedField = new ExcludedField();
public ExcludedField getExcludedField() {
return excludedField;
}
public static class ExcludedField {
private String excludedFieldStringInternal = "defaultValue";
public void setExcludedFieldString(String value) {
this.excludedFieldStringInternal = value;
}
}
}
}
@@ -112,6 +112,14 @@ public class StrutsQueryStringParserTest {
assertEquals("test", queryParameters.getQueryFragment());
}
@Test
public void shouldHandleOnlyFragment() {
QueryStringParser.Result queryParameters = parser.parse("#test");
assertTrue(queryParameters.getQueryParams().isEmpty());
assertEquals("test", queryParameters.getQueryFragment());
}
@Before
public void setUp() throws Exception {
this.parser = new StrutsQueryStringParser(new StrutsUrlDecoder());
@@ -22,14 +22,10 @@ import com.mockobjects.servlet.MockJspWriter;
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 class SetTagTest extends AbstractUITagTest {
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 @@ public class SetTagTest extends AbstractUITagTest {
}
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;
@@ -2092,6 +2092,42 @@ public class URLTagTest extends AbstractUITagTest {
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testQueryParamsAndFragment() throws Exception {
request.setRequestURI("/public/about");
tag.setAction("company");
tag.setValue("/books?hl=en&lr=Y&redir_esc=y#v=twopage&q&f=false");
tag.setEscapeAmp("false");
tag.doStartTag();
tag.doEndTag();
assertEquals("/books?hl=en&lr=Y&redir_esc=y#v=twopage&q&f=false", writer.toString());
}
public void testDoubleEqualSigns() throws Exception {
request.setRequestURI("/public/about");
tag.setAction("company");
tag.setValue("/PublicationsDetail.aspx?ID=GjTu91suYQI=&t=1");
tag.setEscapeAmp("false");
tag.doStartTag();
tag.doEndTag();
assertEquals("/PublicationsDetail.aspx?ID=GjTu91suYQI%3D&t=1", writer.toString());
}
public void testOnlyFragment() throws Exception {
request.setRequestURI("/public/about");
tag.setAction("company");
tag.setValue("/books#v=twopage&q&f=false");
tag.setEscapeAmp("false");
tag.doStartTag();
tag.doEndTag();
assertEquals("/books#v=twopage&q&f=false", writer.toString());
}
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -217,23 +217,9 @@ public class DebugTagTest extends AbstractUITagTest {
/**
* Overwrite the Struts Constant and reload container
*/
private void setStrutsConstant(final Map<String, String> overwritePropeties) {
configurationManager.addContainerProvider(new StubConfigurationProvider() {
@Override
public boolean needsReload() {
return true;
}
@Override
public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {
for (Map.Entry<String, String> stringStringEntry : overwritePropeties.entrySet()) {
props.setProperty(stringStringEntry.getKey(), stringStringEntry.getValue(), null);
}
}
});
configurationManager.reload();
container = configurationManager.getConfiguration().getContainer();
@Override
protected void setStrutsConstant(final Map<String, String> overwritePropeties) {
super.setStrutsConstant(overwritePropeties);
stack.getActionContext().withContainer(container);
}
}
}
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.5//EN"
"struts-6.5.dtd">
<struts>
<package name="default" namespace="/default">
<action name="default" class="com.opensymphony.xwork2.ActionSupport"/>
</package>
<package name="parentLevelTwo" namespace="/parent2" final="true">
<action name="levelTwo" class="com.opensymphony.xwork2.ActionSupport"/>
</package>
<package name="child" namespace="/child" extends="default,parentLevelTwo">
<action name="single" class="com.opensymphony.xwork2.ActionSupport"/>
</package>
</struts>
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.5//EN"
"struts-6.5.dtd">
<struts>
<package name="default" namespace="/default">
<action name="default" class="com.opensymphony.xwork2.ActionSupport"/>
</package>
<package name="finalPackage" namespace="/final" extends="default" final="true">
<action name="actionFinal" class="com.opensymphony.xwork2.ActionSupport"/>
</package>
<package name="normalPackage" namespace="/normal" extends="default">
<action name="actionNormal" class="com.opensymphony.xwork2.ActionSupport"/>
</package>
</struts>
@@ -26,4 +26,12 @@
<message>You must enter a context.</message>
</field-validator>
</field>
<field name="birthday">
<field-validator type="fieldexpression">
<param name="expression"><![CDATA[
(birthday == null || birthday.before(new java.util.Date()))
]]></param>
<message key="errors_birthday" />
</field-validator>
</field>
</validators>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-async-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -25,7 +25,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-cdi-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-config-browser-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-convention-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-dwr-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-embeddedjsp-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-gxp-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-jasperreports-plugin</artifactId>
+1 -1
View File
@@ -25,7 +25,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-javatemplates-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-jfreechart-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-json-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-junit-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-osgi-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-oval-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-pell-multipart-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-plexus-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-plugins</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-portlet-junit-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-portlet-mocks-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-portlet-tiles-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-portlet-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-rest-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-sitemesh-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>6.4.0</version>
<version>6.5.0</version>
</parent>
<artifactId>struts2-spring-plugin</artifactId>
@@ -19,50 +19,72 @@
package com.opensymphony.xwork2.ognl;
import com.opensymphony.xwork2.ActionProxy;
import com.opensymphony.xwork2.XWorkTestCase;
import com.opensymphony.xwork2.XWorkJUnit4TestCase;
import com.opensymphony.xwork2.config.providers.XmlConfigurationProvider;
import org.apache.struts2.config.StrutsXmlConfigurationProvider;
import org.junit.Before;
import org.junit.Test;
import java.lang.reflect.Member;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class SecurityMemberAccessProxyTest extends XWorkTestCase {
private Map<String, Object> context;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class SecurityMemberAccessProxyTest extends XWorkJUnit4TestCase {
private static final String PROXY_MEMBER_METHOD = "isExposeProxy";
private static final String TEST_SUB_BEAN_CLASS_METHOD = "getIssueId";
private Map<String, Object> context;
private ActionProxy proxy;
private final SecurityMemberAccess sma = new SecurityMemberAccess(null, null);
private Member proxyObjectProxyMember;
private Member proxyObjectNonProxyMember;
@Before
@Override
public void setUp() throws Exception {
super.setUp();
XmlConfigurationProvider provider = new StrutsXmlConfigurationProvider("com/opensymphony/xwork2/spring/actionContext-xwork.xml");
loadConfigurationProviders(provider);
context = new HashMap<>();
// Set up XWork
XmlConfigurationProvider provider = new StrutsXmlConfigurationProvider("com/opensymphony/xwork2/spring/actionContext-xwork.xml");
container.inject(provider);
loadConfigurationProviders(provider);
proxy = actionProxyFactory.createActionProxy(null, "chaintoAOPedTestSubBeanAction", null, context);
proxyObjectProxyMember = proxy.getAction().getClass().getMethod(PROXY_MEMBER_METHOD);
proxyObjectNonProxyMember = proxy.getAction().getClass().getMethod(TEST_SUB_BEAN_CLASS_METHOD);
}
public void testProxyAccessIsBlocked() throws Exception {
ActionProxy proxy = actionProxyFactory.createActionProxy(null,
"chaintoAOPedTestSubBeanAction", null, context);
/**
* When {@code disallowProxyObjectAccess} is {@code true}, proxy access is blocked irrespective of
* {@code disallowProxyMemberAccess} value and irrespective of whether the member itself originates from the proxy.
*/
@Test
public void disallowProxyObjectAccess() {
sma.useDisallowProxyObjectAccess(Boolean.TRUE.toString());
Arrays.asList(proxyObjectProxyMember, proxyObjectNonProxyMember).forEach(member ->
Arrays.asList(Boolean.TRUE, Boolean.FALSE).forEach(disallowProxyMemberAccess -> {
sma.useDisallowProxyMemberAccess(disallowProxyMemberAccess.toString());
assertFalse(sma.isAccessible(context, proxy.getAction(), member, ""));
})
);
}
SecurityMemberAccess sma = new SecurityMemberAccess(true);
@Test
public void disallowProxyMemberAccess() {
sma.useDisallowProxyObjectAccess(Boolean.FALSE.toString());
sma.useDisallowProxyMemberAccess(Boolean.TRUE.toString());
Member member = proxy.getAction().getClass().getMethod("isExposeProxy");
boolean accessible = sma.isAccessible(context, proxy.getAction(), member, "");
assertFalse(accessible);
assertFalse(sma.isAccessible(context, proxy.getAction(), proxyObjectProxyMember, ""));
assertTrue(sma.isAccessible(context, proxy.getAction(), proxyObjectNonProxyMember, ""));
}
public void testProxyAccessIsAccessible() throws Exception {
ActionProxy proxy = actionProxyFactory.createActionProxy(null,
"chaintoAOPedTestSubBeanAction", null, context);
SecurityMemberAccess sma = new SecurityMemberAccess(true);
Member member = proxy.getAction().getClass().getMethod("isExposeProxy");
boolean accessible = sma.isAccessible(context, proxy.getAction(), member, "");
assertTrue(accessible);
@Test
public void allowAllProxyAccess() {
sma.useDisallowProxyObjectAccess(Boolean.FALSE.toString());
sma.useDisallowProxyMemberAccess(Boolean.FALSE.toString());
assertTrue(sma.isAccessible(context, proxy.getAction(), proxyObjectProxyMember, ""));
assertTrue(sma.isAccessible(context, proxy.getAction(), proxyObjectNonProxyMember, ""));
}
}

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