Compare commits

...

76 Commits

Author SHA1 Message Date
Lukasz Lenart f6bf43ae0b [maven-release-plugin] prepare release STRUTS_7_0_1 2025-02-02 08:25:53 +01:00
Lukasz Lenart 25d0338e63 Merge pull request #1199 from apache/dependabot/maven/org.apache.commons-commons-text-1.13.0
Bump org.apache.commons:commons-text from 1.12.0 to 1.13.0
2025-01-27 07:00:15 +01:00
Lukasz Lenart 01af64615f Merge pull request #1198 from apache/dependabot/maven/byte-buddy.version-1.16.1
Bump byte-buddy.version from 1.15.11 to 1.16.1
2025-01-27 06:58:56 +01:00
Lukasz Lenart ebc4e3516d Merge pull request #1196 from apache/dependabot/github_actions/github/codeql-action-3.28.5
Bump github/codeql-action from 3.28.1 to 3.28.5
2025-01-27 06:56:56 +01:00
dependabot[bot] d5b94369d1 Bump org.apache.commons:commons-text from 1.12.0 to 1.13.0
Bumps org.apache.commons:commons-text from 1.12.0 to 1.13.0.

---
updated-dependencies:
- dependency-name: org.apache.commons:commons-text
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-27 01:54:25 +00:00
dependabot[bot] c5cf2f8f99 Bump byte-buddy.version from 1.15.11 to 1.16.1
Bumps `byte-buddy.version` from 1.15.11 to 1.16.1.

Updates `net.bytebuddy:byte-buddy` from 1.15.11 to 1.16.1
- [Release notes](https://github.com/raphw/byte-buddy/releases)
- [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md)
- [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.15.11...byte-buddy-1.16.1)

Updates `net.bytebuddy:byte-buddy-agent` from 1.15.11 to 1.16.1
- [Release notes](https://github.com/raphw/byte-buddy/releases)
- [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md)
- [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.15.11...byte-buddy-1.16.1)

---
updated-dependencies:
- dependency-name: net.bytebuddy:byte-buddy
  dependency-type: direct:development
  update-type: version-update:semver-minor
- dependency-name: net.bytebuddy:byte-buddy-agent
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-27 01:54:18 +00:00
dependabot[bot] 10d3545ac2 Bump github/codeql-action from 3.28.1 to 3.28.5
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.1 to 3.28.5.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Commits](https://github.com/github/codeql-action/compare/v3.28.1...v3.28.5)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-27 01:45:24 +00:00
Lukasz Lenart 05deb7b5e7 Merge pull request #1191 from apache/feature/WW-5512-optional-inject-s7
WW-5512 Extends the container to support injecting optional parameters into constructor
2025-01-26 08:19:06 +01:00
Lukasz Lenart 358f808567 WW-5512 Extends the container to support injecting optional parameters into constructor 2025-01-23 08:58:46 +01:00
Kusal Kithul-Godage f98b67562b Merge pull request #1193 from apache/WW-5516-fix-temp-theme-fallback-70 2025-01-22 17:42:17 +11:00
Kusal Kithul-Godage 2def24c7db WW-5516 Fix TemplateDir and Theme fallback to Request/Session/Application attributes 2025-01-22 13:25:01 +11:00
Lukasz Lenart 4e80f2661e Merge pull request #1187 from apache/fix/WW-5517-debug
WW-5517 Fixes <s:debug/> to be compatible with allowlist capability
2025-01-21 18:01:26 +01:00
Lukasz Lenart 9ba5c095fb Merge pull request #1189 from apache/dependabot/maven/org.sitemesh-sitemesh-3.2.2
Bump org.sitemesh:sitemesh from 3.2.1 to 3.2.2
2025-01-21 18:01:05 +01:00
Lukasz Lenart 8f4fa06855 WW-5517 Adds a dedicate test case to cover allowlist 2025-01-20 09:10:02 +01:00
dependabot[bot] 181f0073ff Bump org.sitemesh:sitemesh from 3.2.1 to 3.2.2
Bumps [org.sitemesh:sitemesh](https://github.com/sitemesh/sitemesh3) from 3.2.1 to 3.2.2.
- [Release notes](https://github.com/sitemesh/sitemesh3/releases)
- [Commits](https://github.com/sitemesh/sitemesh3/compare/3.2.1...3.2.2)

---
updated-dependencies:
- dependency-name: org.sitemesh:sitemesh
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-20 01:07:47 +00:00
Lukasz Lenart f3997f705e WW-5517 Fixes <s:debug/> to be compatible with allowlist capability 2025-01-18 21:57:55 +01:00
Lukasz Lenart b054b51929 Merge pull request #1186 from apache/fix/nightlies-67
Adds 6.7.x branch to nigthlies step
2025-01-18 10:03:31 +01:00
Lukasz Lenart e0be8270ca Merge pull request #1185 from apache/feature/WW-5510-tooltip
WW-5510 Marks support for tooltips as deprecated
2025-01-18 09:46:36 +01:00
Lukasz Lenart 32cd5d0dcc Adds 6.7.x branch to nigthlies step 2025-01-18 09:45:51 +01:00
Lukasz Lenart c2a5bfe3c9 WW-5510 Marks support for tooltips as deprecated
This will be removed in the minor release, migrate to use native html tooltip support
2025-01-16 10:31:43 +01:00
Lukasz Lenart fa60e1ceb2 Merge pull request #1184 from apache/fix/WW-5501-exclude-s7
WW-5501 Only exclude malicious file names
2025-01-16 10:19:15 +01:00
Lukasz Lenart 4279511c5b WW-5501 Only exclude malicious file names 2025-01-16 08:34:57 +01:00
Lukasz Lenart 71bca19c13 Merge pull request #1176 from apache/dependabot/maven/byte-buddy.version-1.15.11
Bump byte-buddy.version from 1.14.11 to 1.15.11
2025-01-16 07:58:38 +01:00
Lukasz Lenart 30aefaa5d3 Merge pull request #1183 from apache/dependabot/github_actions/actions/upload-artifact-4.6.0
Bump actions/upload-artifact from 4.5.0 to 4.6.0
2025-01-16 07:57:28 +01:00
Lukasz Lenart 2c023a245d Merge pull request #1182 from apache/dependabot/github_actions/github/codeql-action-3.28.1
Bump github/codeql-action from 3.28.0 to 3.28.1
2025-01-16 07:57:10 +01:00
Lukasz Lenart cebdf6df82 Merge pull request #1178 from apache/dependabot/maven/log4j2.version-2.24.3
Bump log4j2.version from 2.24.2 to 2.24.3
2025-01-16 07:56:53 +01:00
dependabot[bot] 6d01916f12 Bump actions/upload-artifact from 4.5.0 to 4.6.0
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.5.0 to 4.6.0.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/6f51ac03b9356f520e9adb1b1b7802705f340c2b...65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-13 01:55:37 +00:00
dependabot[bot] 6cc4a105e6 Bump github/codeql-action from 3.28.0 to 3.28.1
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.0 to 3.28.1.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Commits](https://github.com/github/codeql-action/compare/v3.28.0...v3.28.1)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-13 01:55:35 +00:00
Kusal Kithul-Godage b5d94a1f07 Merge pull request #1179 from apache/excl-list
Add jakarta package to exclusion list
2025-01-06 21:23:41 +11:00
Kusal Kithul-Godage 9e93bb51db Add jakarta package to exclusion list 2025-01-06 13:46:21 +11:00
dependabot[bot] 0931b88b11 Bump log4j2.version from 2.24.2 to 2.24.3
Bumps `log4j2.version` from 2.24.2 to 2.24.3.

Updates `org.apache.logging.log4j:log4j-api` from 2.24.2 to 2.24.3

Updates `org.apache.logging.log4j:log4j-core` from 2.24.2 to 2.24.3

Updates `org.apache.logging.log4j:log4j-jcl` from 2.24.2 to 2.24.3

Updates `org.apache.logging.log4j:log4j-slf4j-impl` from 2.24.2 to 2.24.3

---
updated-dependencies:
- dependency-name: org.apache.logging.log4j:log4j-api
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.apache.logging.log4j:log4j-core
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.apache.logging.log4j:log4j-jcl
  dependency-type: direct:development
  update-type: version-update:semver-patch
- dependency-name: org.apache.logging.log4j:log4j-slf4j-impl
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-06 01:11:00 +00:00
dependabot[bot] 8dfc157be0 Bump byte-buddy.version from 1.14.11 to 1.15.11
Bumps `byte-buddy.version` from 1.14.11 to 1.15.11.

Updates `net.bytebuddy:byte-buddy` from 1.14.11 to 1.15.11
- [Release notes](https://github.com/raphw/byte-buddy/releases)
- [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md)
- [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.14.11...byte-buddy-1.15.11)

Updates `net.bytebuddy:byte-buddy-agent` from 1.14.11 to 1.15.11
- [Release notes](https://github.com/raphw/byte-buddy/releases)
- [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md)
- [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.14.11...byte-buddy-1.15.11)

---
updated-dependencies:
- dependency-name: net.bytebuddy:byte-buddy
  dependency-type: direct:development
  update-type: version-update:semver-minor
- dependency-name: net.bytebuddy:byte-buddy-agent
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-06 01:10:50 +00:00
Lukasz Lenart 98df24a2a0 Merge pull request #1171 from apache/fix/WW-5500-s7
WW-5500 Extends pattern to validate multipart uploads
2025-01-05 17:47:51 +01:00
Lukasz Lenart fd24a4a9c8 Merge pull request #1170 from apache/fix/WW-5498-s7
WW-5498 Adds devMode errors as action messages to avoid breaking validation logic
2025-01-05 17:47:14 +01:00
Lukasz Lenart 026300b456 Merge pull request #1133 from apache/dependabot/maven/commons-io-commons-io-2.18.0
Bump commons-io:commons-io from 2.15.1 to 2.18.0
2025-01-05 10:18:51 +01:00
Lukasz Lenart b36f7bd9cc Merge pull request #1173 from apache/fix/dependabot
Updates dependabot.yml
2025-01-05 10:07:14 +01:00
dependabot[bot] 93943dbb33 Bump commons-io:commons-io from 2.15.1 to 2.18.0
Bumps commons-io:commons-io from 2.15.1 to 2.18.0.

---
updated-dependencies:
- dependency-name: commons-io:commons-io
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-05 09:01:48 +00:00
Lukasz Lenart 18f3a49028 Updatex dependabot.yml
Stop ignoring any dependency
2025-01-05 09:54:29 +01:00
Lukasz Lenart 9e2f92f7af Merge pull request #1172 from apache/fix/supported-versions
Updates SECURITY.md
2025-01-05 09:46:22 +01:00
Lukasz Lenart f97e2c29bc Updates SECURITY.md 2025-01-05 09:34:58 +01:00
Lukasz Lenart bd46f587a4 Merge pull request #1164 from apache/dependabot/maven/org.apache.velocity-velocity-engine-core-2.4.1
Bump org.apache.velocity:velocity-engine-core from 2.3 to 2.4.1
2025-01-05 09:29:57 +01:00
Lukasz Lenart e5825de28e Merge pull request #1163 from apache/dependabot/maven/org.freemarker-freemarker-2.3.34
Bump org.freemarker:freemarker from 2.3.33 to 2.3.34
2025-01-05 09:26:32 +01:00
Lukasz Lenart 6d71674cc1 WW-5500 Extends pattern to validate multipart uploads 2025-01-05 09:21:08 +01:00
Lukasz Lenart db9684e1c1 WW-5498 Adds devMode errors as action messages to avoid breaking validation logic 2025-01-05 09:18:40 +01:00
Lukasz Lenart ac9a02c685 Merge pull request #1166 from apache/fix/sonar-scan
Removes checking branch ref and removes branch name as not needed
2025-01-04 14:50:48 +01:00
Lukasz Lenart 9ac86ed61b Merge pull request #1165 from apache/feature/yourkit
Add YourKit thank you section
2025-01-04 14:38:11 +01:00
Lukasz Lenart c0a720475b Removes checking branch ref and removes branch name as not needed 2025-01-03 16:29:55 +01:00
Lukasz Lenart cadb7a1fb2 Add YourKit thank you section 2025-01-03 08:53:00 +01:00
dependabot[bot] 4f1556c2d7 Bump org.apache.velocity:velocity-engine-core from 2.3 to 2.4.1
Bumps org.apache.velocity:velocity-engine-core from 2.3 to 2.4.1.

---
updated-dependencies:
- dependency-name: org.apache.velocity:velocity-engine-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-30 01:52:04 +00:00
dependabot[bot] 80b3108c86 Bump org.freemarker:freemarker from 2.3.33 to 2.3.34
Bumps org.freemarker:freemarker from 2.3.33 to 2.3.34.

---
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-12-30 01:52:00 +00:00
Lukasz Lenart 375d00aa8e Merge pull request #1162 from apache/fix/strict-branch
Uses exact branch name instead of wildcard
2024-12-24 10:23:11 +01:00
Lukasz Lenart 44ff04683a Uses exact branch name instead of wildcard 2024-12-24 08:32:08 +01:00
Lukasz Lenart 2850315cf3 Merge pull request #1159 from apache/feature/WW-5503-removes-deps
WW-5503 Removes unused dependencies
2024-12-24 07:52:41 +01:00
Lukasz Lenart e695ac8a4e Merge pull request #1161 from apache/feature/wildcard-packages
Adds additional test cases to match Struts packages
2024-12-24 07:15:44 +01:00
Lukasz Lenart 0114dff50d WW-5503 Removes unused dependencies 2024-12-24 07:14:49 +01:00
Lukasz Lenart 7c62bfc5a4 Merge pull request #1160 from ljharb/patch-1
[readme] add link to struts site commercial support page
2024-12-23 20:43:15 +01:00
Lukasz Lenart 3be56a4840 Adds additional test cases to match Struts packages 2024-12-23 19:16:57 +01:00
Jordan Harband a9662ee583 [readme] add link to struts site commercial support page
Link directly to https://struts.apache.org/commercial-support.html on the readme in the relevant section.
2024-12-23 10:04:16 -08:00
Lukasz Lenart 43295b1f42 Merge pull request #1152 from apache/dependabot/maven/org.codehaus.mojo-versions-maven-plugin-2.18.0
Bump org.codehaus.mojo:versions-maven-plugin from 2.17.1 to 2.18.0
2024-12-23 16:12:33 +01:00
Lukasz Lenart faa3e723aa Merge pull request #1154 from apache/dependabot/github_actions/github/codeql-action-3.28.0
Bump github/codeql-action from 3.27.9 to 3.28.0
2024-12-23 16:12:22 +01:00
Lukasz Lenart 92b2cb9ee1 Merge pull request #1155 from apache/dependabot/github_actions/actions/upload-artifact-4.5.0
Bump actions/upload-artifact from 4.4.3 to 4.5.0
2024-12-23 16:12:11 +01:00
Lukasz Lenart 36ca20d3dc Merge pull request #1157 from apache/feature/WW-5501-exclude-s7
WW-5501 Excludes malicious names
2024-12-23 15:09:05 +01:00
Lukasz Lenart bbdb38dedc WW-5501 Uses StringUtils.normalizeSpace instead of sanitizeNewlines 2024-12-23 14:26:30 +01:00
Lukasz Lenart 552f7eba77 WW-5501 Excludes malicious names 2024-12-23 12:39:57 +01:00
dependabot[bot] 196aa3f366 Bump actions/upload-artifact from 4.4.3 to 4.5.0
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.4.3 to 4.5.0.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882...6f51ac03b9356f520e9adb1b1b7802705f340c2b)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-23 02:01:04 +00:00
dependabot[bot] 0792d0586f Bump github/codeql-action from 3.27.9 to 3.28.0
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.27.9 to 3.28.0.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Commits](https://github.com/github/codeql-action/compare/v3.27.9...v3.28.0)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-23 02:01:01 +00:00
dependabot[bot] 6b07ee83ee Bump org.codehaus.mojo:versions-maven-plugin from 2.17.1 to 2.18.0
Bumps [org.codehaus.mojo:versions-maven-plugin](https://github.com/mojohaus/versions) from 2.17.1 to 2.18.0.
- [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.17.1...2.18.0)

---
updated-dependencies:
- dependency-name: org.codehaus.mojo:versions-maven-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-23 01:10:38 +00:00
Lukasz Lenart 0bcb6516bf Merge pull request #1151 from apache/feature/homepage
Adds info about homepage and descritpion to Github repo
2024-12-20 08:06:38 +01:00
Lukasz Lenart dba7510984 Adds info about homepage and descritpion to Github repo 2024-12-20 07:47:53 +01:00
Lukasz Lenart d7188c18d2 Merge pull request #1149 from apache/dependabot/github_actions/github/codeql-action-3.27.9
Bump github/codeql-action from 3.27.7 to 3.27.9
2024-12-18 15:42:00 +01:00
Lukasz Lenart 1655838220 Merge pull request #1148 from apache/dependabot/maven/org.codehaus.mojo-exec-maven-plugin-3.5.0
Bump org.codehaus.mojo:exec-maven-plugin from 3.4.1 to 3.5.0
2024-12-18 15:41:39 +01:00
Lukasz Lenart e9a12dd94f Merge pull request #1147 from apache/dependabot/maven/org.apache.maven.plugins-maven-failsafe-plugin-3.5.2
Bump org.apache.maven.plugins:maven-failsafe-plugin from 3.5.1 to 3.5.2
2024-12-18 15:41:18 +01:00
dependabot[bot] ddf80bec29 Bump github/codeql-action from 3.27.7 to 3.27.9
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.27.7 to 3.27.9.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Commits](https://github.com/github/codeql-action/compare/v3.27.7...v3.27.9)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-16 01:40:42 +00:00
dependabot[bot] e81caf5cec Bump org.codehaus.mojo:exec-maven-plugin from 3.4.1 to 3.5.0
Bumps [org.codehaus.mojo:exec-maven-plugin](https://github.com/mojohaus/exec-maven-plugin) from 3.4.1 to 3.5.0.
- [Release notes](https://github.com/mojohaus/exec-maven-plugin/releases)
- [Commits](https://github.com/mojohaus/exec-maven-plugin/compare/3.4.1...3.5.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-12-16 01:16:41 +00:00
dependabot[bot] 9d2eb5e607 Bump org.apache.maven.plugins:maven-failsafe-plugin from 3.5.1 to 3.5.2
Bumps [org.apache.maven.plugins:maven-failsafe-plugin](https://github.com/apache/maven-surefire) from 3.5.1 to 3.5.2.
- [Release notes](https://github.com/apache/maven-surefire/releases)
- [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.5.1...surefire-3.5.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-16 01:16:38 +00:00
Lukasz Lenart 9aa41f18ae [maven-release-plugin] prepare for next development iteration 2024-12-11 07:56:30 +01:00
65 changed files with 1396 additions and 402 deletions
+3 -1
View File
@@ -11,6 +11,8 @@ notifications:
jira_options: link label worklog
github:
description: "Apache Struts is a free, open-source, MVC framework for creating elegant, modern Java web applications"
homepage: https://struts.apache.org/
del_branch_on_merge: true
protected_branches:
main:
@@ -21,7 +23,7 @@ github:
# it does not work because our github teams are private/secret, see INFRA-25666
require_code_owner_reviews: false
required_approving_review_count: 0
release/*:
release/struts-6-7-x:
# contexts are the names of checks that must pass.
contexts:
- build
+1
View File
@@ -8,3 +8,4 @@ updates:
directory: "/"
schedule:
interval: "weekly"
ignore: []
+3 -3
View File
@@ -52,12 +52,12 @@ jobs:
java-version: 17
cache: 'maven'
- name: Initialize CodeQL
uses: github/codeql-action/init@v3.27.7
uses: github/codeql-action/init@v3.28.5
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v3.27.7
uses: github/codeql-action/autobuild@v3.28.5
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3.27.7
uses: github/codeql-action/analyze@v3.28.5
with:
category: "/language:${{matrix.language}}"
+2 -2
View File
@@ -58,13 +58,13 @@ jobs:
publish_results: true
- name: "Upload artifact"
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # 4.4.3
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # 4.6.0
with:
name: SARIF file
path: results.sarif
retention-days: 5
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@89757925c7adddb19b7a2f28e3e1b27da88b7304 # 2.22.11
uses: github/codeql-action/upload-sarif@e7c0c9d71b7bd108fd12e06b56fc58d3d154164d # 2.22.11
with:
sarif_file: results.sarif
+1 -3
View File
@@ -44,6 +44,4 @@ jobs:
- env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONARCLOUD_TOKEN }}
MAIN_BRANCH: ${{ github.ref || github.base_ref || github.ref || github.base_ref }}
if: ${{ github.ref == 'main' || github.base_ref == 'main' }}
run: ./mvnw -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.branch.name=${{ env.MAIN_BRANCH }} -Pcoverage -DskipAssembly
run: ./mvnw -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Pcoverage -DskipAssembly
Vendored
+3
View File
@@ -87,6 +87,7 @@ pipeline {
when {
anyOf {
branch 'main'
branch 'release/struts-6-7-x'
}
}
steps {
@@ -100,6 +101,7 @@ pipeline {
when {
anyOf {
branch 'main'
branch 'release/struts-6-7-x'
}
}
steps {
@@ -112,6 +114,7 @@ pipeline {
when {
anyOf {
branch 'main'
branch 'release/struts-6-7-x'
}
}
steps {
+8 -1
View File
@@ -103,8 +103,15 @@ the Struts approach.
## Commercial Support
The Apache Struts community does not offer commercial support by itself.
The Apache Struts community does not offer commercial support by itself, but we maintain [a list of companies offering
commercial support on our website](https://struts.apache.org/commercial-support.html).
Some Apache Struts maintainers are working with [Tidelift](https://tidelift.com/) to provide commercial support and
invest paid working time in the improvement of the Apache Struts framework. For more information, visit
the [Tidelift resources regarding Apache Struts](https://tidelift.com/subscription/pkg/maven-org-apache-struts-struts2-core?utm_source=maven-org-apache-struts-struts2-core&utm_medium=referral&utm_campaign=readme)
## Thank you
[YourKit](https://www.yourkit.com/) is kindly supporting open source projects with its full-featured Java Profiler.
YourKit is the creator of innovative and intelligent tools for profiling Java and .NET applications.
As an Apache committer, you can get a free license at [YourKit's open source sponsorship program](https://www.yourkit.com/java/profiler/purchase/#os_license).
+3 -2
View File
@@ -7,8 +7,9 @@ and what potential vulnerability it can have:
| Version | Supported |
|---------|--------------------|
| 6.x | :white_check_mark: |
| 2.5.x | :white_check_mark: |
| 7.x | :white_check_mark: |
| 6.7.x | :white_check_mark: |
| 2.5.x | ❌ |
## Reporting New Security Issues with thr Apache Struts
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>7.0.0</version>
<version>7.0.1</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>7.0.0</version>
<version>7.0.1</version>
</parent>
<artifactId>struts2-rest-showcase</artifactId>
<packaging>war</packaging>
<version>7.0.0</version>
<version>7.0.1</version>
<name>Struts 2 Rest Showcase Webapp</name>
<description>Struts 2 Rest Showcase Example</description>
+3 -3
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-apps</artifactId>
<version>7.0.0</version>
<version>7.0.1</version>
</parent>
<artifactId>struts2-showcase</artifactId>
@@ -123,7 +123,7 @@
<dependency>
<groupId>org.sitemesh</groupId>
<artifactId>sitemesh</artifactId>
<version>3.2.1</version>
<version>3.2.2</version>
</dependency>
<dependency>
@@ -207,7 +207,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.5.1</version>
<version>3.5.2</version>
<configuration>
<includes>
<include>it.org.apache.struts2.showcase.*Test</include>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>7.0.0</version>
<version>7.0.1</version>
</parent>
<artifactId>struts2-assembly</artifactId>
+4 -4
View File
@@ -25,11 +25,11 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>7.0.0</version>
<version>7.0.1</version>
</parent>
<artifactId>struts2-bom</artifactId>
<version>7.0.0</version>
<version>7.0.1</version>
<packaging>pom</packaging>
<name>Struts 2 Bill of Materials</name>
@@ -44,7 +44,7 @@
</licenses>
<properties>
<struts-version.version>7.0.0</struts-version.version>
<struts-version.version>7.0.1</struts-version.version>
<maven.site.skip>true</maven.site.skip>
<maven.site.deploy.skip>true</maven.site.deploy.skip>
</properties>
@@ -160,7 +160,7 @@
</dependencyManagement>
<scm>
<tag>STRUTS_7_0_0</tag>
<tag>STRUTS_7_0_1</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-parent</artifactId>
<version>7.0.0</version>
<version>7.0.1</version>
</parent>
<artifactId>struts2-core</artifactId>
<packaging>jar</packaging>
@@ -18,7 +18,10 @@
*/
package org.apache.struts2.components;
import org.apache.commons.lang3.ClassUtils;
import org.apache.struts2.inject.Inject;
import org.apache.struts2.ognl.ThreadAllowlist;
import org.apache.struts2.util.CompoundRoot;
import org.apache.struts2.util.ValueStack;
import org.apache.struts2.util.reflection.ReflectionProvider;
import jakarta.servlet.http.HttpServletRequest;
@@ -40,6 +43,7 @@ public class Debug extends UIBean {
protected ReflectionProvider reflectionProvider;
private ThreadAllowlist threadAllowlist;
public Debug(ValueStack stack, HttpServletRequest request, HttpServletResponse response) {
super(stack, request, response);
@@ -50,6 +54,11 @@ public class Debug extends UIBean {
this.reflectionProvider = prov;
}
@Inject
public void setThreadAllowlist(ThreadAllowlist threadAllowlist) {
this.threadAllowlist = threadAllowlist;
}
protected String getDefaultTemplate() {
return TEMPLATE;
}
@@ -59,16 +68,19 @@ public class Debug extends UIBean {
if (showDebug()) {
ValueStack stack = getStack();
Iterator iter = stack.getRoot().iterator();
List stackValues = new ArrayList(stack.getRoot().size());
allowList(stack.getRoot());
Iterator<Object> iter = stack.getRoot().iterator();
List<Object> stackValues = new ArrayList<>(stack.getRoot().size());
while (iter.hasNext()) {
Object o = iter.next();
Map values;
Map<String, Object> values;
try {
values = reflectionProvider.getBeanMap(o);
} catch (Exception e) {
throw new StrutsException("Caught an exception while getting the property values of " + o, e);
}
allowListClass(o);
stackValues.add(new DebugMapEntry(o.getClass().getName(), values));
}
@@ -77,6 +89,16 @@ public class Debug extends UIBean {
return result;
}
private void allowList(CompoundRoot root) {
root.forEach(this::allowListClass);
}
private void allowListClass(Object o) {
threadAllowlist.allowClass(o.getClass());
ClassUtils.getAllSuperclasses(o.getClass()).forEach(threadAllowlist::allowClass);
ClassUtils.getAllInterfaces(o.getClass()).forEach(threadAllowlist::allowClass);
}
@Override
public boolean end(Writer writer, String body) {
if (showDebug()) {
@@ -91,17 +113,17 @@ public class Debug extends UIBean {
return (devMode || Boolean.TRUE == PrepareOperations.getDevModeOverride());
}
private static class DebugMapEntry implements Map.Entry {
private final Object key;
private static class DebugMapEntry implements Map.Entry<String, Object> {
private final String key;
private Object value;
DebugMapEntry(Object key, Object value) {
DebugMapEntry(String key, Object value) {
this.key = key;
this.value = value;
}
@Override
public Object getKey() {
public String getKey() {
return key;
}
@@ -26,7 +26,6 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsConstants;
@@ -35,6 +34,7 @@ import org.apache.struts2.components.template.Template;
import org.apache.struts2.components.template.TemplateEngine;
import org.apache.struts2.components.template.TemplateEngineManager;
import org.apache.struts2.components.template.TemplateRenderingContext;
import org.apache.struts2.dispatcher.AttributeMap;
import org.apache.struts2.dispatcher.StaticContentLoader;
import org.apache.struts2.util.ComponentUtils;
import org.apache.struts2.util.TextProviderHelper;
@@ -48,6 +48,10 @@ import java.util.List;
import java.util.Map;
import java.util.function.Function;
import static java.util.Collections.emptyMap;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.struts2.dispatcher.DispatcherConstants.ATTRIBUTES;
/**
* <p>
* UIBean is the standard superclass of all Struts UI components.
@@ -300,7 +304,7 @@ import java.util.function.Function;
* <!-- END SNIPPET: javascriptRelatedAttributes -->
*
* <!-- START SNIPPET: tooltipattributes -->
*
* <strong>Deprecated since 7.0.1</strong
* <table border="1" summary="">
* <tr>
* <td>Attribute</td>
@@ -344,7 +348,7 @@ import java.util.function.Function;
*
* <!-- START SNIPPET: tooltipdescription -->
* <p>
* <b>tooltipConfig is deprecated, use individual tooltip configuration attributes instead </b>
* <strong>tooltipConfig is deprecated, use individual tooltip configuration attributes instead </strong>
* </p>
*
* <p>
@@ -380,7 +384,7 @@ import java.util.function.Function;
*
* <pre>
* <!-- START SNIPPET: tooltipexample -->
*
* <strong>Deprecated since 7.0.1</strong>
* &lt;!-- Example 1: --&gt;
* &lt;s:form
* tooltipDelay="500"
@@ -440,6 +444,9 @@ public abstract class UIBean extends Component {
private static final Logger LOG = LogManager.getLogger(UIBean.class);
static final String TEMPLATE_DIR = "templateDir";
static final String THEME = "theme";
protected static final String ATTR_FIELD_VALUE = "fieldValue";
protected static final String ATTR_NAME_VALUE = "nameValue";
protected static final String ATTR_VALUE = "value";
@@ -602,13 +609,18 @@ public abstract class UIBean extends Component {
result = findString(this.templateDir);
}
// Check Request, Session, Application scopes
if (isBlank(result)) {
result = (String) getAttrMap().get(TEMPLATE_DIR);
}
// Default template set
if (StringUtils.isBlank(result)) {
if (isBlank(result)) {
result = defaultTemplateDir;
}
// Defaults to 'template'
if (StringUtils.isBlank(result)) {
if (isBlank(result)) {
result = "template";
}
@@ -622,26 +634,36 @@ public abstract class UIBean extends Component {
result = findString(this.theme);
}
if (StringUtils.isBlank(result)) {
if (isBlank(result)) {
Form form = (Form) findAncestor(Form.class);
if (form != null) {
result = form.getTheme();
}
}
// Check Request, Session, Application scopes
if (isBlank(result)) {
result = (String) getAttrMap().get(THEME);
}
// Default theme set
if (StringUtils.isBlank(result)) {
if (isBlank(result)) {
result = defaultUITheme;
}
return result;
}
private Map<String, Object> getAttrMap() {
AttributeMap attrMap = (AttributeMap) getStack().getContext().get(ATTRIBUTES);
return attrMap != null ? attrMap : emptyMap();
}
public void evaluateParams() {
String gotTheme = getTheme();
addParameter("templateDir", getTemplateDir());
addParameter("theme", gotTheme);
addParameter(TEMPLATE_DIR, getTemplateDir());
addParameter(THEME, gotTheme);
addParameter("template", template != null ? findString(template) : getDefaultTemplate());
addParameter("dynamicAttributes", dynamicAttributes);
addParameter("themeExpansionToken", uiThemeExpansionToken);
@@ -968,6 +990,7 @@ public abstract class UIBean extends Component {
}
}
@Deprecated(since = "7.0.1", forRemoval = true)
protected Map<String, String> getTooltipConfig(UIBean component) {
Object tooltipConfigObj = component.getAttributes().get("tooltipConfig");
Map<String, String> result = new LinkedHashMap<>();
@@ -1237,11 +1260,13 @@ public abstract class UIBean extends Component {
}
@StrutsTagAttribute(description="Set the tooltip of this particular component")
@Deprecated(since = "7.0.1", forRemoval = true)
public void setTooltip(String tooltip) {
this.tooltip = tooltip;
}
@StrutsTagAttribute(description="Deprecated. Use individual tooltip configuration attributes instead.")
@Deprecated(since = "7.0.1", forRemoval = true)
public void setTooltipConfig(String tooltipConfig) {
this.tooltipConfig = tooltipConfig;
}
@@ -1252,22 +1277,26 @@ public abstract class UIBean extends Component {
}
@StrutsTagAttribute(description="Use JavaScript to generate tooltips", type="Boolean", defaultValue="false")
@Deprecated(since = "7.0.1", forRemoval = true)
public void setJavascriptTooltip(String javascriptTooltip) {
this.javascriptTooltip = javascriptTooltip;
}
@StrutsTagAttribute(description="CSS class applied to JavaScrip tooltips", defaultValue="StrutsTTClassic")
@Deprecated(since = "7.0.1", forRemoval = true)
public void setTooltipCssClass(String tooltipCssClass) {
this.tooltipCssClass = tooltipCssClass;
}
@StrutsTagAttribute(description="Delay in milliseconds, before showing JavaScript tooltips ",
defaultValue="Classic")
@Deprecated(since = "7.0.1", forRemoval = true)
public void setTooltipDelay(String tooltipDelay) {
this.tooltipDelay = tooltipDelay;
}
@StrutsTagAttribute(description="Icon path used for image that will have the tooltip")
@Deprecated(since = "7.0.1", forRemoval = true)
public void setTooltipIconPath(String tooltipIconPath) {
this.tooltipIconPath = tooltipIconPath;
}
@@ -111,7 +111,7 @@ public class Dispatcher {
*/
public static final String REQUEST_POST_METHOD = "POST";
public static final String MULTIPART_FORM_DATA_REGEX = "^multipart/form-data(?:\\s*;\\s*boundary=[0-9a-zA-Z'()+_,\\-./:=?]{1,70})?(?:\\s*;\\s*charset=[a-zA-Z\\-0-9]{3,14})?";
public static final String MULTIPART_FORM_DATA_REGEX = "^multipart/form-data(?:\\s*;\\s*boundary=[0-9a-zA-Z'\"()+_,\\-./:=?]{1,70})?(?:\\s*;\\s*charset=[a-zA-Z\\-0-9]{3,14})?";
private static final String CONFIG_SPLIT_REGEX = "\\s*,\\s*";
@@ -19,6 +19,8 @@
package org.apache.struts2.dispatcher.multipart;
import org.apache.struts2.inject.Inject;
import org.apache.struts2.security.DefaultExcludedPatternsChecker;
import org.apache.struts2.security.ExcludedPatternsChecker;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload2.core.FileUploadByteCountLimitException;
import org.apache.commons.fileupload2.core.FileUploadContentTypeException;
@@ -52,6 +54,8 @@ public abstract class AbstractMultiPartRequest implements MultiPartRequest {
private static final Logger LOG = LogManager.getLogger(AbstractMultiPartRequest.class);
private static final String EXCLUDED_FILE_PATTERN = ".*[<>&\"'|;\\\\/?*:]+.*|.*\\.\\..*";
/**
* Defines the internal buffer size used during streaming operations.
*/
@@ -107,6 +111,14 @@ public abstract class AbstractMultiPartRequest implements MultiPartRequest {
*/
protected Map<String, List<String>> parameters = new HashMap<>();
private final ExcludedPatternsChecker patternsChecker;
protected AbstractMultiPartRequest() {
patternsChecker = new DefaultExcludedPatternsChecker();
((DefaultExcludedPatternsChecker) patternsChecker).setAdditionalExcludePatterns(EXCLUDED_FILE_PATTERN);
}
/**
* @param bufferSize Sets the buffer size to be used.
*/
@@ -296,6 +308,10 @@ public abstract class AbstractMultiPartRequest implements MultiPartRequest {
return fileName;
}
/**
* @deprecated since 7.0.1, use {@link StringUtils#normalizeSpace(String)} instead
*/
@Deprecated
protected String sanitizeNewlines(String before) {
return before.replaceAll("\\R", "_");
}
@@ -413,4 +429,12 @@ public abstract class AbstractMultiPartRequest implements MultiPartRequest {
}
}
/**
* @param fileName file name to check
* @return true if the file name is excluded
*/
protected boolean isExcluded(String fileName) {
return patternsChecker.isExcluded(fileName).isExcluded();
}
}
@@ -32,6 +32,8 @@ import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import static org.apache.commons.lang3.StringUtils.normalizeSpace;
/**
* Multipart form data request adapter for Jakarta Commons FileUpload package.
*/
@@ -47,11 +49,11 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
prepareServletFileUpload(charset, Path.of(saveDir));
for (DiskFileItem item : servletFileUpload.parseRequest(request)) {
LOG.debug(() -> "Processing a form field: " + sanitizeNewlines(item.getFieldName()));
LOG.debug(() -> "Processing a form field: " + normalizeSpace(item.getFieldName()));
if (item.isFormField()) {
processNormalFormField(item, charset);
} else {
LOG.debug(() -> "Processing a file: " + sanitizeNewlines(item.getFieldName()));
LOG.debug(() -> "Processing a file: " + normalizeSpace(item.getFieldName()));
processFileField(item);
}
}
@@ -77,6 +79,11 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
protected void processNormalFormField(DiskFileItem item, Charset charset) throws IOException {
LOG.debug("Item: {} is a normal form field", item.getName());
if (isExcluded(item.getFieldName())) {
LOG.warn(() -> "Form field [%s] is rejected!".formatted(normalizeSpace(item.getFieldName())));
return;
}
List<String> values;
String fieldName = item.getFieldName();
if (parameters.get(fieldName) != null) {
@@ -98,9 +105,19 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
}
protected void processFileField(DiskFileItem item) {
if (isExcluded(item.getName())) {
LOG.warn(() -> "File name [%s] is not accepted".formatted(normalizeSpace(item.getName())));
return;
}
if (isExcluded(item.getFieldName())) {
LOG.warn(() -> "Field name [%s] is not accepted".formatted(normalizeSpace(item.getFieldName())));
return;
}
// Skip file uploads that don't have a file name - meaning that no file was selected.
if (item.getName() == null || item.getName().trim().isEmpty()) {
LOG.debug(() -> "No file has been uploaded for the field: " + sanitizeNewlines(item.getFieldName()));
LOG.debug(() -> "No file has been uploaded for the field: " + normalizeSpace(item.getFieldName()));
return;
}
@@ -112,7 +129,7 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
}
if (item.isInMemory()) {
LOG.warn("Storing uploaded files just in memory isn't supported currently, skipping file: {}!", item.getName());
LOG.warn(() -> "Storing uploaded files just in memory isn't supported currently, skipping file: %s!".formatted(normalizeSpace(item.getName())));
} else {
UploadedFile uploadedFile = StrutsUploadedFile.Builder
.create(item.getPath().toFile())
@@ -42,6 +42,8 @@ import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static org.apache.commons.lang3.StringUtils.normalizeSpace;
/**
* Multi-part form data request adapter for Jakarta Commons FileUpload package that
* leverages the streaming API rather than the traditional non-streaming API.
@@ -71,10 +73,10 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
LOG.debug("Using Jakarta Stream API to process request");
servletFileUpload.getItemIterator(request).forEachRemaining(item -> {
if (item.isFormField()) {
LOG.debug(() -> "Processing a form field: " + sanitizeNewlines(item.getFieldName()));
LOG.debug(() -> "Processing a form field: " + normalizeSpace(item.getFieldName()));
processFileItemAsFormField(item);
} else {
LOG.debug(() -> "Processing a file: " + sanitizeNewlines(item.getFieldName()));
LOG.debug(() -> "Processing a file: " + normalizeSpace(item.getFieldName()));
processFileItemAsFileField(item, location);
}
});
@@ -114,6 +116,11 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
String fieldName = fileItemInput.getFieldName();
String fieldValue = readStream(fileItemInput.getInputStream());
if (isExcluded(fieldName)) {
LOG.warn(() -> "Form field [%s] is rejected!".formatted(normalizeSpace(fieldName)));
return;
}
if (exceedsMaxStringLength(fieldName, fieldValue)) {
return;
}
@@ -141,7 +148,7 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
if (maxFiles != null && maxFiles == uploadedFiles.size()) {
if (LOG.isDebugEnabled()) {
LOG.debug("Cannot accept another file: {} as it will exceed max files: {}",
sanitizeNewlines(fileItemInput.getName()), maxFiles);
normalizeSpace(fileItemInput.getName()), maxFiles);
}
LocalizedMessage errorMessage = buildErrorMessage(
FileUploadFileCountLimitException.class,
@@ -160,7 +167,7 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
private void exceedsMaxSizeOfFiles(FileItemInput fileItemInput, File file, Long currentFilesSize) {
if (LOG.isDebugEnabled()) {
LOG.debug("File: {} of size: {} exceeds allowed max size: {}, actual size of already uploaded files: {}",
sanitizeNewlines(fileItemInput.getName()), file.length(), maxSizeOfFiles, currentFilesSize
normalizeSpace(fileItemInput.getName()), file.length(), maxSizeOfFiles, currentFilesSize
);
}
LocalizedMessage errorMessage = buildErrorMessage(
@@ -174,7 +181,7 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
}
if (!file.delete() && LOG.isWarnEnabled()) {
LOG.warn("Cannot delete file: {} which exceeds maximum size: {} of all files!",
sanitizeNewlines(fileItemInput.getName()), maxSizeOfFiles);
normalizeSpace(fileItemInput.getName()), maxSizeOfFiles);
}
}
@@ -187,7 +194,12 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
protected void processFileItemAsFileField(FileItemInput fileItemInput, Path location) throws IOException {
// Skip file uploads that don't have a file name - meaning that no file was selected.
if (fileItemInput.getName() == null || fileItemInput.getName().trim().isEmpty()) {
LOG.debug(() -> "No file has been uploaded for the field: " + sanitizeNewlines(fileItemInput.getFieldName()));
LOG.debug(() -> "No file has been uploaded for the field: " + normalizeSpace(fileItemInput.getFieldName()));
return;
}
if (isExcluded(fileItemInput.getName())) {
LOG.warn(() -> "File field [%s] rejected".formatted(normalizeSpace(fileItemInput.getName())));
return;
}
@@ -230,7 +242,9 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
InputStream input = fileItemInput.getInputStream();
try (OutputStream output = new BufferedOutputStream(Files.newOutputStream(file.toPath()), bufferSize)) {
byte[] buffer = new byte[bufferSize];
LOG.debug("Streaming file: {} using buffer size: {}", fileItemInput.getName(), bufferSize);
if (LOG.isDebugEnabled()) {
LOG.debug("Streaming file: {} using buffer size: {}", normalizeSpace(fileItemInput.getName()), bufferSize);
}
for (int length; ((length = input.read(buffer)) > 0); ) {
output.write(buffer, 0, length);
}
@@ -242,6 +242,41 @@ class ContainerImpl implements Container {
return toArray(parameterInjectors);
}
/**
* Gets parameter injectors with nulls for optional dependencies.
*
* @param member to which the parameters belong
* @param annotations on the parameters
* @param parameterTypes parameter types
* @return injections
*/
<M extends AccessibleObject & Member> List<ParameterInjector<?>> getParametersInjectorsWithNulls(
M member,
Annotation[][] annotations,
Class<?>[] parameterTypes,
String defaultName
) throws MissingDependencyException {
final List<ParameterInjector<?>> parameterInjectors = new ArrayList<>();
final Iterator<Annotation[]> annotationsIterator = Arrays.asList(annotations).iterator();
for (Class<?> parameterType : parameterTypes) {
Inject annotation = findInject(annotationsIterator.next());
String name = annotation == null ? defaultName : annotation.value();
Key<?> key = Key.newInstance(parameterType, name);
try {
parameterInjectors.add(createParameterInjector(key, member));
} catch (MissingDependencyException e) {
if (annotation != null && annotation.required()) {
throw e;
} else {
parameterInjectors.add(createNullParameterInjector(key, member));
}
}
}
return parameterInjectors;
}
<T> ParameterInjector<T> createParameterInjector(Key<T> key, Member member) throws MissingDependencyException {
final InternalFactory<? extends T> factory = getFactory(key);
if (factory == null) {
@@ -252,6 +287,23 @@ class ContainerImpl implements Container {
return new ParameterInjector<>(externalContext, factory);
}
<T> ParameterInjector<T> createNullParameterInjector(Key<T> key, Member member) {
final InternalFactory<? extends T> factory = new InternalFactory<>() {
@Override
public T create(InternalContext context) {
return null;
}
@Override
public Class<? extends T> type() {
return key.getType();
}
};
final ExternalContext<T> externalContext = ExternalContext.newInstance(member, key, this);
return new ParameterInjector<>(externalContext, factory);
}
private ParameterInjector<?>[] toArray(List<ParameterInjector<?>> parameterInjections) {
return parameterInjections.toArray(new ParameterInjector[0]);
}
@@ -339,7 +391,7 @@ class ContainerImpl implements Container {
MissingDependencyException exception = null;
Inject inject = null;
ParameterInjector<?>[] parameters = null;
List<ParameterInjector<?>> parameters = null;
try {
inject = constructor.getAnnotation(Inject.class);
@@ -347,7 +399,7 @@ class ContainerImpl implements Container {
} catch (MissingDependencyException e) {
exception = e;
}
parameterInjectors = parameters;
parameterInjectors = parameters != null ? container.toArray(parameters) : null;
if (exception != null) {
if (inject != null && inject.required()) {
@@ -357,11 +409,11 @@ class ContainerImpl implements Container {
injectors = container.injectors.get(implementation);
}
ParameterInjector<?>[] constructParameterInjector(
List<ParameterInjector<?>> constructParameterInjector(
Inject inject, ContainerImpl container, Constructor<T> constructor) throws MissingDependencyException {
return constructor.getParameterTypes().length == 0
? null // default constructor.
: container.getParametersInjectors(
: container.getParametersInjectorsWithNulls(
constructor,
constructor.getParameterAnnotations(),
constructor.getParameterTypes(),
@@ -18,11 +18,14 @@
*/
package org.apache.struts2.interceptor;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.ActionInvocation;
import org.apache.struts2.config.entities.ExceptionMappingConfig;
import org.apache.struts2.dispatcher.HttpParameters;
import org.apache.struts2.inject.Inject;
import org.apache.struts2.ognl.ThreadAllowlist;
import java.util.List;
import java.util.Map;
@@ -42,11 +45,11 @@ import java.util.Map;
* you make this interceptor the first interceptor on the stack, ensuring that it has full access to catch any
* exception, even those caused by other interceptors.
* </p>
*
* <p>
* <!-- END SNIPPET: description -->
*
* <p><u>Interceptor parameters:</u></p>
*
* <p>
* <!-- START SNIPPET: parameters -->
*
* <ul>
@@ -64,11 +67,11 @@ import java.util.Map;
* The parameters above enables us to log all thrown exceptions with stacktace in our own logfile,
* and present a friendly webpage (with no stacktrace) to the end user.
* </p>
*
* <p>
* <!-- END SNIPPET: parameters -->
*
* <p><u>Extending the interceptor:</u></p>
*
* <p>
* <!-- START SNIPPET: extending -->
* <p>
* If you want to add custom handling for publishing the Exception, you may override
@@ -158,11 +161,17 @@ public class ExceptionMappingInterceptor extends AbstractInterceptor {
private static final Logger LOG = LogManager.getLogger(ExceptionMappingInterceptor.class);
private transient ThreadAllowlist threadAllowlist;
protected Logger categoryLogger;
protected boolean logEnabled = false;
protected String logCategory;
protected String logLevel;
@Inject
public void setThreadAllowlist(ThreadAllowlist threadAllowlist) {
this.threadAllowlist = threadAllowlist;
}
public boolean isLogEnabled() {
return logEnabled;
@@ -173,20 +182,20 @@ public class ExceptionMappingInterceptor extends AbstractInterceptor {
}
public String getLogCategory() {
return logCategory;
}
return logCategory;
}
public void setLogCategory(String logCatgory) {
this.logCategory = logCatgory;
}
public void setLogCategory(String logCategory) {
this.logCategory = logCategory;
}
public String getLogLevel() {
return logLevel;
}
public String getLogLevel() {
return logLevel;
}
public void setLogLevel(String logLevel) {
this.logLevel = logLevel;
}
public void setLogLevel(String logLevel) {
this.logLevel = logLevel;
}
@Override
public String intercept(ActionInvocation invocation) throws Exception {
@@ -200,13 +209,16 @@ public class ExceptionMappingInterceptor extends AbstractInterceptor {
}
List<ExceptionMappingConfig> exceptionMappings = invocation.getProxy().getConfig().getExceptionMappings();
ExceptionMappingConfig mappingConfig = this.findMappingFromExceptions(exceptionMappings, e);
if (mappingConfig != null && mappingConfig.getResult()!=null) {
if (mappingConfig != null && mappingConfig.getResult() != null) {
Map<String, String> mappingParams = mappingConfig.getParams();
// create a mutable HashMap since some interceptors will remove parameters, and parameterMap is immutable
HttpParameters parameters = HttpParameters.create(mappingParams).build();
invocation.getInvocationContext().withParameters(parameters);
result = mappingConfig.getResult();
publishException(invocation, new ExceptionHolder(e));
ExceptionHolder holder = new ExceptionHolder(e);
threadAllowlist.allowClass(holder.getClass());
threadAllowlist.allowClass(e.getClass());
publishException(invocation, holder);
} else {
throw e;
}
@@ -221,55 +233,45 @@ public class ExceptionMappingInterceptor extends AbstractInterceptor {
* @param e the exception to log.
*/
protected void handleLogging(Exception e) {
if (logCategory != null) {
if (categoryLogger == null) {
// init category logger
categoryLogger = LogManager.getLogger(logCategory);
}
doLog(categoryLogger, e);
} else {
doLog(LOG, e);
}
if (logCategory != null) {
if (categoryLogger == null) {
// init category logger
categoryLogger = LogManager.getLogger(logCategory);
}
doLog(categoryLogger, e);
} else {
doLog(LOG, e);
}
}
/**
* Performs the actual logging.
*
* @param logger the provided logger to use.
* @param e the exception to log.
* @param logger the provided logger to use.
* @param e the exception to log.
*/
protected void doLog(Logger logger, Exception e) {
if (logLevel == null) {
logger.debug(e.getMessage(), e);
return;
}
if (logLevel == null) {
logger.debug(e.getMessage(), e);
return;
}
if ("trace".equalsIgnoreCase(logLevel)) {
logger.trace(e.getMessage(), e);
} else if ("debug".equalsIgnoreCase(logLevel)) {
logger.debug(e.getMessage(), e);
} else if ("info".equalsIgnoreCase(logLevel)) {
logger.info(e.getMessage(), e);
} else if ("warn".equalsIgnoreCase(logLevel)) {
logger.warn(e.getMessage(), e);
} else if ("error".equalsIgnoreCase(logLevel)) {
logger.error(e.getMessage(), e);
} else if ("fatal".equalsIgnoreCase(logLevel)) {
logger.fatal(e.getMessage(), e);
} else {
throw new IllegalArgumentException("LogLevel [" + logLevel + "] is not supported");
}
Level level = Level.getLevel(logLevel);
if (level == null) {
throw new IllegalArgumentException("LogLevel [" + logLevel + "] is not supported");
}
logger.log(level, e.getMessage(), e);
}
/**
* Try to find appropriate {@link ExceptionMappingConfig} based on provided Throwable
*
* @param exceptionMappings list of defined exception mappings
* @param t caught exception
* @param t caught exception
* @return appropriate mapping or null
*/
protected ExceptionMappingConfig findMappingFromExceptions(List<ExceptionMappingConfig> exceptionMappings, Throwable t) {
ExceptionMappingConfig config = null;
ExceptionMappingConfig config = null;
// Check for specific exception mappings.
if (exceptionMappings != null) {
int deepest = Integer.MAX_VALUE;
@@ -288,15 +290,15 @@ public class ExceptionMappingInterceptor extends AbstractInterceptor {
* Return the depth to the superclass matching. 0 means ex matches exactly. Returns -1 if there's no match.
* Otherwise, returns depth. Lowest depth wins.
*
* @param exceptionMapping the mapping classname
* @param t the cause
* @param exceptionMapping the mapping classname
* @param t the cause
* @return the depth, if not found -1 is returned.
*/
public int getDepth(String exceptionMapping, Throwable t) {
return getDepth(exceptionMapping, t.getClass(), 0);
}
private int getDepth(String exceptionMapping, Class exceptionClass, int depth) {
private int getDepth(String exceptionMapping, Class<?> exceptionClass, int depth) {
if (exceptionClass.getName().contains(exceptionMapping)) {
// Found it!
return depth;
@@ -312,7 +314,7 @@ public class ExceptionMappingInterceptor extends AbstractInterceptor {
* Default implementation to handle ExceptionHolder publishing. Pushes given ExceptionHolder on the stack.
* Subclasses may override this to customize publishing.
*
* @param invocation The invocation to publish Exception for.
* @param invocation The invocation to publish Exception for.
* @param exceptionHolder The exceptionHolder wrapping the Exception to publish.
*/
protected void publishException(ActionInvocation invocation, ExceptionHolder exceptionHolder) {
@@ -18,21 +18,23 @@
*/
package org.apache.struts2.interceptor.debugging;
import org.apache.struts2.ActionContext;
import org.apache.struts2.ActionInvocation;
import org.apache.struts2.inject.Inject;
import org.apache.struts2.interceptor.AbstractInterceptor;
import org.apache.struts2.util.ValueStack;
import org.apache.struts2.util.reflection.ReflectionProvider;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.ClassUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.ActionContext;
import org.apache.struts2.ActionInvocation;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.dispatcher.DispatcherConstants;
import org.apache.struts2.dispatcher.Parameter;
import org.apache.struts2.dispatcher.PrepareOperations;
import org.apache.struts2.dispatcher.RequestMap;
import org.apache.struts2.inject.Inject;
import org.apache.struts2.interceptor.AbstractInterceptor;
import org.apache.struts2.ognl.ThreadAllowlist;
import org.apache.struts2.util.ValueStack;
import org.apache.struts2.util.reflection.ReflectionProvider;
import org.apache.struts2.views.freemarker.FreemarkerManager;
import org.apache.struts2.views.freemarker.FreemarkerResult;
@@ -101,10 +103,10 @@ public class DebuggingInterceptor extends AbstractInterceptor {
private final String[] ignorePrefixes = new String[]{"org.apache.struts.", "org.apache.struts2.", "xwork."};
private final Set<String> ignoreKeys = Set.of(
DispatcherConstants.APPLICATION,
DispatcherConstants.SESSION,
DispatcherConstants.PARAMETERS,
DispatcherConstants.REQUEST
DispatcherConstants.APPLICATION,
DispatcherConstants.SESSION,
DispatcherConstants.PARAMETERS,
DispatcherConstants.REQUEST
);
private final static String XML_MODE = "xml";
@@ -126,6 +128,7 @@ public class DebuggingInterceptor extends AbstractInterceptor {
private boolean consoleEnabled = false;
private ReflectionProvider reflectionProvider;
private transient ThreadAllowlist threadAllowlist;
@Inject(StrutsConstants.STRUTS_DEVMODE)
public void setDevMode(String mode) {
@@ -142,6 +145,11 @@ public class DebuggingInterceptor extends AbstractInterceptor {
this.reflectionProvider = reflectionProvider;
}
@Inject
public void setThreadAllowlist(ThreadAllowlist threadAllowlist) {
this.threadAllowlist = threadAllowlist;
}
/*
* (non-Javadoc)
*
@@ -200,7 +208,7 @@ public class DebuggingInterceptor extends AbstractInterceptor {
res.setContentType("text/plain");
try (PrintWriter writer =
ServletActionContext.getResponse().getWriter()) {
ServletActionContext.getResponse().getWriter()) {
writer.print(stack.findValue(cmd));
} catch (IOException ex) {
LOG.warn("Interceptor in: {} mode has failed!", COMMAND_MODE, ex);
@@ -217,6 +225,7 @@ public class DebuggingInterceptor extends AbstractInterceptor {
String decorate = getParameter(DECORATE_PARAM);
ValueStack stack = ctx.getValueStack();
Object rootObject = stack.findValue(rootObjectExpression);
allowListClass(rootObject);
try (StringWriter writer = new StringWriter()) {
ObjectToHTMLWriter htmlWriter = new ObjectToHTMLWriter(writer);
@@ -228,8 +237,9 @@ public class DebuggingInterceptor extends AbstractInterceptor {
//on the first request, response can be decorated
//but we need plain text on the other ones
if ("false".equals(decorate))
if ("false".equals(decorate)) {
ServletActionContext.getRequest().setAttribute("decorator", "none");
}
FreemarkerResult result = new FreemarkerResult();
result.setFreemarkerManager(freemarkerManager);
@@ -239,7 +249,6 @@ public class DebuggingInterceptor extends AbstractInterceptor {
} catch (Exception ex) {
LOG.error("Unable to create debugging console", ex);
}
});
}
}
@@ -262,6 +271,14 @@ public class DebuggingInterceptor extends AbstractInterceptor {
}
}
private void allowListClass(Object o) {
if (o != null) {
threadAllowlist.allowClass(o.getClass());
ClassUtils.getAllSuperclasses(o.getClass()).forEach(threadAllowlist::allowClass);
ClassUtils.getAllInterfaces(o.getClass()).forEach(threadAllowlist::allowClass);
}
}
/**
* Gets a single string from the request parameters
*
@@ -277,12 +294,11 @@ public class DebuggingInterceptor extends AbstractInterceptor {
* Prints the current context to the response in XML format.
*/
protected void printContext() {
HttpServletResponse res = ServletActionContext.getResponse();
res.setContentType("text/xml");
HttpServletResponse response = ActionContext.getContext().getServletResponse();
response.setContentType("text/xml");
try {
PrettyPrintWriter writer = new PrettyPrintWriter(
ServletActionContext.getResponse().getWriter());
PrettyPrintWriter writer = new PrettyPrintWriter(response.getWriter());
printContext(writer);
writer.close();
} catch (IOException ex) {
@@ -311,6 +327,7 @@ public class DebuggingInterceptor extends AbstractInterceptor {
}
}
if (print) {
allowListClass(ctxMap.get(key));
serializeIt(ctxMap.get(key), key, writer, new ArrayList<>());
}
}
@@ -426,5 +443,3 @@ public class DebuggingInterceptor extends AbstractInterceptor {
return filter;
}
}
@@ -25,8 +25,6 @@ import org.apache.commons.lang3.BooleanUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.ognl.ProviderAllowlist;
import org.apache.struts2.ognl.ThreadAllowlist;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
@@ -36,7 +36,8 @@ public class DefaultExcludedPatternsChecker implements ExcludedPatternsChecker {
private static final Logger LOG = LogManager.getLogger(DefaultExcludedPatternsChecker.class);
public static final String[] EXCLUDED_PATTERNS = {
"(^|\\%\\{)((#?)(top(\\.|\\['|\\[\")|\\[\\d\\]\\.)?)(dojo|struts|session|request|response|application|servlet(Request|Response|Context)|parameters|context|_memberAccess)(\\.|\\[).*",
"(^|\\%\\{)(#?top\\.)[^\\s]*",
"(^|\\%\\{)((#?)(\\[\\d\\]\\.)?)(dojo|struts|session|request|response|application|servlet(Request|Response|Context)|parameters|context|_memberAccess)(\\.|\\[).*",
".*(^|\\.|\\[|\\'|\"|get)class(\\(\\.|\\[|\\'|\").*",
"actionErrors|actionMessages|fieldErrors"
};
@@ -38,7 +38,7 @@ public final class DebugUtils {
}
log.error(message);
if (action instanceof ValidationAware validationAware) {
validationAware.addActionError(message);
validationAware.addActionMessage(message);
}
}
@@ -29,6 +29,8 @@ import org.apache.struts2.dispatcher.AttributeMap;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.jsp.PageContext;
import static org.apache.struts2.dispatcher.DispatcherConstants.ATTRIBUTES;
public class TagUtils {
private static final Logger LOG = LogManager.getLogger(TagUtils.class);
@@ -46,8 +48,8 @@ public class TagUtils {
} else {
LOG.trace("Adds the current PageContext to ActionContext");
stack.getActionContext()
.withPageContext(pageContext)
.with("attr", new AttributeMap(stack.getContext()));
.withPageContext(pageContext)
.with(ATTRIBUTES, new AttributeMap(stack.getContext()));
}
return stack;
@@ -261,10 +261,12 @@ public abstract class AbstractUITag extends ComponentTagSupport implements Dynam
this.onchange = onchange;
}
@Deprecated(since = "7.0.1", forRemoval = true)
public void setTooltip(String tooltip) {
this.tooltip = tooltip;
}
@Deprecated(since = "7.0.1", forRemoval = true)
public void setTooltipConfig(String tooltipConfig) {
this.tooltipConfig = tooltipConfig;
}
@@ -277,18 +279,22 @@ public abstract class AbstractUITag extends ComponentTagSupport implements Dynam
this.key = key;
}
@Deprecated(since = "7.0.1", forRemoval = true)
public void setJavascriptTooltip(String javascriptTooltip) {
this.javascriptTooltip = javascriptTooltip;
}
@Deprecated(since = "7.0.1", forRemoval = true)
public void setTooltipCssClass(String tooltipCssClass) {
this.tooltipCssClass = tooltipCssClass;
}
@Deprecated(since = "7.0.1", forRemoval = true)
public void setTooltipDelay(String tooltipDelay) {
this.tooltipDelay = tooltipDelay;
}
@Deprecated(since = "7.0.1", forRemoval = true)
public void setTooltipIconPath(String tooltipIconPath) {
this.tooltipIconPath = tooltipIconPath;
}
@@ -19,8 +19,8 @@
*/
-->
<!DOCTYPE html>
<html>
<@s.style>
<html lang="en">
<style>
.debugTable {
border-style: solid;
border-width: 1px;
@@ -50,40 +50,40 @@
.emptyCollection {
background-color: #EEEEEE;
}
</@s.style>
</style>
<@s.script>
<script>
function expand(src, path) {
var baseUrl = location.href;
var i = baseUrl.indexOf("&object=");
let baseUrl = location.href;
const i = baseUrl.indexOf('&object=');
baseUrl = (i > 0 ? baseUrl.substring(0, i) : baseUrl) + "&object=" + path;
if (baseUrl.indexOf("decorate") < 0) {
baseUrl += "&decorate=false";
}
var request = new XMLHttpRequest();
const request = new XMLHttpRequest();
request.open('GET', baseUrl, true);
request.onreadystatechange = function() {
if (this.readyState === 4) {
if (this.status >= 200 && this.status < 400) {
var div = document.createElement("div");
const div = document.createElement('div');
console.log(this.responseText);
div.innerHTML = this.responseText;
src.parentNode.appendChild(div);
src.innerHTML = "Collapse";
var oldonclick = src.onclick;
const oldOnclick = src.onclick;
src.onclick = function() {
src.innerHTML = "Expand";
src.parentNode.removeChild(div);
src.onclick = oldonclick;
src.onclick = oldOnclick;
};
}
}
};
request.send();
}
</@s.script>
</script>
<body>
${debugHtml?no_esc}
@@ -23,21 +23,25 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="webconsole.css"/>
<script src="webconsole.js"></script>
<title>OGNL Console</title>
<link rel="stylesheet" type="text/css" href="webconsole.css"/>
<script src="webconsole.js"></script>
<title>OGNL Console</title>
</head>
<body>
<div id="shell">
<div class="wc-results" id="wc-result">
Welcome to the OGNL console!
<br/>
:-&gt;
</div>
<form onsubmit="return false" id="wc-form">
<input type="hidden" name="debug" value="command"/>
<input name="expression" onkeyup="keyEvent(event)" class="wc-command" id="wc-command" type="text"/>
</form>
<div class="wc-results" id="wc-result">
Welcome to the OGNL console!
<br/>
:-&gt;
</div>
<form onsubmit="return false" id="wc-form">
<input type="hidden" name="debug" value="command"/>
<input name="expression" class="wc-command" id="wc-command" type="text"/>
<script>
const input = document.getElementById("wc-command")
input.addEventListener("keyup", keyEvent);
</script>
</form>
</div>
</body>
</html>
@@ -28,7 +28,6 @@
<constant name="struts.excludedClasses"
value="
org.apache.struts2.ActionContext,
java.lang.Class,
java.lang.ClassLoader,
java.lang.InheritableThreadLocal,
@@ -43,7 +42,8 @@
java.lang.System,
java.lang.Thread,
java.lang.ThreadGroup,
java.lang.ThreadLocal
java.lang.ThreadLocal,
org.apache.struts2.ActionContext
"/>
<constant name="struts.devMode.excludedClasses"
@@ -74,15 +74,11 @@
<constant name="struts.excludedPackageNames"
value="
antlr.build,
org.apache.struts2.config,
org.apache.struts2.inject,
org.apache.struts2.ognl,
org.apache.struts2.security,
org.apache.struts2.util,
freemarker.core,
freemarker.ext.jsp,
freemarker.ext.rhino,
freemarker.template,
jakarta,
java.io,
java.net,
java.nio,
@@ -93,6 +89,11 @@
org.apache.commons.beanutils,
org.apache.commons.collections,
org.apache.jasper,
org.apache.struts2.config,
org.apache.struts2.inject,
org.apache.struts2.ognl,
org.apache.struts2.security,
org.apache.struts2.util,
org.apache.tomcat,
org.apache.velocity,
org.objectweb.asm,
@@ -106,15 +107,11 @@
<constant name="struts.devMode.excludedPackageNames"
value="
antlr.build,
org.apache.struts2.config,
org.apache.struts2.inject,
org.apache.struts2.ognl,
org.apache.struts2.security,
org.apache.struts2.util,
freemarker.core,
freemarker.ext.jsp,
freemarker.ext.rhino,
freemarker.template,
jakarta,
java.io,
java.net,
java.nio,
@@ -125,7 +122,11 @@
org.apache.commons.beanutils,
org.apache.commons.collections,
org.apache.jasper,
org.apache.struts2.config,
org.apache.struts2.inject,
org.apache.struts2.ognl,
org.apache.struts2.security,
org.apache.struts2.util,
org.apache.tomcat,
org.apache.velocity,
org.objectweb.asm,
@@ -18,8 +18,6 @@
*/
package org.apache.struts2;
import org.apache.struts2.ActionProxyFactory;
import org.apache.struts2.XWorkJUnit4TestCase;
import org.apache.struts2.dispatcher.Dispatcher;
import org.apache.struts2.util.StrutsTestCaseHelper;
import org.apache.struts2.views.jsp.StrutsMockServletContext;
@@ -0,0 +1,88 @@
/*
* 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.components;
import org.apache.struts2.ActionContext;
import org.apache.struts2.dispatcher.DispatcherConstants;
import org.apache.struts2.views.jsp.AbstractTagTest;
import java.util.Map;
import static org.apache.struts2.components.UIBean.TEMPLATE_DIR;
import static org.apache.struts2.components.UIBean.THEME;
public class UIBeanTagTest extends AbstractTagTest {
private UIBean bean;
@Override
public void setUp() throws Exception {
super.setUp();
bean = new UIBean(stack, request, response) {
@Override
protected String getDefaultTemplate() {
return null;
}
};
}
public void testTemplateDir_ognlExpression() {
bean.setTemplateDir("%{testDir}");
stack.push(new Object() {
public String getTestDir() {
return "testValue";
}
});
assertEquals("testValue", bean.getTemplateDir());
}
public void testTemplateDir_attrMapFallback() {
ActionContext.of(context).getApplication().put(TEMPLATE_DIR, "applicationValue");
assertEquals("applicationValue", bean.getTemplateDir());
ActionContext.of(context).getSession().put(TEMPLATE_DIR, "sessionValue");
assertEquals("sessionValue", bean.getTemplateDir());
((Map<String, Object>) context.get(DispatcherConstants.REQUEST)).put(TEMPLATE_DIR, "requestValue");
assertEquals("requestValue", bean.getTemplateDir());
}
public void testTheme_ognlExpression() {
bean.setTheme("%{testTheme}");
stack.push(new Object() {
public String getTestTheme() {
return "testValue";
}
});
assertEquals("testValue", bean.getTheme());
}
public void testTheme_attrMapFallback() {
ActionContext.of(context).getApplication().put(THEME, "applicationValue");
assertEquals("applicationValue", bean.getTheme());
ActionContext.of(context).getSession().put(THEME, "sessionValue");
assertEquals("sessionValue", bean.getTheme());
((Map<String, Object>) context.get(DispatcherConstants.REQUEST)).put(THEME, "requestValue");
assertEquals("requestValue", bean.getTheme());
}
}
@@ -344,6 +344,9 @@ public class DispatcherTest extends StrutsJUnit4InternalTestCase {
req.setContentType("Multipart/Form-Data ; boundary=---------------------------207103069210263;charset=UTF-16LE");
assertTrue(dispatcher.isMultipartRequest(req));
req.setContentType("multipart/form-data; boundary=\"----=_Part_38_1092302434.1734807780737\"");
assertTrue(dispatcher.isMultipartRequest(req));
}
@Test
@@ -19,7 +19,13 @@
package org.apache.struts2.dispatcher.multipart;
import org.apache.commons.fileupload2.jakarta.servlet6.JakartaServletDiskFileUpload;
import org.apache.struts2.config.Configuration;
import org.apache.struts2.config.ConfigurationManager;
import org.apache.struts2.dispatcher.Dispatcher;
import org.apache.struts2.dispatcher.LocalizedMessage;
import org.apache.struts2.inject.Container;
import org.apache.struts2.util.StrutsTestCaseHelper;
import org.apache.struts2.views.jsp.StrutsMockServletContext;
import org.assertj.core.api.InstanceOfAssertFactories;
import org.junit.After;
import org.junit.Before;
@@ -31,6 +37,7 @@ import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -47,6 +54,7 @@ abstract class AbstractMultiPartRequestTest {
protected final String endline = "\r\n";
protected AbstractMultiPartRequest multiPart;
protected Container container;
abstract protected AbstractMultiPartRequest createMultipartRequest();
@@ -59,7 +67,13 @@ abstract class AbstractMultiPartRequestTest {
}
@Before
public void before() {
public void before() throws Exception {
StrutsMockServletContext servletContext = new StrutsMockServletContext();
Dispatcher dispatcher = StrutsTestCaseHelper.initDispatcher(servletContext, Collections.emptyMap());
ConfigurationManager configurationManager = dispatcher.getConfigurationManager();
Configuration configuration = configurationManager.getConfiguration();
container = configuration.getContainer();
mockRequest = new MockHttpServletRequest();
mockRequest.setCharacterEncoding(StandardCharsets.UTF_8.name());
mockRequest.setMethod("post");
@@ -492,6 +506,47 @@ abstract class AbstractMultiPartRequestTest {
.containsExactly("struts.messages.upload.error.FileUploadException");
}
@Test
public void maliciousFields() throws IOException {
String content = formFile("file1", "test1.csv", "1,2,3,4") +
formField("top.param", "expression") +
endline + "--" + boundary + "--";
mockRequest.setContent(content.getBytes(StandardCharsets.UTF_8));
assertThat(JakartaServletDiskFileUpload.isMultipartContent(mockRequest)).isTrue();
multiPart.parse(mockRequest, tempDir);
assertThat(multiPart.getErrors())
.isEmpty();
assertThat(multiPart.getParameterNames().asIterator()).toIterable()
.isEmpty();
}
@Test
public void maliciousFilename() throws IOException {
String content = formFile("file1", "../test1.csv", "1,2,3,4") +
formField("param", "expression") +
endline + "--" + boundary + "--";
mockRequest.setContent(content.getBytes(StandardCharsets.UTF_8));
assertThat(JakartaServletDiskFileUpload.isMultipartContent(mockRequest)).isTrue();
multiPart.parse(mockRequest, tempDir);
assertThat(multiPart.getErrors())
.isEmpty();
assertThat(multiPart.getParameterNames().asIterator()).toIterable()
.hasSize(1);
assertThat(multiPart.getParameterNames().asIterator()).toIterable()
.containsOnly("param");
assertThat(multiPart.getFileNames("file1")).isEmpty();
}
protected String formFile(String fieldName, String filename, String content) {
return endline +
"--" + boundary + endline +
@@ -48,7 +48,7 @@ public class JakartaStreamMultiPartRequestTest extends AbstractMultiPartRequestT
// when
multiPart.setMaxSizeOfFiles("10");
multiPart.parse(mockRequest, tempDir.toString());
multiPart.parse(mockRequest, tempDir);
// then
assertThat(multiPart.uploadedFiles)
@@ -28,7 +28,9 @@ import java.util.concurrent.Callable;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
@@ -49,6 +51,7 @@ public class ContainerImplTest {
ContainerBuilder cb = new ContainerBuilder();
cb.constant("methodCheck.name", "Lukasz");
cb.constant("fieldCheck.name", "Lukasz");
cb.constant("constructorCheck.name", "Lukasz");
cb.factory(EarlyInitializable.class, EarlyInitializableBean.class, Scope.SINGLETON);
cb.factory(Initializable.class, InitializableBean.class, Scope.SINGLETON);
cb.factory(EarlyInitializable.class, "prototypeEarlyInitializable", EarlyInitializableBean.class, Scope.PROTOTYPE);
@@ -65,15 +68,43 @@ public class ContainerImplTest {
}
@Test
public void fieldInjector() throws Exception {
public void fieldInjector() {
FieldCheck fieldCheck = new FieldCheck();
c.inject(fieldCheck);
assertEquals(fieldCheck.getName(), "Lukasz");
assertEquals("Lukasz", fieldCheck.getName());
}
@Test
public void methodInjector() throws Exception {
c.inject(new MethodCheck());
public void methodInjector() {
MethodCheck methodCheck = new MethodCheck();
c.inject(methodCheck);
assertEquals("Lukasz", methodCheck.getName());
}
@Test
public void constructorInjector() {
ConstructorCheck constructorCheck = c.inject(ConstructorCheck.class);
assertEquals("Lukasz", constructorCheck.getName());
}
@Test
public void optionalConstructorInjector() {
OptionalConstructorCheck constructorCheck = c.inject(OptionalConstructorCheck.class);
assertNull(constructorCheck.getName());
}
@Test
public void requiredOptionalConstructorInjector() {
RequiredOptionalConstructorCheck constructorCheck = c.inject(RequiredOptionalConstructorCheck.class);
assertNotNull(constructorCheck.getExistingName());
assertNull(constructorCheck.getNonExitingName());
}
@Test
public void optionalRequiredConstructorInjector() {
OptionalRequiredConstructorCheck constructorCheck = c.inject(OptionalRequiredConstructorCheck.class);
assertNull(constructorCheck.getNonExitingName());
assertNotNull(constructorCheck.getExistingName());
}
/**
@@ -92,7 +123,7 @@ public class ContainerImplTest {
* Inject values into method under SecurityManager
*/
@Test
public void testMethodInjectorWithSecurityEnabled() throws Exception {
public void testMethodInjectorWithSecurityEnabled() {
assumeTrue(SystemUtils.isJavaVersionAtMost(JavaVersion.JAVA_20));
System.setSecurityManager(new TestSecurityManager());
assertThrows(DependencyException.class, () -> c.inject(new MethodCheck()));
@@ -101,7 +132,7 @@ public class ContainerImplTest {
}
@Test
public void testEarlyInitializable() throws Exception {
public void testEarlyInitializable() {
assertTrue("should being initialized already", EarlyInitializableBean.initializedEarly);
EarlyInitializableCheck earlyInitializableCheck = new EarlyInitializableCheck();
@@ -148,22 +179,19 @@ public class ContainerImplTest {
final InitializableCheck initializableCheck3 = new InitializableCheck();
final TestScopeStrategy testScopeStrategy = new TestScopeStrategy();
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
ContainerBuilder cb2 = new ContainerBuilder();
cb2.factory(EarlyInitializable.class, EarlyInitializableBean.class, Scope.SINGLETON);
cb2.factory(Initializable.class, InitializableBean.class, Scope.SINGLETON);
cb2.factory(EarlyInitializable.class, "prototypeEarlyInitializable", EarlyInitializableBean.class, Scope.PROTOTYPE);
cb2.factory(Initializable.class, "prototypeInitializable", InitializableBean.class, Scope.PROTOTYPE);
cb2.factory(Initializable.class, "requestInitializable", InitializableBean.class, Scope.REQUEST);
cb2.factory(Initializable.class, "sessionInitializable", InitializableBean.class, Scope.SESSION);
cb2.factory(Initializable.class, "threadInitializable", InitializableBean.class, Scope.THREAD);
cb2.factory(Initializable.class, "wizardInitializable", InitializableBean.class, Scope.WIZARD);
Container c2 = cb2.create(false);
c2.setScopeStrategy(testScopeStrategy);
c2.inject(initializableCheck3);
}
Thread thread = new Thread(() -> {
ContainerBuilder cb2 = new ContainerBuilder();
cb2.factory(EarlyInitializable.class, EarlyInitializableBean.class, Scope.SINGLETON);
cb2.factory(Initializable.class, InitializableBean.class, Scope.SINGLETON);
cb2.factory(EarlyInitializable.class, "prototypeEarlyInitializable", EarlyInitializableBean.class, Scope.PROTOTYPE);
cb2.factory(Initializable.class, "prototypeInitializable", InitializableBean.class, Scope.PROTOTYPE);
cb2.factory(Initializable.class, "requestInitializable", InitializableBean.class, Scope.REQUEST);
cb2.factory(Initializable.class, "sessionInitializable", InitializableBean.class, Scope.SESSION);
cb2.factory(Initializable.class, "threadInitializable", InitializableBean.class, Scope.THREAD);
cb2.factory(Initializable.class, "wizardInitializable", InitializableBean.class, Scope.WIZARD);
Container c2 = cb2.create(false);
c2.setScopeStrategy(testScopeStrategy);
c2.inject(initializableCheck3);
});
thread.run();
thread.join();
@@ -205,6 +233,76 @@ public class ContainerImplTest {
}
public static class ConstructorCheck {
private String name;
@Inject("constructorCheck.name")
public ConstructorCheck(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public static class OptionalConstructorCheck {
private String name;
@Inject(value = "nonExistingConstant", required = false)
public OptionalConstructorCheck(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public static class RequiredOptionalConstructorCheck {
private final String existingName;
private final String nonExitingName;
@Inject(required = false)
public RequiredOptionalConstructorCheck(
@Inject("constructorCheck.name") String existingName,
@Inject(value = "nonExistingConstant", required = false) String nonExitingName
) {
this.existingName = existingName;
this.nonExitingName = nonExitingName;
}
public String getExistingName() {
return existingName;
}
public String getNonExitingName() {
return nonExitingName;
}
}
public static class OptionalRequiredConstructorCheck {
private final String existingName;
private final String nonExitingName;
@Inject(required = false)
public OptionalRequiredConstructorCheck(
@Inject(value = "nonExistingConstant", required = false) String nonExitingName,
@Inject("constructorCheck.name") String existingName
) {
this.existingName = existingName;
this.nonExitingName = nonExitingName;
}
public String getExistingName() {
return existingName;
}
public String getNonExitingName() {
return nonExitingName;
}
}
class InitializableCheck {
private Initializable initializable;
@@ -514,11 +514,71 @@ public class ActionFileUploadInterceptorTest extends StrutsInternalTestCase {
assertTrue(msg.startsWith("Der Request übertraf die maximal erlaubte Größe"));
}
public void testUnacceptedFieldName() throws Exception {
request.setCharacterEncoding(StandardCharsets.UTF_8.name());
request.setMethod("post");
request.addHeader("Content-type", "multipart/form-data; boundary=---1234");
// inspired by the unit tests for jakarta commons fileupload
String content = ("-----1234\r\n" +
"Content-Disposition: form-data; name=\"top.file\"; filename=\"deleteme.txt\"\r\n" +
"Content-Type: text/html\r\n" +
"\r\n" +
"Unit test of ActionFileUploadInterceptor" +
"\r\n" +
"-----1234--\r\n");
request.setContent(content.getBytes(StandardCharsets.US_ASCII));
MyFileUploadAction action = container.inject(MyFileUploadAction.class);
MockActionInvocation mai = new MockActionInvocation();
mai.setAction(action);
mai.setResultCode("success");
mai.setInvocationContext(ActionContext.getContext());
ActionContext.getContext()
.withServletRequest(createMultipartRequestMaxSize(2000));
interceptor.intercept(mai);
assertFalse(action.hasActionErrors());
assertNull(action.getUploadFiles());
}
public void testUnacceptedFileName() throws Exception {
request.setCharacterEncoding(StandardCharsets.UTF_8.name());
request.setMethod("post");
request.addHeader("Content-type", "multipart/form-data; boundary=---1234");
// inspired by the unit tests for jakarta commons fileupload
String content = ("-----1234\r\n" +
"Content-Disposition: form-data; name=\"file\"; filename=\"../deleteme.txt\"\r\n" +
"Content-Type: text/html\r\n" +
"\r\n" +
"Unit test of ActionFileUploadInterceptor" +
"\r\n" +
"-----1234--\r\n");
request.setContent(content.getBytes(StandardCharsets.US_ASCII));
MyFileUploadAction action = container.inject(MyFileUploadAction.class);
MockActionInvocation mai = new MockActionInvocation();
mai.setAction(action);
mai.setResultCode("success");
mai.setInvocationContext(ActionContext.getContext());
ActionContext.getContext()
.withServletRequest(createMultipartRequestMaxSize(2000));
interceptor.intercept(mai);
assertFalse(action.hasActionErrors());
assertNull(action.getUploadFiles());
}
private String encodeTextFile(String filename, String contentType, String content) {
return endline +
"--" + boundary +
endline +
"Content-Disposition: form-data; name=\"" + "file" + "\"; filename=\"" + filename +
"Content-Disposition: form-data; name=\"" + "file" + "\"; filename=\"" + filename + "\"" +
endline +
"Content-Type: " + contentType +
endline +
@@ -549,6 +609,7 @@ public class ActionFileUploadInterceptorTest extends StrutsInternalTestCase {
jak.setMaxFiles(String.valueOf(maxfiles));
jak.setMaxStringLength(String.valueOf(maxStringLength));
jak.setDefaultEncoding(StandardCharsets.UTF_8.name());
return new MultiPartRequestWrapper(jak, request, tempDir.getAbsolutePath(), new DefaultLocaleProvider());
}
@@ -19,16 +19,17 @@
package org.apache.struts2.interceptor;
import com.mockobjects.dynamic.Mock;
import org.apache.struts2.action.Action;
import org.apache.struts2.ActionContext;
import org.apache.struts2.ActionInvocation;
import org.apache.struts2.ActionProxy;
import org.apache.struts2.StrutsException;
import org.apache.struts2.XWorkTestCase;
import org.apache.struts2.action.Action;
import org.apache.struts2.config.entities.ActionConfig;
import org.apache.struts2.config.entities.ExceptionMappingConfig;
import org.apache.struts2.ognl.ThreadAllowlist;
import org.apache.struts2.util.ValueStack;
import org.apache.struts2.validator.ValidationException;
import org.apache.struts2.StrutsException;
/**
* Unit test for ExceptionMappingInterceptor.
@@ -52,7 +53,7 @@ public class ExceptionMappingInterceptorTest extends XWorkTestCase {
String result = interceptor.intercept(invocation);
assertNotNull(stack.findValue("exception"));
assertEquals(stack.findValue("exception"), exception);
assertEquals(result, "spooky");
assertEquals("spooky", result);
ExceptionHolder holder = (ExceptionHolder) stack.getRoot().get(0); // is on top of the root
assertNotNull(holder.getExceptionStack()); // to invoke the method for unit test
}
@@ -67,7 +68,7 @@ public class ExceptionMappingInterceptorTest extends XWorkTestCase {
String result = interceptor.intercept(invocation);
assertNotNull(stack.findValue("exception"));
assertEquals(stack.findValue("exception"), exception);
assertEquals(result, "throwable");
assertEquals("throwable", result);
}
public void testNoThrownException() throws Exception {
@@ -77,7 +78,7 @@ public class ExceptionMappingInterceptorTest extends XWorkTestCase {
mockInvocation.expectAndReturn("invoke", Action.SUCCESS);
mockInvocation.matchAndReturn("getAction", action.proxy());
String result = interceptor.intercept(invocation);
assertEquals(result, Action.SUCCESS);
assertEquals(Action.SUCCESS, result);
assertNull(stack.findValue("exception"));
}
@@ -106,7 +107,7 @@ public class ExceptionMappingInterceptorTest extends XWorkTestCase {
mockInvocation.matchAndReturn("getAction", action.proxy());
try {
interceptor.setLogEnabled(true);
interceptor.setLogEnabled(true);
interceptor.intercept(invocation);
fail("Should not have reached this point.");
} catch (Exception e) {
@@ -123,8 +124,8 @@ public class ExceptionMappingInterceptorTest extends XWorkTestCase {
mockInvocation.matchAndReturn("getAction", action.proxy());
try {
interceptor.setLogEnabled(true);
interceptor.setLogCategory("showcase.unhandled");
interceptor.setLogEnabled(true);
interceptor.setLogCategory("showcase.unhandled");
interceptor.intercept(invocation);
fail("Should not have reached this point.");
} catch (Exception e) {
@@ -141,9 +142,9 @@ public class ExceptionMappingInterceptorTest extends XWorkTestCase {
mockInvocation.matchAndReturn("getAction", action.proxy());
try {
interceptor.setLogEnabled(true);
interceptor.setLogCategory("showcase.unhandled");
interceptor.setLogLevel("fatal");
interceptor.setLogEnabled(true);
interceptor.setLogCategory("showcase.unhandled");
interceptor.setLogLevel("fatal");
interceptor.intercept(invocation);
fail("Should not have reached this point.");
} catch (Exception e) {
@@ -164,9 +165,9 @@ public class ExceptionMappingInterceptorTest extends XWorkTestCase {
mockInvocation.matchAndReturn("getAction", action.proxy());
try {
interceptor.setLogEnabled(true);
interceptor.setLogCategory("showcase.unhandled");
interceptor.setLogLevel("error");
interceptor.setLogEnabled(true);
interceptor.setLogCategory("showcase.unhandled");
interceptor.setLogLevel("error");
interceptor.intercept(invocation);
fail("Should not have reached this point.");
} catch (Exception e) {
@@ -183,9 +184,9 @@ public class ExceptionMappingInterceptorTest extends XWorkTestCase {
mockInvocation.matchAndReturn("getAction", action.proxy());
try {
interceptor.setLogEnabled(true);
interceptor.setLogCategory("showcase.unhandled");
interceptor.setLogLevel("warn");
interceptor.setLogEnabled(true);
interceptor.setLogCategory("showcase.unhandled");
interceptor.setLogLevel("warn");
interceptor.intercept(invocation);
fail("Should not have reached this point.");
} catch (Exception e) {
@@ -202,9 +203,9 @@ public class ExceptionMappingInterceptorTest extends XWorkTestCase {
mockInvocation.matchAndReturn("getAction", action.proxy());
try {
interceptor.setLogEnabled(true);
interceptor.setLogCategory("showcase.unhandled");
interceptor.setLogLevel("info");
interceptor.setLogEnabled(true);
interceptor.setLogCategory("showcase.unhandled");
interceptor.setLogLevel("info");
interceptor.intercept(invocation);
fail("Should not have reached this point.");
} catch (Exception e) {
@@ -221,9 +222,9 @@ public class ExceptionMappingInterceptorTest extends XWorkTestCase {
mockInvocation.matchAndReturn("getAction", action.proxy());
try {
interceptor.setLogEnabled(true);
interceptor.setLogCategory("showcase.unhandled");
interceptor.setLogLevel("debug");
interceptor.setLogEnabled(true);
interceptor.setLogCategory("showcase.unhandled");
interceptor.setLogLevel("debug");
interceptor.intercept(invocation);
fail("Should not have reached this point.");
} catch (Exception e) {
@@ -240,9 +241,9 @@ public class ExceptionMappingInterceptorTest extends XWorkTestCase {
mockInvocation.matchAndReturn("getAction", action.proxy());
try {
interceptor.setLogEnabled(true);
interceptor.setLogCategory("showcase.unhandled");
interceptor.setLogLevel("trace");
interceptor.setLogEnabled(true);
interceptor.setLogCategory("showcase.unhandled");
interceptor.setLogLevel("trace");
interceptor.intercept(invocation);
fail("Should not have reached this point.");
} catch (Exception e) {
@@ -259,12 +260,12 @@ public class ExceptionMappingInterceptorTest extends XWorkTestCase {
mockInvocation.matchAndReturn("getAction", action.proxy());
try {
interceptor.setLogEnabled(true);
interceptor.setLogLevel("xxx");
interceptor.setLogEnabled(true);
interceptor.setLogLevel("xxx");
interceptor.intercept(invocation);
fail("Should not have reached this point.");
} catch (IllegalArgumentException e) {
// success
// success
}
}
@@ -296,6 +297,7 @@ public class ExceptionMappingInterceptorTest extends XWorkTestCase {
mockInvocation.expectAndReturn("getStack", stack);
mockInvocation.expectAndReturn("getInvocationContext", ActionContext.of().bind());
interceptor = new ExceptionMappingInterceptor();
interceptor.setThreadAllowlist(new ThreadAllowlist());
interceptor.init();
}
@@ -0,0 +1,569 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.struts2.interceptor.debugging;
import org.apache.struts2.ActionContext;
import org.apache.struts2.StrutsJUnit4InternalTestCase;
import org.apache.struts2.TestAction;
import org.apache.struts2.dispatcher.DispatcherConstants;
import org.apache.struts2.dispatcher.HttpParameters;
import org.apache.struts2.dispatcher.RequestMap;
import org.apache.struts2.dispatcher.SessionMap;
import org.apache.struts2.mock.MockActionInvocation;
import org.apache.struts2.ognl.ThreadAllowlist;
import org.apache.struts2.util.ValueStack;
import org.assertj.core.api.InstanceOfAssertFactories;
import org.assertj.core.util.Maps;
import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockHttpSession;
import java.util.HashMap;
import java.util.Map;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
public class DebuggingInterceptorTest extends StrutsJUnit4InternalTestCase {
private DebuggingInterceptor interceptor;
private MockActionInvocation invocation;
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private ActionContext context;
private TestAction action;
@Test
public void noDevMode() throws Exception {
interceptor.intercept(invocation);
assertThat(invocation.getResultCode()).isEqualTo("mock");
assertThat(response.getContentAsString()).isEmpty();
}
@Test
public void debugXml() throws Exception {
interceptor.setDevMode("true");
context.withParameters(HttpParameters.create(Maps.newHashMap("debug", "xml")).build());
interceptor.intercept(invocation);
assertThat(response.getContentAsString()).isEqualToIgnoringWhitespace("""
<debug>
<parameters/>
<context/>
<request/>
<session/>
<valueStack>
<value>
<action>
<actionErrors/>
<actionMessages/>
<class>class org.apache.struts2.TestAction</class>
<fieldErrors/>
<locale>
<ISO3Country>USA</ISO3Country>
<ISO3Language>eng</ISO3Language>
<class>class java.util.Locale</class>
<country>US</country>
<displayCountry>United States</displayCountry>
<displayLanguage>English</displayLanguage>
<displayName>English (United States)</displayName>
<displayScript></displayScript>
<displayVariant></displayVariant>
<extensionKeys/>
<language>en</language>
<script></script>
<unicodeLocaleAttributes/>
<unicodeLocaleKeys/>
<variant></variant>
</locale>
<status>
<class>class org.apache.struts2.SomeEnum</class>
<declaringClass>class org.apache.struts2.SomeEnum</declaringClass>
<displayName>completed</displayName>
<name>COMPLETED</name>
</status>
<statusList>
<value>
<class>class org.apache.struts2.SomeEnum</class>
<declaringClass>class org.apache.struts2.SomeEnum</declaringClass>
<displayName>init</displayName>
<name>INIT</name>
</value>
<value>
<class>class org.apache.struts2.SomeEnum</class>
<declaringClass>class org.apache.struts2.SomeEnum</declaringClass>
<displayName>completed</displayName>
<name>COMPLETED</name>
</value>
</statusList>
<texts>
<baseBundleName>org.apache.struts2.TestAction</baseBundleName>
<class>class java.util.PropertyResourceBundle</class>
<keys>
<class>class sun.util.ResourceBundleEnumeration</class>
</keys>
<locale>
<ISO3Country></ISO3Country>
<ISO3Language></ISO3Language>
<class>class java.util.Locale</class>
<country></country>
<displayCountry></displayCountry>
<displayLanguage></displayLanguage>
<displayName></displayName>
<displayScript></displayScript>
<displayVariant></displayVariant>
<extensionKeys/>
<language></language>
<script></script>
<unicodeLocaleAttributes/>
<unicodeLocaleKeys/>
<variant></variant>
</locale>
</texts>
</action>
<org.apache.struts2.util.OgnlValueStack.MAP_IDENTIFIER_KEY></org.apache.struts2.util.OgnlValueStack.MAP_IDENTIFIER_KEY>
</value>
<value>
<class>class org.apache.struts2.text.DefaultTextProvider</class>
</value>
</valueStack>
</debug>
""");
}
@Test
public void debugXmlWithConsole() throws Exception {
interceptor.setDevMode("true");
context.withParameters(HttpParameters.create(Maps.newHashMap("debug", "console")).build());
interceptor.setEnableXmlWithConsole(true);
interceptor.intercept(invocation);
assertThat(response.getContentAsString()).isEqualToIgnoringWhitespace("""
<!DOCTYPE html>
<html>
<head>
<script>
var baseUrl = "/static";
window.open(baseUrl+"/webconsole.html", 'OGNL Console','width=500,height=450,status=no,toolbar=no,menubar=no');
</script>
</head>
<body>
<pre>
&amp;lt;debug&amp;gt;
&amp;lt;parameters/&amp;gt;
&amp;lt;context/&amp;gt;
&amp;lt;request/&amp;gt;
&amp;lt;session/&amp;gt;
&amp;lt;valueStack&amp;gt;
&amp;lt;value&amp;gt;
&amp;lt;action&amp;gt;
&amp;lt;actionErrors/&amp;gt;
&amp;lt;actionMessages/&amp;gt;
&amp;lt;class&amp;gt;class org.apache.struts2.TestAction&amp;lt;/class&amp;gt;
&amp;lt;fieldErrors/&amp;gt;
&amp;lt;locale&amp;gt;
&amp;lt;ISO3Country&amp;gt;USA&amp;lt;/ISO3Country&amp;gt;
&amp;lt;ISO3Language&amp;gt;eng&amp;lt;/ISO3Language&amp;gt;
&amp;lt;class&amp;gt;class java.util.Locale&amp;lt;/class&amp;gt;
&amp;lt;country&amp;gt;US&amp;lt;/country&amp;gt;
&amp;lt;displayCountry&amp;gt;United States&amp;lt;/displayCountry&amp;gt;
&amp;lt;displayLanguage&amp;gt;English&amp;lt;/displayLanguage&amp;gt;
&amp;lt;displayName&amp;gt;English (United States)&amp;lt;/displayName&amp;gt;
&amp;lt;displayScript&amp;gt;&amp;lt;/displayScript&amp;gt;
&amp;lt;displayVariant&amp;gt;&amp;lt;/displayVariant&amp;gt;
&amp;lt;extensionKeys/&amp;gt;
&amp;lt;language&amp;gt;en&amp;lt;/language&amp;gt;
&amp;lt;script&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;unicodeLocaleAttributes/&amp;gt;
&amp;lt;unicodeLocaleKeys/&amp;gt;
&amp;lt;variant&amp;gt;&amp;lt;/variant&amp;gt;
&amp;lt;/locale&amp;gt;
&amp;lt;status&amp;gt;
&amp;lt;class&amp;gt;class org.apache.struts2.SomeEnum&amp;lt;/class&amp;gt;
&amp;lt;declaringClass&amp;gt;class org.apache.struts2.SomeEnum&amp;lt;/declaringClass&amp;gt;
&amp;lt;displayName&amp;gt;completed&amp;lt;/displayName&amp;gt;
&amp;lt;name&amp;gt;COMPLETED&amp;lt;/name&amp;gt;
&amp;lt;/status&amp;gt;
&amp;lt;statusList&amp;gt;
&amp;lt;value&amp;gt;
&amp;lt;class&amp;gt;class org.apache.struts2.SomeEnum&amp;lt;/class&amp;gt;
&amp;lt;declaringClass&amp;gt;class org.apache.struts2.SomeEnum&amp;lt;/declaringClass&amp;gt;
&amp;lt;displayName&amp;gt;init&amp;lt;/displayName&amp;gt;
&amp;lt;name&amp;gt;INIT&amp;lt;/name&amp;gt;
&amp;lt;/value&amp;gt;
&amp;lt;value&amp;gt;
&amp;lt;class&amp;gt;class org.apache.struts2.SomeEnum&amp;lt;/class&amp;gt;
&amp;lt;declaringClass&amp;gt;class org.apache.struts2.SomeEnum&amp;lt;/declaringClass&amp;gt;
&amp;lt;displayName&amp;gt;completed&amp;lt;/displayName&amp;gt;
&amp;lt;name&amp;gt;COMPLETED&amp;lt;/name&amp;gt;
&amp;lt;/value&amp;gt;
&amp;lt;/statusList&amp;gt;
&amp;lt;texts&amp;gt;
&amp;lt;baseBundleName&amp;gt;org.apache.struts2.TestAction&amp;lt;/baseBundleName&amp;gt;
&amp;lt;class&amp;gt;class java.util.PropertyResourceBundle&amp;lt;/class&amp;gt;
&amp;lt;keys&amp;gt;
&amp;lt;class&amp;gt;class sun.util.ResourceBundleEnumeration&amp;lt;/class&amp;gt;
&amp;lt;/keys&amp;gt;
&amp;lt;locale&amp;gt;
&amp;lt;ISO3Country&amp;gt;&amp;lt;/ISO3Country&amp;gt;
&amp;lt;ISO3Language&amp;gt;&amp;lt;/ISO3Language&amp;gt;
&amp;lt;class&amp;gt;class java.util.Locale&amp;lt;/class&amp;gt;
&amp;lt;country&amp;gt;&amp;lt;/country&amp;gt;
&amp;lt;displayCountry&amp;gt;&amp;lt;/displayCountry&amp;gt;
&amp;lt;displayLanguage&amp;gt;&amp;lt;/displayLanguage&amp;gt;
&amp;lt;displayName&amp;gt;&amp;lt;/displayName&amp;gt;
&amp;lt;displayScript&amp;gt;&amp;lt;/displayScript&amp;gt;
&amp;lt;displayVariant&amp;gt;&amp;lt;/displayVariant&amp;gt;
&amp;lt;extensionKeys/&amp;gt;
&amp;lt;language&amp;gt;&amp;lt;/language&amp;gt;
&amp;lt;script&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;unicodeLocaleAttributes/&amp;gt;
&amp;lt;unicodeLocaleKeys/&amp;gt;
&amp;lt;variant&amp;gt;&amp;lt;/variant&amp;gt;
&amp;lt;/locale&amp;gt;
&amp;lt;/texts&amp;gt;
&amp;lt;/action&amp;gt;
&amp;lt;org.apache.struts2.util.OgnlValueStack.MAP_IDENTIFIER_KEY&amp;gt;&amp;lt;/org.apache.struts2.util.OgnlValueStack.MAP_IDENTIFIER_KEY&amp;gt;
&amp;lt;/value&amp;gt;
&amp;lt;value&amp;gt;
&amp;lt;class&amp;gt;class org.apache.struts2.text.DefaultTextProvider&amp;lt;/class&amp;gt;
&amp;lt;/value&amp;gt;
&amp;lt;/valueStack&amp;gt;
&amp;lt;/debug&amp;gt;
</pre>
</body>
</html>
""");
}
@Test
public void debugConsole() throws Exception {
interceptor.setDevMode("true");
context.withParameters(HttpParameters.create(Maps.newHashMap("debug", "console")).build());
interceptor.intercept(invocation);
assertThat(response.getContentAsString()).isEqualToIgnoringWhitespace("""
<!DOCTYPE html>
<html>
<head>
<script>
var baseUrl = "/static";
window.open(baseUrl+"/webconsole.html", 'OGNL Console','width=500,height=450,status=no,toolbar=no,menubar=no');
</script>
</head>
<body>
<pre>
</pre>
</body>
</html>
""");
}
@Test
public void debugCommand() throws Exception {
interceptor.setDevMode("true");
Map<String, Object> params = new HashMap<>() {{
put("debug", "command");
put("expression", "1+1");
}};
context.withParameters(HttpParameters.create(params).build());
interceptor.intercept(invocation);
assertThat(response.getContentAsString()).isEqualToIgnoringWhitespace("2");
}
@Test
public void debugBrowser() throws Exception {
interceptor.setDevMode("true");
context.withParameters(HttpParameters.create(Maps.newHashMap("debug", "browser")).build());
interceptor.intercept(invocation);
invocation.invoke();
assertThat(response.getContentAsString()).isEqualToIgnoringWhitespace("""
<!DOCTYPE html>
<html lang="en">
<style>
.debugTable {
border-style: solid;
border-width: 1px;
}
.debugTable td {
border-style: solid;
border-width: 1px;
}
.nameColumn {
background-color:#CCDDFF;
}
.valueColumn {
background-color: #CCFFCC;
}
.nullValue {
background-color: #FF0000;
}
.typeColumn {
background-color: white;
}
.emptyCollection {
background-color: #EEEEEE;
}
</style>
<script>
function expand(src, path) {
let baseUrl = location.href;
const i = baseUrl.indexOf('&object=');
baseUrl = (i > 0 ? baseUrl.substring(0, i) : baseUrl) + "&object=" + path;
if (baseUrl.indexOf("decorate") < 0) {
baseUrl += "&decorate=false";
}
const request = new XMLHttpRequest();
request.open('GET', baseUrl, true);
request.onreadystatechange = function() {
if (this.readyState === 4) {
if (this.status >= 200 && this.status < 400) {
const div = document.createElement('div');
console.log(this.responseText);
div.innerHTML = this.responseText;
src.parentNode.appendChild(div);
src.innerHTML = "Collapse";
const oldOnclick = src.onclick;
src.onclick = function() {
src.innerHTML = "Expand";
src.parentNode.removeChild(div);
src.onclick = oldOnclick;
};
}
}
};
request.send();
}
</script>
<body>
<table class="debugTable">
<tr>
<td class="nameColumn">container</td>
<td class="valueColumn">There is no read method for container</td>
<td class="typeColumn">java.lang.String</td>
</tr>
<tr>
<td class="nameColumn">foo</td>
<td class="nullValue">null</td>
<td class="nullValue">unknown</td>
</tr>
<tr>
<td class="nameColumn">intList</td>
<td class="nullValue">null</td>
<td class="nullValue">unknown</td>
</tr>
<tr>
<td class="nameColumn">locale</td>
<td class="valueColumn">
<a onclick="expand(this, 'action[&quot;locale&quot;]')" href="javascript://nop/">Expand</a>
</td>
<td class="typeColumn">java.util.Locale</td>
</tr>
<tr>
<td class="nameColumn">result</td>
<td class="nullValue">null</td>
<td class="nullValue">unknown</td>
</tr>
<tr>
<td class="nameColumn">collection2</td>
<td class="nullValue">null</td>
<td class="nullValue">unknown</td>
</tr>
<tr>
<td class="nameColumn">someBool</td>
<td class="nullValue">null</td>
<td class="nullValue">unknown</td>
</tr>
<tr>
<td class="nameColumn">array</td>
<td class="nullValue">null</td>
<td class="nullValue">unknown</td>
</tr>
<tr>
<td class="nameColumn">fooInt</td>
<td class="nullValue">null</td>
<td class="nullValue">unknown</td>
</tr>
<tr>
<td class="nameColumn">id</td>
<td class="nullValue">null</td>
<td class="nullValue">unknown</td>
</tr>
<tr>
<td class="nameColumn">map</td>
<td class="nullValue">null</td>
<td class="nullValue">unknown</td>
</tr>
<tr>
<td class="nameColumn">actionErrors</td>
<td class="emptyCollection">empty</td>
<td class="typeColumn">java.util.LinkedList</td>
</tr>
<tr>
<td class="nameColumn">objectArray</td>
<td class="nullValue">null</td>
<td class="nullValue">unknown</td>
</tr>
<tr>
<td class="nameColumn">fieldErrors</td>
<td class="emptyCollection">empty</td>
<td class="typeColumn">java.util.LinkedHashMap</td>
</tr>
<tr>
<td class="nameColumn">collection</td>
<td class="nullValue">null</td>
<td class="nullValue">unknown</td>
</tr>
<tr>
<td class="nameColumn">floatNumber</td>
<td class="nullValue">null</td>
<td class="nullValue">unknown</td>
</tr>
<tr>
<td class="nameColumn">list</td>
<td class="nullValue">null</td>
<td class="nullValue">unknown</td>
</tr>
<tr>
<td class="nameColumn">enumList</td>
<td class="nullValue">null</td>
<td class="nullValue">unknown</td>
</tr>
<tr>
<td class="nameColumn">actionMessages</td>
<td class="emptyCollection">empty</td>
<td class="typeColumn">java.util.LinkedList</td>
</tr>
<tr>
<td class="nameColumn">statusList</td>
<td class="valueColumn">
<a onclick="expand(this, 'action[&quot;statusList&quot;]')" href="javascript://nop/">Expand</a>
</td>
<td class="typeColumn">java.util.Arrays$ArrayList</td>
</tr>
<tr>
<td class="nameColumn">texts</td>
<td class="valueColumn">
<a onclick="expand(this, 'action[&quot;texts&quot;]')" href="javascript://nop/">Expand</a>
</td>
<td class="typeColumn">java.util.PropertyResourceBundle</td>
</tr>
<tr>
<td class="nameColumn">list3</td>
<td class="nullValue">null</td>
<td class="nullValue">unknown</td>
</tr>
<tr>
<td class="nameColumn">list2</td>
<td class="nullValue">null</td>
<td class="nullValue">unknown</td>
</tr>
<tr>
<td class="nameColumn">user</td>
<td class="nullValue">null</td>
<td class="nullValue">unknown</td>
</tr>
<tr>
<td class="nameColumn">status</td>
<td class="valueColumn">
<a onclick="expand(this, 'action[&quot;status&quot;]')" href="javascript://nop/">Expand</a>
</td>
<td class="typeColumn">org.apache.struts2.SomeEnum</td>
</tr>
</table>
</body>
</html>
""");
}
@Test
public void allowlist() throws Exception {
interceptor.setDevMode("true");
context.withParameters(HttpParameters.create(Maps.newHashMap("debug", "browser")).build());
assertThat(container.getInstance(ThreadAllowlist.class))
.extracting(ThreadAllowlist::getAllowlist).asInstanceOf(InstanceOfAssertFactories.SET)
.isEmpty();
interceptor.intercept(invocation);
invocation.invoke();
assertThat(container.getInstance(ThreadAllowlist.class))
.extracting(ThreadAllowlist::getAllowlist).asInstanceOf(InstanceOfAssertFactories.SET)
.contains(
org.apache.struts2.interceptor.ValidationAware.class,
org.apache.struts2.Validateable.class,
org.apache.struts2.action.Action.class,
org.apache.struts2.text.TextProvider.class,
org.apache.struts2.ActionSupport.class,
org.apache.struts2.locale.LocaleProvider.class,
org.apache.struts2.TestAction.class
);
}
@Before
public void before() {
request = new MockHttpServletRequest();
request.setSession(new MockHttpSession());
response = new MockHttpServletResponse();
ValueStack valueStack = dispatcher.getValueStackFactory().createValueStack();
context = valueStack.getActionContext()
.withServletContext(servletContext)
.withServletRequest(request)
.withServletResponse(response)
.withSession(new SessionMap(request))
.with(DispatcherConstants.REQUEST, new RequestMap(request));
interceptor = container.inject(DebuggingInterceptor.class);
interceptor.init();
invocation = new MockActionInvocation();
invocation.setResultCode("mock");
invocation.setInvocationContext(context);
action = new TestAction();
invocation.setAction(action);
invocation.setStack(valueStack);
valueStack.set("action", invocation.getAction());
context = context.withActionInvocation(invocation).bind();
}
}
@@ -117,9 +117,9 @@ public class ParametersInterceptorTest extends XWorkTestCase {
pi.applyParameters(action, vs, HttpParameters.create(params).build());
// then
assertEquals(3, action.getActionErrors().size());
assertEquals(3, action.getActionMessages().size());
List<String> actionErrors = new ArrayList<>(action.getActionErrors());
List<String> actionErrors = new ArrayList<>(action.getActionMessages());
String msg1 = actionErrors.get(0);
String msg2 = actionErrors.get(1);
@@ -204,9 +204,9 @@ public class ParametersInterceptorTest extends XWorkTestCase {
pi.applyParameters(action, vs, HttpParameters.create(params).build());
// then
assertEquals(3, action.getActionErrors().size());
assertEquals(3, action.getActionMessages().size());
List<String> actionErrors = new ArrayList<>(action.getActionErrors());
List<String> actionErrors = new ArrayList<>(action.getActionMessages());
String msg1 = actionErrors.get(0);
String msg2 = actionErrors.get(1);
String msg3 = actionErrors.get(2);
@@ -586,7 +586,7 @@ 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 actionError = "" + ((SimpleAction) proxy.getAction()).getActionErrors().toArray()[0];
final String actionError = "" + ((SimpleAction) proxy.getAction()).getActionMessages().toArray()[0];
assertTrue(actionError.contains("Error setting expression 'not_a_property' with value 'There is no action property named like this'"));
}
@@ -93,7 +93,7 @@ public class DefaultExcludedPatternsCheckerTest extends XWorkTestCase {
public void testDefaultExcludePatterns() throws Exception {
// given
List<String> prefixes = Arrays.asList("#[0].%s", "[0].%s", "top.%s", "%{[0].%s}", "%{#[0].%s}", "%{top.%s}", "%{#top.%s}", "%{#%s}", "%{%s}", "#%s");
List<String> prefixes = Arrays.asList("#[0].%s", "[0].%s", "top.%s", "%{[0].%s}", "%{#[0].%s}", "%{top.%s}", "%{#top.%s}", "%{#%s}", "%{%s}", "#%s", "top.param", "%{top.request}", "#top.param");
List<String> inners = Arrays.asList("servletRequest", "servletResponse", "servletContext", "application", "session", "struts", "request", "response", "dojo", "parameters");
List<String> suffixes = Arrays.asList("['test']", "[\"test\"]", ".test");
@@ -24,34 +24,60 @@ import java.util.HashMap;
public class WildcardHelperTest extends XWorkTestCase {
public void testMatch() {
private WildcardHelper wildcardHelper;
WildcardHelper wild = new WildcardHelper();
HashMap<String, String> matchedPatterns = new HashMap<>();
int[] pattern = wild.compilePattern("wes-rules");
assertEquals(wild.match(matchedPatterns,"wes-rules", pattern), true);
assertEquals(wild.match(matchedPatterns, "rules-wes", pattern), false);
@Override
public void setUp() throws Exception {
super.setUp();
pattern = wild.compilePattern("wes-*");
assertEquals(wild.match(matchedPatterns,"wes-rules", pattern), true);
assertEquals("rules".equals(matchedPatterns.get("1")), true);
assertEquals(wild.match(matchedPatterns, "rules-wes", pattern), false);
wildcardHelper = new WildcardHelper();
}
pattern = wild.compilePattern("path/**/file");
assertEquals(wild.match(matchedPatterns, "path/to/file", pattern), true);
assertEquals("to".equals(matchedPatterns.get("1")), true);
assertEquals(wild.match(matchedPatterns, "path/to/another/location/of/file", pattern), true);
assertEquals("to/another/location/of".equals(matchedPatterns.get("1")), true);
public void testMatch() {
HashMap<String, String> matchedPatterns = new HashMap<>();
int[] pattern = wildcardHelper.compilePattern("wes-rules");
assertEquals(wildcardHelper.match(matchedPatterns, "wes-rules", pattern), true);
assertEquals(wildcardHelper.match(matchedPatterns, "rules-wes", pattern), false);
pattern = wild.compilePattern("path/*/file");
assertEquals(wild.match(matchedPatterns, "path/to/file", pattern), true);
assertEquals("to".equals(matchedPatterns.get("1")), true);
assertEquals(wild.match(matchedPatterns, "path/to/another/location/of/file", pattern), false);
pattern = wildcardHelper.compilePattern("wes-*");
assertEquals(wildcardHelper.match(matchedPatterns, "wes-rules", pattern), true);
assertEquals("rules".equals(matchedPatterns.get("1")), true);
assertEquals(wildcardHelper.match(matchedPatterns, "rules-wes", pattern), false);
pattern = wild.compilePattern("path/*/another/**/file");
assertEquals(wild.match(matchedPatterns, "path/to/another/location/of/file", pattern), true);
assertEquals("to".equals(matchedPatterns.get("1")), true);
assertEquals("location/of".equals(matchedPatterns.get("2")), true);
}
pattern = wildcardHelper.compilePattern("path/**/file");
assertEquals(wildcardHelper.match(matchedPatterns, "path/to/file", pattern), true);
assertEquals("to".equals(matchedPatterns.get("1")), true);
assertEquals(wildcardHelper.match(matchedPatterns, "path/to/another/location/of/file", pattern), true);
assertEquals("to/another/location/of".equals(matchedPatterns.get("1")), true);
pattern = wildcardHelper.compilePattern("path/*/file");
assertEquals(wildcardHelper.match(matchedPatterns, "path/to/file", pattern), true);
assertEquals("to".equals(matchedPatterns.get("1")), true);
assertEquals(wildcardHelper.match(matchedPatterns, "path/to/another/location/of/file", pattern), false);
pattern = wildcardHelper.compilePattern("path/*/another/**/file");
assertEquals(wildcardHelper.match(matchedPatterns, "path/to/another/location/of/file", pattern), true);
assertEquals("to".equals(matchedPatterns.get("1")), true);
assertEquals("location/of".equals(matchedPatterns.get("2")), true);
}
public void testMatchStrutsPackages() {
// given
HashMap<String, String> matchedPatterns = new HashMap<>();
int[] pattern = wildcardHelper.compilePattern("org.apache.struts2.*");
// when & then
assertTrue(wildcardHelper.match(matchedPatterns, "org.apache.struts2.XWorkTestCase", pattern));
assertEquals("org.apache.struts2.XWorkTestCase", matchedPatterns.get("0"));
assertEquals("XWorkTestCase", matchedPatterns.get("1"));
assertTrue(wildcardHelper.match(matchedPatterns, "org.apache.struts2.core.SomeClass", pattern));
assertEquals("org.apache.struts2.core.SomeClass", matchedPatterns.get("0"));
assertEquals("core.SomeClass", matchedPatterns.get("1"));
assertTrue(wildcardHelper.match(matchedPatterns, "org.apache.struts2.", pattern));
assertEquals("org.apache.struts2.", matchedPatterns.get("0"));
assertEquals("", matchedPatterns.get("1"));
}
}
@@ -18,13 +18,12 @@
*/
package org.apache.struts2.views.jsp.ui;
import org.apache.struts2.config.ConfigurationException;
import org.apache.commons.lang3.StringUtils;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.dispatcher.PrepareOperations;
import org.apache.struts2.views.jsp.AbstractUITagTest;
import java.util.HashMap;
import java.util.Collections;
import java.util.Map;
/**
@@ -59,7 +58,7 @@ public class DebugTagTest extends AbstractUITagTest {
freshTag.setPageContext(pageContext);
// DebugTag has no additional state, so it compares as equal with the default tag clear state as well.
assertTrue("Tag state after doEndTag() under default tag clear state is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
@@ -82,7 +81,7 @@ public class DebugTagTest extends AbstractUITagTest {
freshTag.setPerformClearTagStateForTagPoolingServers(true);
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
@@ -98,7 +97,7 @@ public class DebugTagTest extends AbstractUITagTest {
freshTag.setPageContext(pageContext);
// DebugTag has no additional state, so it compares as equal with the default tag clear state as well.
assertTrue("Tag state after doEndTag() under default tag clear state is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
@@ -116,7 +115,7 @@ public class DebugTagTest extends AbstractUITagTest {
freshTag.setPerformClearTagStateForTagPoolingServers(true);
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
@@ -135,7 +134,7 @@ public class DebugTagTest extends AbstractUITagTest {
freshTag.setPageContext(pageContext);
// DebugTag has no additional state, so it compares as equal with the default tag clear state as well.
assertTrue("Tag state after doEndTag() under default tag clear state is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
PrepareOperations.clearDevModeOverride(); // Clear DevMode override. Avoid ThreadLocal side-effects if test thread re-used.
@@ -158,7 +157,7 @@ public class DebugTagTest extends AbstractUITagTest {
freshTag.setPerformClearTagStateForTagPoolingServers(true);
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
PrepareOperations.clearDevModeOverride(); // Clear DevMode override. Avoid ThreadLocal side-effects if test thread re-used.
@@ -177,7 +176,7 @@ public class DebugTagTest extends AbstractUITagTest {
freshTag.setPageContext(pageContext);
// DebugTag has no additional state, so it compares as equal with the default tag clear state as well.
assertTrue("Tag state after doEndTag() under default tag clear state is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
PrepareOperations.clearDevModeOverride(); // Clear DevMode override. Avoid ThreadLocal side-effects if test thread re-used.
@@ -198,16 +197,14 @@ public class DebugTagTest extends AbstractUITagTest {
freshTag.setPerformClearTagStateForTagPoolingServers(true);
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
PrepareOperations.clearDevModeOverride(); // Clear DevMode override. Avoid ThreadLocal side-effects if test thread re-used.
}
private void setDevMode(final boolean devMode) {
setStrutsConstant(new HashMap<String, String>() {{
put(StrutsConstants.STRUTS_DEVMODE, Boolean.toString(devMode));
}});
setStrutsConstant(Collections.singletonMap(StrutsConstants.STRUTS_DEVMODE, Boolean.toString(devMode)));
}
/**
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>7.0.0</version>
<version>7.0.1</version>
</parent>
<artifactId>struts2-jakarta</artifactId>
<packaging>pom</packaging>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-jakarta</artifactId>
<version>7.0.0</version>
<version>7.0.1</version>
</parent>
<artifactId>struts2-velocity-tools-jsp-jakarta</artifactId>
<packaging>jar</packaging>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-jakarta</artifactId>
<version>7.0.0</version>
<version>7.0.1</version>
</parent>
<artifactId>struts2-velocity-tools-view-jakarta</artifactId>
<packaging>jar</packaging>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>7.0.0</version>
<version>7.0.1</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>7.0.0</version>
<version>7.0.1</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>7.0.0</version>
<version>7.0.1</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>7.0.0</version>
<version>7.0.1</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>7.0.0</version>
<version>7.0.1</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>7.0.0</version>
<version>7.0.1</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>7.0.0</version>
<version>7.0.1</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>7.0.0</version>
<version>7.0.1</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>7.0.0</version>
<version>7.0.1</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>7.0.0</version>
<version>7.0.1</version>
</parent>
<artifactId>struts2-junit-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-parent</artifactId>
<version>7.0.0</version>
<version>7.0.1</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>7.0.0</version>
<version>7.0.1</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>7.0.0</version>
<version>7.0.1</version>
</parent>
<artifactId>struts2-spring-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>7.0.0</version>
<version>7.0.1</version>
</parent>
<artifactId>struts2-testng-plugin</artifactId>
+2 -2
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>7.0.0</version>
<version>7.0.1</version>
</parent>
<artifactId>struts2-tiles-plugin</artifactId>
@@ -40,7 +40,7 @@
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.4.1</version>
<version>3.5.0</version>
<executions>
<execution>
<phase>compile</phase>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>7.0.0</version>
<version>7.0.1</version>
</parent>
<artifactId>struts2-velocity-plugin</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>7.0.0</version>
<version>7.0.1</version>
</parent>
<artifactId>struts2-xslt-plugin</artifactId>
+10 -112
View File
@@ -29,7 +29,7 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>struts2-parent</artifactId>
<version>7.0.0</version>
<version>7.0.1</version>
<packaging>pom</packaging>
<name>Struts 2</name>
<url>https://struts.apache.org/</url>
@@ -51,7 +51,7 @@
<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>
<tag>STRUTS_7_0_0</tag>
<tag>STRUTS_7_0_1</tag>
</scm>
<issueManagement>
@@ -104,18 +104,18 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.outputTimestamp>2024-12-11T06:54:21Z</project.build.outputTimestamp>
<project.build.outputTimestamp>2025-02-02T07:23:42Z</project.build.outputTimestamp>
<maven.compiler.release>17</maven.compiler.release>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<!-- dependency versions in alphanumeric order -->
<asm.version>9.7.1</asm.version>
<byte-buddy.version>1.14.11</byte-buddy.version>
<freemarker.version>2.3.33</freemarker.version>
<byte-buddy.version>1.16.1</byte-buddy.version>
<freemarker.version>2.3.34</freemarker.version>
<hibernate-validator.version>8.0.1.Final</hibernate-validator.version>
<jackson.version>2.18.2</jackson.version>
<log4j2.version>2.24.2</log4j2.version>
<log4j2.version>2.24.3</log4j2.version>
<maven-surefire-plugin.version>3.5.2</maven-surefire-plugin.version>
<mockito.version>5.8.0</mockito.version>
<ognl.version>3.3.5</ognl.version>
@@ -484,7 +484,7 @@
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>versions-maven-plugin</artifactId>
<version>2.17.1</version>
<version>2.18.0</version>
<reportSets>
<reportSet>
<reports>
@@ -606,42 +606,11 @@
<version>3.1.8</version>
</dependency>
<!-- override version of error_prone_annotations until caffeine updates from 2.10.0 to 2.23.0 -->
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_annotations</artifactId>
<version>2.23.0</version>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.framework</artifactId>
<version>6.0.3</version>
<scope>provided
</scope> <!-- felix-main provides everything in felix-framework (and more), override here to provent both JARs from being included in the build. -->
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.main</artifactId>
<version>7.0.5</version>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.shell</artifactId>
<version>1.4.3</version>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.shell.tui</artifactId>
<version>1.4.1</version>
</dependency>
<!-- Velocity -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
<version>2.4.1</version>
</dependency>
<dependency>
@@ -727,40 +696,12 @@
<version>5.0.0-M1</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>jakarta.servlet.jsp.jstl</artifactId>
<version>3.0.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jakarta.servlet.jsp.jstl</groupId>
<artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
<version>3.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jakarta.el</groupId>
<artifactId>jakarta.el-api</artifactId>
<version>5.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jasper</artifactId>
<version>10.1.15</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-api</artifactId>
<version>10.1.15</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.servlet.jsp</groupId>
<artifactId>jakarta.servlet.jsp-api</artifactId>
@@ -768,19 +709,6 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>request</artifactId>
<version>1.0.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-juli</artifactId>
<version>10.1.15</version>
</dependency>
<!-- Commons -->
<dependency>
<groupId>commons-logging</groupId>
@@ -800,7 +728,7 @@
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.15.1</version>
<version>2.18.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
@@ -810,12 +738,7 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.12.0</version>
</dependency>
<dependency>
<groupId>commons-el</groupId>
<artifactId>commons-el</artifactId>
<version>1.0</version>
<version>1.13.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
@@ -921,13 +844,6 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>jmock</groupId>
<artifactId>jmock-cglib</artifactId>
<version>1.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
@@ -983,17 +899,6 @@
</exclusions>
</dependency>
<dependency>
<groupId>io.github.x-stream</groupId>
<artifactId>mxparser</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
@@ -1041,13 +946,6 @@
<version>5.1.2.Final</version>
</dependency>
<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
<version>2.12.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>