Skip to content

WW-5839 Reject unconfined wildcard substitution into class= attributes - #2115

Open
g0w6y wants to merge 2 commits into
apache:mainfrom
g0w6y:WW-5839-input-hygiene-hardening
Open

g0w6y wants to merge 2 commits into
apache:mainfrom
g0w6y:WW-5839-input-hygiene-hardening

Conversation

@g0w6y

@g0w6y g0w6y commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

Fixes WW-5839

AbstractMatcher/ActionConfigMatcher: a wildcard {n} substituted into an action/result/exception-mapping class= attribute was unconfined, unlike the sibling method-name slot (ActionConfig#isAllowedMethod). A substituted value containing anything other than a letter, digit, _ or $ (in particular . or /) now makes the whole match fail, as a disallowed method name does.

Reachable only if a developer writes a wildcard mapping with a substituted class name -- application-responsibility, not a default-config issue.

Verified: WildcardClassNameHardeningTest (new, 6 tests: action, result and exception-mapping class names, non-ASCII and $ identifiers, literal $/\ substitution, method-name control). Full core: 3746/0 failures.

@lukaszlenart

Copy link
Copy Markdown
Member

Thanks for this. The three changes are unrelated, touch different modules and will need separate review rounds and separate release notes, so please split this PR into three, each with its own Jira ticket:

  1. Wildcard substitution into class=: keep it on WW-5839, and narrow that ticket's summary and description to this item.
  2. Path-stripped original upload file name: use WW-5840, which already describes this change. Please title the PR WW-5840 ....
  3. Struts Boot session tracking (cookie-only tracking, SameSite): please open a new ticket for it (component Boot, if available).

Review comments will follow on each PR separately. Thanks!

AbstractMatcher/ActionConfigMatcher: a wildcard "{n}" substituted into an
action/result/exception-mapping class= attribute was unconfined, unlike the
sibling method-name slot (ActionConfig#isAllowedMethod). A substituted segment
containing '.' or '/' is now rejected rather than used verbatim.

Reachable only if a developer writes a wildcard mapping with a substituted
class name -- application-responsibility, not a default-config issue.

Tests: WildcardClassNameHardeningTest (new, 2 tests). Full core suite:
3742/0 failures.
@g0w6y
g0w6y force-pushed the WW-5839-input-hygiene-hardening branch from ec24682 to 6619965 Compare November 7, 2026 09:26
@g0w6y g0w6y changed the title WW-5839 Harden wildcard class-name substitution, raw upload file names, and Boot session tracking WW-5839 Reject unconfined wildcard substitution into class= attributes Nov 7, 2026
@g0w6y

g0w6y commented Nov 7, 2026

Copy link
Copy Markdown
Contributor Author

Done -- this PR is now narrowed to just the wildcard class-name item (6619965), title/description updated to match. #2116 is open against WW-5840 for the upload file name item. Still working on a new ticket for the Boot session tracking item before opening its PR. Will update WW-5839's Jira description to match too.

@g0w6y

g0w6y commented Nov 7, 2026

Copy link
Copy Markdown
Contributor Author

All 3 split out now: this PR (WW-5839, wildcard class-name), #2116 (WW-5840, upload file name), and #2117 (WW-5841, new ticket, Boot session tracking).

@mcc0nnell

Copy link
Copy Markdown
Contributor

I took a closer look at this change. The approach makes sense, but I noticed a few things worth considering before merging.

The main concern is how rejected wildcard values are handled. Right now, an invalid segment gets replaced with an empty string, but the match still succeeds. For example, actions.{1}Action with util.Harmless becomes actions.Action. If that class exists, we could end up loading a different action instead of rejecting the match entirely. I think failing the match would be safer and more consistent with the existing method-name checks.

A couple of smaller things:

  • The new character restriction also excludes valid non-ASCII Java identifiers and potentially supported bean names. It might be worth checking whether that's intentional.
  • The tests cover action class names, but not the result and exception-mapping class names that use the same new restriction.
  • A regression test for $ and backslash handling would help protect the Matcher.quoteReplacement change.

Overall, I think this is useful hardening. I'd mainly like to see the rejected-wildcard behavior addressed so we don't accidentally resolve to another valid class.

A rejected value used to become an empty string and the match still
succeeded, so 'actions.{1}Action' with 'util.Harmless' resolved to
'actions.Action'. The whole match now fails, as it does for a
disallowed method name. Result and exception-mapping class names get
the same check, Unicode letters and digits are accepted, and tests pin
the result/exception slots and the literal handling of '$' and '\'.
@g0w6y

g0w6y commented Nov 12, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review. Addressed in f6d5aef:

  • A rejected wildcard value now fails the whole match (the mapping returns no config) instead of being replaced by an empty string, so actions.{1}Action with util.Harmless no longer resolves to actions.Action.
  • The same check covers result and exception-mapping class names, each with a test.
  • The allowed characters are now Unicode letters and digits, _ and $; . and / are still rejected.
  • Added a test for literal $ and \ in substituted values, which pins the Matcher.quoteReplacement change.

Full core: 3746/0 failures.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants