)]}'
{
  "log": [
    {
      "commit": "26141511f8bb78fde0b432b6c7880e9c3681b0ad",
      "tree": "2914fdc75d4736f858fcdafd28346a55ddf6d30c",
      "parents": [
        "d52ffed7ff435a71b9f65454b407abb9337dc95e"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Wed Jul 29 07:57:45 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 29 07:57:45 2026 +0200"
      },
      "message": "build(deps): bump ossf/scorecard-action from 2.4.3 to 2.4.4 (#1818)\n\nBumps [ossf/scorecard-action](https://github.com/ossf/scorecard-action) from 2.4.3 to 2.4.4.\n- [Release notes](https://github.com/ossf/scorecard-action/releases)\n- [Changelog](https://github.com/ossf/scorecard-action/blob/main/RELEASE.md)\n- [Commits](https://github.com/ossf/scorecard-action/compare/4eaacf0543bb3f2c246792bd56e8cdeffafb205a...2d1146689b8cda280b9bc96326124645441f03bc)\n\n---\nupdated-dependencies:\n- dependency-name: ossf/scorecard-action\n  dependency-version: 2.4.4\n  dependency-type: direct:production\n  update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "d52ffed7ff435a71b9f65454b407abb9337dc95e",
      "tree": "3906d4c15f678afd237ca0ba8cb602993bd75be8",
      "parents": [
        "94a8fcb26cd9f9ba42a80aca8d38e63d54e404cd"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Wed Jul 29 07:57:35 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 29 07:57:35 2026 +0200"
      },
      "message": "build(deps): bump github/codeql-action from 4.37.2 to 4.37.3 (#1817)\n\nBumps [github/codeql-action](https://github.com/github/codeql-action) from 4.37.2 to 4.37.3.\n- [Release notes](https://github.com/github/codeql-action/releases)\n- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)\n- [Commits](https://github.com/github/codeql-action/compare/v4.37.2...v4.37.3)\n\n---\nupdated-dependencies:\n- dependency-name: github/codeql-action\n  dependency-version: 4.37.3\n  dependency-type: direct:production\n  update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "94a8fcb26cd9f9ba42a80aca8d38e63d54e404cd",
      "tree": "41032076c54488973f305b343fb473357de7f630",
      "parents": [
        "6f802987f5bf14dbb6bb3a801480d9224a3631cb"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Wed Jul 29 07:56:58 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 29 07:56:58 2026 +0200"
      },
      "message": "WW-3784 Order annotated wildcard actions most-specific-first (#1813)\n\n* WW-3784 docs: design for specificity-ordered wildcard matching in annotated actions\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-3784 docs: implementation plan for annotated wildcard specificity ordering\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-3784 feat(convention): add action-name specificity comparator\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-3784 fix(convention): add Apache License header to test file\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-3784 feat(core): add PackageConfig.Builder.reorderActionConfigs\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-3784 docs: add javadoc for PackageConfig.Builder.reorderActionConfigs\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-3784 feat(convention): order annotated wildcard actions most-specific-first\n\nSorts each convention-built package\u0027s action configs by pattern specificity so a\nspecific pattern (some/usefull/*) is matched before a general one (some/*),\nregardless of class-scan order. Also makes convention action ordering deterministic.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-3784 docs: correct wildcard cross-segment claims and note comparator limitations\n\nThe spec incorrectly stated that WildcardHelper\u0027s single `*` is greedy\nand crosses `/`, and that `some/*` shadows `some/usefull/*`. Verified\nagainst WildcardHelper.java and NamedVariablePatternMatcher.java: only\n`**` crosses `/`, so those two patterns are actually disjoint (different\nsegment counts) and never compete for the same request. Correct the\nProblem narrative, ticket example, and matcher bullets to state this\naccurately, and document two known limitations of the specificity\ncomparator (raw wildcard-token-count key can misrank `**` ahead of\nnarrower multi-token patterns; parent-package actions bypass sorting).\nAlso add a test asserting the natural-order alphabetical tiebreak key.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-3784 test(convention): prove specificity ordering fixes wildcard shadowing end-to-end\n\nAdds an end-to-end routing test driving the production reorder\n(PackageConfig.Builder.reorderActionConfigs + ActionNameSpecificityComparator)\nthrough the real ActionConfigMatcher/WildcardHelper. some/** and some/usefull/*\ngenuinely overlap for some/usefull/sleeping (** crosses \u0027/\u0027), so the test asserts\nthe general pattern shadows the specific one when registered first, and that\nspecificity ordering makes the specific action reachable again.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "6f802987f5bf14dbb6bb3a801480d9224a3631cb",
      "tree": "3da2e6a0ddac1f83505f08d2cd7e2137b506eabc",
      "parents": [
        "d906f23448cb56216a6c316ae01f0d288b68571b"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Wed Jul 29 07:55:46 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 29 07:55:46 2026 +0200"
      },
      "message": "WW-3871 Derive ConversionRule prefixes for @TypeConversion keys (#1812)\n\n* WW-3871 docs: add design spec for @TypeConversion key derivation\n\nSpecifies deriving the ConversionRule prefix for @TypeConversion keys at\nclass, method and field level via a single resolver, adds ElementType.FIELD\nas a target, and records the break/continue and empty-key fixes in the same\ncode block.\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n\n* WW-3871 docs: note interaction with the 7.3.0 converter mapping cache\n\nRecords that addConverterMapping runs inside the computeMappingIfAbsent\nbuilder introduced by WW-5539, which executes outside any lock, so the new\nfield pass adds no deadlock risk but must stay side-effect free.\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n\n* WW-3871 docs: add implementation plan for @TypeConversion key derivation\n\nSeven TDD tasks covering ConversionRule#prefix(), the shared resolveKey\nhelper, class- and field-level derivation, the break/continue and empty-key\nfixes, an end-to-end binding proof and the Javadoc updates. Refines the\nspec\u0027s resolveKey signature to take the two annotation attributes rather\nthan the annotation instance, so it can be unit tested directly.\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n\n* WW-3871 feat(core): add ConversionRule#prefix() owning the rule-to-prefix table\n\n* WW-3871 refactor(core): split addConverterMapping into per-source passes\n\n* WW-3871 feat(core): derive conversion mapping keys through a single resolver\n\n* WW-3871 fix(core): derive class level conversion keys and stop dropping later entries\n\n* WW-3871 feat(core): support @TypeConversion on fields\n\n* WW-3871 test(core): assert bare conversion keys bind through the action lifecycle\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n\n* WW-3871 docs(core): document conversion key derivation and field level support\n\n* WW-3871 docs(core): add deprecated Collection_ prefix to parameter table\n\n* WW-3871 fix(core): widen resolveKey idempotence guard against any rule prefix\n\nresolveKey only recognized a key as already-prefixed if it started with\nits own declared rule\u0027s prefix. COLLECTION and ELEMENT are interchangeable\nthroughout the conversion pipeline (DefaultConversionAnnotationProcessor\nhandles them in the same branch, DefaultObjectTypeDeterminer.getElementClass\nreads Element_ then falls back to the deprecated Collection_), so\nkey\u003d\"Element_users\" with rule\u003dCOLLECTION silently doubled to\nCollection_Element_users instead of being left alone, losing the mapping.\nMatch against every known rule\u0027s prefix instead.\n\nAlso documents two related precedence subtleties surfaced during review:\nprocessFieldAnnotations\u0027 Javadoc now notes that an inherited method can\nclaim a key before a subclass\u0027s own field annotation is considered, since\ngetMethods() includes inherited methods and runs first; and the\nunresolvable-key WARN in processMethodAnnotations now names the method\u0027s\ndeclaring class rather than the class being scanned, since getMethods()\ncan surface the same inherited method at every level of the hierarchy.\n\nDesign spec section 2 updated to match the implementation.\n\n* WW-3871 docs(core): correct TypeConversion Javadoc property attribute and determiner package\n\nTwo pre-existing errors in the block this ticket\u0027s commits already touch:\nthe APPLICATION example used a non-existent \"property\" attribute where\n\"key\" is the working form (see ConversionTestAction.java:97), and the\nrule() Javadoc pointed at org.apache.struts2.util.DefaultObjectTypeDeterminer\ninstead of the actual org.apache.struts2.conversion.impl package.\n\n* WW-3871 test(core): cover key-prefix crossover, empty class-level key, and KeyProperty_ end-to-end binding\n\n- testResolveKeyLeavesAnAlreadyPrefixedKeyAlone: add the COLLECTION/ELEMENT\n  crossover cases that demonstrate the resolveKey guard fix (fail before,\n  pass after).\n- New EmptyKeyConversionAction fixture plus\n  testClassLevelEmptyKeyRegistersNoMapping: a class-level @TypeConversion\n  with no key must be skipped, not registered under \"\". This was the one\n  behavioural bullet in the spec\u0027s test plan with no coverage.\n- MyBeanActionTest.testBareConversionKeysBindTheSameWayAsPrefixedOnes: add\n  an assertion that the bare KeyProperty_ derivation actually binds the\n  list index onto the created bean\u0027s id property end to end, not just that\n  a converter mapping exists.\n\n* WW-3871 fix(core): skip APPLICATION-scoped @TypeConversion with no explicit key\n\nMethod- and field-level @TypeConversion(type \u003d APPLICATION) with no key\npreviously derived a member name (e.g. a setter\u0027s property name) and\nregistered it in the global default converter map via\naddDefaultMapping. That map is only ever read by class name\n(lookup(String, boolean) and lookup(Class)), so the entry was\npermanently unreachable. Skip it before deriving a name, logging a WARN\nnaming the declaring class and member; the class-level pass already\nhandled this correctly via resolveKey returning null.\n\nAdds a fixture and tests proving no default mapping is registered under\nthe derived member name in either pass.\n\n* WW-3871 docs(core): fix broken TypeConversion Javadoc example and align spec\n\nTypeConversion\u0027s example class declared `users` twice (once\nunannotated, once again at its annotated field), so the sample no\nlonger compiled as written; drop the earlier, redundant declaration.\n\nThe same example\u0027s setConvertInt showed @TypeConversion(type \u003d\nAPPLICATION) with no key - exactly the case the previous commit\u0027s\nXWorkConverter fix now skips. Drop the type attribute so it reads as\na class-scoped conversion, matching the corrected ConversionTestAction\nfixture. The correct APPLICATION example further down (execute(), key\n\u003d \"java.util.Date\") is untouched.\n\nAlso records the APPLICATION no-key skip rule in the design spec\u0027s\ncarve-out paragraph so spec and code agree.\n\n* WW-3871 fix(core): dedupe method-pass WARN logging for inherited @TypeConversion\n\nprocessMethodAnnotations iterates clazz.getMethods(), which includes inherited\npublic methods, and buildConverterMapping calls it once per class in the\nhierarchy. A single misconfigured @TypeConversion on a base class method was\ntherefore logging its WARN once per subclass level. Gate both WARN call sites\non method.getDeclaringClass() \u003d\u003d clazz so each fires exactly once, at the\nlevel that owns the method; the derivation/registration logic keeps running\non every visit unchanged.\n\nAdds a small permanent test proving the gate is logging-only: an inherited\nannotated setter still resolves and registers through a subclass that\noverrides nothing.\n\n* WW-3871 docs(core): clarify field-name key default and dedicated-annotation precedence\n\nTwo gaps in the @TypeConversion Javadoc, both newly relevant now that the\nannotation targets fields:\n\n- The key() default on a field is the field name, not the JavaBean property\n  name (processFieldAnnotations uses field.getName()). A field like _users\n  backing property users would otherwise derive CreateIfNull__users, a key\n  DefaultObjectTypeDeterminer never looks up.\n- org.apache.struts2.util\u0027s dedicated field annotations (@Key, @Element,\n  @KeyProperty, @CreateIfNull) are consulted by DefaultObjectTypeDeterminer\n  before it falls back to the converter mapping @TypeConversion populates,\n  so a dedicated annotation silently wins over an equivalent @TypeConversion\n  on the same property. Verified against getAnnotation/getElementClass/\n  getKeyProperty in DefaultObjectTypeDeterminer before documenting it.\n\n* WW-3871 docs(core): note COLLECTION derives the deprecated Collection_ prefix\n\nConversionRule.COLLECTION.prefix() intentionally returns Collection_, the\nspelling DefaultObjectTypeDeterminer treats as deprecated and logs an INFO\nabout on every fallback hit, kept for compatibility with existing\nannotations. Document that the derivation is deliberate and point readers\nat ELEMENT as the current form.\n\n* WW-3871 refactor(core): extract shared annotation-registration pipeline\n\nprocessMethodAnnotations and processFieldAnnotations were the same\nfive-step pipeline (skip non-@TypeConversion, skip APPLICATION-scoped\nwithout a key, derive the name, resolve the key, register unless\nalready mapped) written twice, driving SonarCloud S3776 cognitive\ncomplexity to 26 and 21 respectively and triggering three S135\nmultiple-break/continue findings.\n\nExtract steps 2-5 into a private registerAnnotatedMember(mapping, tc,\nMember, fallbackName, logSkips) helper that both passes delegate to.\nEach pass is now just its loop plus one instanceof check. The method\npass keeps its per-declaring-class log gate (getMethods() revisits\ninherited methods once per hierarchy level); the field pass always\nlogs, since getDeclaredFields() is visited once per class. The two\nWARN wordings, which differed only in a trailing clause, are merged\ninto one message accurate for both a method and a field.\n\nNo change to the registered mapping, pass order, or precedence for\nany class - verified via the existing XWorkConverterTest,\nAnnotationXWorkConverterTest, MyBeanActionTest, and ConversionRuleTest\nsuites (92 tests, same count and same triggering warnings before and\nafter) plus the full core module suite (3043 tests).\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n\n* WW-3871 docs(core): fix inaccurate and self-contradicting TypeConversion key Javadoc\n\n@Key, @Element, @KeyProperty and @CreateIfNull are @Target({FIELD, METHOD}), not\nfield-only, and the same paragraph already says they are read from the field,\nsetter and getter - drop \"field\" from \"dedicated field annotations\". Fold the\nfield-vs-property-name correction into key()\u0027s opening sentence instead of\nstating \"defaults to the property name\" and rebutting it three lines later, and\nalign the parameters table row for key with the same rule.\n\n* WW-3871 docs(core): clarify XWorkConverter annotation-registration logging\n\nGive the success DEBUG the same [declaringClass#member] shape the three skip\nmessages already use, instead of logging the bare member name that identifies\nneither the class nor whether it was a method or a field. Reword the \"already\nmapped\" DEBUG so it covers its commonest trigger - the same annotation seen one\nhierarchy level down, not just a genuinely higher-precedence source. Note in the\nlogSkips comment that buildConverterMapping only visits each class\u0027 direct\ninterfaces, so a misconfigured annotation declared on a super-interface method\nnever gets logged at all, even though registration is unaffected. Also drop a\nstray extra blank line.\n\nNo behavioural change: registration/derivation logic is untouched.\n\n* WW-3871 test(core): make inherited-method-annotation test diagnostic\n\ntestInheritedMethodAnnotationStillRegistersThroughASubclass previously asserted\nnothing the logSkips gate could break: the hierarchy walk always reaches\nInheritedMethodConversionAction itself, where declaringClass \u003d\u003d clazz, so the\nkey registers there regardless of whether registration is (wrongly) gated\nalongside logging. The test passed identically with logSkips hardcoded true or\nfalse.\n\nGive InheritedMethodConversionSubAction a contesting field annotation for the\nsame property the inherited setter claims. The inherited method annotation\nregisters at the subclass level - before the subclass\u0027s own field pass runs -\nso its value must keep winning; that is the invariant documented on\nprocessFieldAnnotations, and it is exactly what gating registration would\nbreak, since the subclass field would start winning over the inherited method\nannotation instead.\n\nVerified: temporarily wrapping the registerAnnotatedMember call in\nprocessMethodAnnotations with `if (logSkips)` makes this test fail\n(expected:\u003ctrue\u003e but was:\u003cfalse\u003e); reverting it passes again. Mutation was not\ncommitted.\n\nCorrected both Javadocs, which overclaimed what the old assertion proved.\n\n---------\n\nCo-authored-by: Claude Opus 5 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "d906f23448cb56216a6c316ae01f0d288b68571b",
      "tree": "585ed785e5bf55022731b1b548af13a35c3e1dac",
      "parents": [
        "532ca7f864ae8d72bbf740a0e2d9d11191c0fadb"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Wed Jul 29 07:55:22 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 29 07:55:22 2026 +0200"
      },
      "message": "WW-3530 Fix visitor-validator cache-key collision under wildcard actions (#1811)\n\n* WW-3530 docs: add design spec for visitor-validator cache-key fix\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-3530 docs: add implementation plan for visitor-validator cache-key fix\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-3530 test(core): cover visitor-validator cache-key context handling under wildcard actions\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-3530 fix(core): keep visitor-validator context in cache key under wildcard actions\n\nApply the wildcard config-name substitution only when validating the action\u0027s\nown class. Visited objects carry a stable, explicit visitor context that must\nremain part of the cache key, otherwise two visitor validators on one field with\ndifferent contexts collide and the second is silently dropped.\n\nFixes https://issues.apache.org/jira/browse/WW-3530\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-3530 docs: document \u003cs:form\u003e render-path caching limitation\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-3530 docs: correct WW-2996 scope claim and note default-context visitor limitation\n\nFinal-review finding: default-context visitor validators under wildcard actions\nkey on the volatile resolved action name for the visited class, reintroducing\nbounded WW-2996-style cache growth (memory only; correct validators still load).\nCorrect the \u0027WW-2996 untouched\u0027 wording to \u0027untouched for the action\u0027s own class\u0027,\ndocument the subpath as an accepted limitation folded into the follow-up ticket,\nand clarify that end-to-end visitor execution is covered by existing visitor suites.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-3530 chore(core): add DEBUG logging for validator cache-key branch decision\n\nLog the built key together with clazz, context, validatingActionClass, wildcard,\nand the action config name, so the wildcard-vs-visited-object branch taken in\nbuildValidatorKey can be diagnosed at runtime.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-3530 test(core): use assertNotEquals for cache-key inequality; fix comment grammar\n\nAddress SonarCloud S5785 (assertFalse+equals -\u003e assertNotEquals) and a Copilot\ngrammar nit in the WW-4536 comment. DEBUG logging kept as-is per author decision.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "532ca7f864ae8d72bbf740a0e2d9d11191c0fadb",
      "tree": "6bbf2a1cda6d84b2b5fd6e95941f4efc31cfe8ca",
      "parents": [
        "0b2bc2be1420829dfd26356dd8ea906a793c8047"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Mon Jul 27 17:40:21 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 27 17:40:21 2026 +0200"
      },
      "message": "WW-2934 Skip field validators when a field has a conversion error (opt-in) (#1810)\n\n* WW-2934 docs: add design spec for skipping validators on conversion error\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-2934 docs: add implementation plan for skipping validators on conversion error\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-2934 feat(core): skip field validators on conversion error behind opt-in flag\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-2934 test(core): cover annotation manager + document conversion-error skip exemption\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-2934 test(core): assert custom conversion message survives and cover nested field skip\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "0b2bc2be1420829dfd26356dd8ea906a793c8047",
      "tree": "5bc549dd11d2ff4d40629d5d9db6b23463d3c6e1",
      "parents": [
        "12015d0bf5ff4683187c16c36d93cd3144553ded"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Sun Jul 26 10:33:09 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Jul 26 10:33:09 2026 +0200"
      },
      "message": "WW-3427 Add regression test for conversion errors on aliased properties (#1814)\n\n* WW-3427 test(core): cover conversion errors on aliased properties\n\nReproduce the WW-3427 scenario: an aliased property whose custom\nTypeConverter throws TypeConversionException. AliasInterceptor already\nreports such errors (setReportingConversionErrors on the secure child\nstack, then copies conversion errors back to the original ActionContext),\nbut nothing exercised the alias + conversion-error path.\n\nThe test drives an action through params -\u003e alias -\u003e conversionError and\nasserts the failure surfaces both in ActionContext.getConversionErrors()\nand as a field error, confirming WW-3427 is fixed. Removing the copy-back\nin AliasInterceptor makes it fail with \"swallowed\", proving it guards the\nbehavior.\n\nTest-only; no production changes.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-3427 test(core): add Apache license header to conversion.properties\n\nRAT flagged the new test resource as having an unapproved license.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "12015d0bf5ff4683187c16c36d93cd3144553ded",
      "tree": "81ca223e0b3a88da6ec9c7a36767e0b2f5a62094",
      "parents": [
        "833220346ceb32ee44533da715ab3b59af05c16f"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Sun Jul 26 09:52:08 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Jul 26 09:52:08 2026 +0200"
      },
      "message": "WW-5540 Add caching to AbstractLocalizedTextProvider (#1808)\n\n* WW-5540 docs: add caching design spec for AbstractLocalizedTextProvider\n\nDesign for caching the class/package hierarchy traversal result in\nfindText, keyed on (classloader, class name, textKey, locale). Caches\nthe raw resolved pattern (or a NOT_FOUND marker) only; translation and\nformatting stay per-call. Wires invalidation into the existing\nreloadBundles/clearBundle/clearMissingBundlesCache sites.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5540 docs: add implementation plan and refine spec\n\nAdd the 3-task TDD implementation plan and record the\nformatWithNullDetection fall-through decision in the spec.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5540 docs: deprecate+delegate findMessage/getMessage in plan\n\nResolve pre-flight duplication/dead-code finding: old traversal helpers\ndelegate to the raw twins and are marked @Deprecated instead of being\nduplicated. Add a direct characterization test for the findMessage delegator.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5540 refactor(core): split raw message resolution from formatting\n\nAdd getRawMessage/formatMessage and a raw twin findMessageRaw. Re-express\ngetMessage via formatMessage and make findMessage delegate to\nfindMessageRaw + formatMessage; deprecate both as legacy extension points\nsuperseded by the raw-resolution path. The deprecated findMessage triggers\nthe bundle reload on entry, preserving the reload side effect the old\ngetMessage-per-probe walk provided. Groundwork for the traversal caches.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5540 docs: refine Task 1 plan (deprecate/delegate + reload-on-entry)\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5540 perf(core): cache class-hierarchy text resolution\n\nCache the class/interface/superclass traversal in findText keyed on\n(classloader, class name, key, locale), storing the raw pattern or a\nNOT_FOUND marker. Formatting stays per call and falls through to the\nnext tier when a cached pattern formats to null. Invalidated on\nreloadBundles/clearBundle/clearMissingBundlesCache; reload is hoisted\nto the top of findText so caches are cleared before they are read.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5540 docs: draft follow-up ticket for null-control-flow cleanup\n\nCapture the deferred result-wrapper refactor (raised during WW-5540) as a\nready-to-file Jira draft; keep WW-5540 focused on caching.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5540 perf(core): cache package-hierarchy text resolution\n\nCache the *.package traversal in findText the same way as the class\nhierarchy, with the same keying, fall-through, and invalidation.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5540 test(core): tighten localized-text cache tests\n\nAssert single cache entry in the per-call-format tests (proves the raw\npattern is cached, not the formatted result), and mirror the package-cache\nclearBundle/clearMissingBundlesCache invalidation test.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5540 docs: note devMode null-valueStack eager-reload edge\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5540 docs: link follow-up doc to filed ticket WW-5655\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5540 chore(core): add ASF license header to CacheFixture.properties\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5540 chore(core): add since/forRemoval to @Deprecated annotations\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5540 docs: drop follow-up draft superseded by WW-5655\n\nThe ticket is filed; the draft\u0027s content now lives in WW-5655 itself.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5540 fix(core): address fresh-eyes review findings\n\n- Document that the deprecated getMessage/findMessage are no longer\n  invoked by findText, and name formatMessage as the override point\n- Fall back to the ActionContext-based reloadBundles() when findText is\n  called without a value stack, so the RELOADED flag is tracked and the\n  caches can warm on that path in reload/devMode\n- Narrow resolveClassHierarchyRaw/resolvePackageHierarchyRaw to\n  package-private (the cache key omits indexedKey, which is safe only\n  when derived from textKey as the internal call sites do)\n- Suppress java:S2129 on the NOT_FOUND identity sentinel\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5540 docs: strip stray NUL bytes from design spec\n\nTwo literal NUL bytes in the sentinel example made git/GitHub treat the\nwhole markdown file as binary and unreviewable in the PR UI; align the\nexample with the shipped sentinel name.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5540 test(core): cover ModelDriven tier, per-locale keys, indexed keys\n\nClose the review-noted coverage gaps: the ModelDriven tier resolves via\nthe shared class-hierarchy cache (action miss + model hit), each locale\ngets its own cache entry backed by a new _de fixture bundle, and indexed\nkeys (name[N] -\u003e name[*]) resolve and cache per full textKey.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5540 fix(core): address Copilot review comments\n\n- Partition the caches by System.identityHashCode of the context\n  classloader so a custom ClassLoader overriding hashCode() cannot\n  collide or collapse the per-loader partitions\n- Derive the indexed key inside the resolvers (miss-only) instead of\n  accepting it as a parameter, so the cache key trivially covers every\n  input that influences the resolution result\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5540 fix(core): resolve SonarCloud quality-gate findings\n\n- Suppress S4973 on isNotFound: the identity comparison against the\n  non-interned NOT_FOUND sentinel is the design, not a bug\n- Reduce findMessageRaw cognitive complexity (S3776) by extracting\n  getRawMessageWithAlternate, reused by the package walk\n- Add missing @Override annotations and suppress the deliberate\n  deprecated-delegator call in the test helper (S1161, S5738)\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5540 fix(core): make findMessageRaw cycle guard effective\n\nThe `checked` set tested `contains(clazz.getName())` but never added the\nclass, so the diamond-interface cycle guard was a no-op (a latent issue\ninherited from the original findMessage). Add the class name after the\ncontains-check so repeated interface branches aren\u0027t re-traversed. Behavior\nis unchanged (lookups are idempotent); this only avoids redundant recursion.\nThe deprecated findMessage delegates here, so it\u0027s fixed too.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5540 docs(core): reference WW-5658 in deprecated method javadoc\n\nPoint the @deprecated javadoc of getMessage and findMessage at WW-5658,\nthe ticket tracking their removal in the next major release.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "833220346ceb32ee44533da715ab3b59af05c16f",
      "tree": "6060aa24ef6cb15e9fd1378d1186b7c4047c1bfb",
      "parents": [
        "c3e9db1b21fc9e8453c654e2f7a18e13bf3cf8d3"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Sun Jul 26 04:57:19 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Jul 26 04:57:19 2026 +0200"
      },
      "message": "WW-5474 Count files only for maxFiles, add maxParameterCount (#1806)\n\n* WW-5474 docs(multipart): design for files-only maxFiles + maxParameterCount\n\nSpec for correcting struts.multipart.maxFiles to count file parts only\n(consistently across the jakarta and jakarta-stream parsers) and adding\nstruts.multipart.maxParameterCount to cap non-file form fields, restoring\nthe DoS guard the old accidental total-part cap provided. Fail-closed on\nbreach.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5474 docs(multipart): implementation plan for maxFiles/maxParameterCount\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5474 fix(multipart): count files only for maxFiles, add maxParameterCount (jakarta)\n\nThe jakarta parser passed maxFiles to commons-fileupload2 setMaxFileCount,\nwhich counts every part (fields + files), so maxFiles wrongly limited total\nparameters. Enforce a files-only count and non-file field count in Struts,\nfailing closed on breach; keep a total-parts commons backstop.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5474 fix(multipart): honor -1 unlimited sentinel in total-parts backstop\n\nprepareServletFileUpload applied the total-parts backstop whenever both\nmaxFiles and maxParameterCount were non-null, without checking for the\n-1 \"unlimited\" sentinel already honored by enforceMaxFiles/enforceMaxParameterCount.\nWith maxFiles\u003d-1 and maxParameterCount\u003d256, maxParts computed to 255 and\nwas passed to commons-fileupload2\u0027s setMaxFileCount (which counts ALL\nparts), wrongly rejecting large file-only uploads. Only apply the\nbackstop when both limits are finite (non-null and \u003e\u003d 0).\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5474 fix(multipart): apply files-only maxFiles + maxParameterCount to stream parser\n\nReplace the field-name-based exceedsMaxFiles with the shared files-only\nenforcement and add parameter-count enforcement, matching the jakarta parser\nand failing closed on breach.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5474 fix(multipart): track all parsed items to avoid temp-file leak on fail-closed breach\n\nservletFileUpload.parseRequest() fully materializes every part - spilling\nlarge ones to disk - before processUpload() iterates over the result. The\nloop only added each DiskFileItem to diskFileItems as it was reached, so\nwhen enforceMaxFiles/enforceMaxParameterCount threw mid-loop on a breach,\nevery item positioned after the breaching one was never registered for\ncleanup. With no FileCleaningTracker on the factory, cleanUp() had no way\nto reclaim those temp files, leaking disk space on the hardening path.\n\nMaterialize the parsed list once and register all items for cleanup\nbefore processing so cleanUp() reclaims every temp file regardless of\nwhere enforcement aborts.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5474 fix(multipart): guard debug logging in enforce helpers (Sonar S2629)\n\nWrap the LOG.debug calls in enforceMaxFiles/enforceMaxParameterCount with\nisDebugEnabled() so normalizeSpace() is not evaluated when debug is disabled,\nmatching the exceedsMaxStringLength pattern in the same class.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5474 fix(multipart): address Copilot review - parser parity + overflow guard\n\n- JakartaMultiPartRequest: only count/enforce a file part toward maxFiles when it\n  has a non-null field name, matching JakartaStreamMultiPartRequest\u0027s accept criteria\n  (defensive: commons-fileupload2 already drops parts without a name attribute before\n  parseRequest returns, so the two parsers stay consistent regardless).\n- AbstractMultiPartRequest: compute the total-parts backstop with Math.addExact and\n  clamp to Long.MAX_VALUE on overflow, so extremely large configured limits cannot\n  wrap negative and silently disable the commons backstop.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5474 fix(multipart): log \"processing a form field\" only for form fields\n\nMove the debug log into the isFormField branch so file parts are not\nmislabelled; the file branch already logs \"Processing a file\".\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "c3e9db1b21fc9e8453c654e2f7a18e13bf3cf8d3",
      "tree": "9ead1b3ea18e7666d2cb162326744cbc2100d2e5",
      "parents": [
        "8a5323fbdfa45d8d1cc9ce87888f24d8dc2cac99"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Sun Jul 26 04:29:05 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Jul 26 04:29:05 2026 +0200"
      },
      "message": "WW-5413 Avoid writing small in-memory multipart uploads to disk (#1805)\n\n* WW-5413 docs(core): design for in-memory multipart upload optimization\n\nLazy-materializing UploadedFile plus a new getInputStream() accessor so\nsmall (in-memory) uploads no longer eagerly write a temp file, while\ngetContent() keeps returning a File for backward compatibility.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5413 docs(core): implementation plan for in-memory upload optimization\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5413 feat(core): add UploadedFile.getInputStream() streaming accessor\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5413 feat(core): add lazily-materializing StrutsInMemoryUploadedFile\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5413 fix(core): make StrutsInMemoryUploadedFile serializable and thread-safe\n\n* WW-5413 refactor(core): drop eager temp-file write for in-memory uploads\n\n* WW-5413 test(core): cover deferred-write behavior for in-memory uploads\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5413 perf(core): avoid materializing in-memory uploads during interceptor validation\n\n* WW-5413 chore(core): clean up partial materialization and cover isMissing()\n\n* WW-5413 docs(core): sync design/plan with interceptor fix and deviations\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5413 chore(core): deprecate now-unused STRUTS_MESSAGES_INVALID_CONTENT_TYPE_KEY\n\nMark the orphaned constant @Deprecated(forRemoval \u003d true) instead of leaving it\nsilently unused. The message key it referenced was only emitted from an unreachable\nblock in acceptFile() that was removed with the in-memory upload optimization.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5413 test(core): cover materialization failure and getInputStream default branches\n\nAddress review follow-ups on PR #1805:\n- document that processFileField\u0027s retained \u0027throws IOException\u0027 is intentional\n  (subclass source compatibility), not an oversight\n- add a negative test: getContent() on an unwritable save dir throws StrutsException,\n  stays unmaterialized, and leaves no partial file behind\n- cover the UploadedFile.getInputStream() default File branch and the no-content\n  IOException branch\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5413 fix(core): address SonarCloud and Copilot review findings\n\n- materialize() now writes with StandardOpenOption.CREATE_NEW and fails closed if the\n  target already exists, so a pre-planted file/symlink is never overwritten or followed\n  (Copilot security note) + regression test\n- defensively copy the content byte array on construction and reject null content, so the\n  instance owns its bytes and cannot observe caller mutation (Copilot / review)\n- delete() uses Files.deleteIfExists and logs the real cause on failure instead of a silent\n  File.delete() boolean (Sonar MAJOR)\n- reorder field modifiers to JLS order \u0027transient volatile\u0027 (Sonar)\n- tests: assertThat(dir).isEmptyDirectory() instead of listFiles().isEmpty() (Sonar);\n  drop unused DiskFileItem import (Sonar)\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "8a5323fbdfa45d8d1cc9ce87888f24d8dc2cac99",
      "tree": "03556452db5dc57308b96ebbd8a8047d43c0f9db",
      "parents": [
        "73f17c1be7bbc4cde5b1ba007298a6cfefa5b4bf"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Sat Jul 25 12:30:53 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Jul 25 12:30:53 2026 +0200"
      },
      "message": "WW-5539 Concurrency performance enhancements (#1799)\n\n* WW-5539 docs: add concurrency performance enhancements design\n\nDesign for removing coarse locks from XWorkConverter,\nDefaultActionValidatorManager and StrutsTypeConverterHolder in favour of\nconcurrent collections.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5539 docs: add concurrency performance implementation plan\n\nFive tasks derived from the approved design: make StrutsTypeConverterHolder\nconcurrent, add the computeMappingIfAbsent SPI method, remove the locks from\nXWorkConverter and DefaultActionValidatorManager, then benchmark and raise\nthe PR.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5539 docs: make conditionalReload store its rebuilt mapping\n\nbuildConverterMapping no longer stores its result, so reload mode would\nhave rebuilt from disk on every request without ever caching.\n\n* WW-5539 Make StrutsTypeConverterHolder collections concurrent\n\nThe holder is a container singleton whose HashMaps were read without any\nlock by XWorkConverter.lookup() while being written elsewhere, risking\nlost updates and torn reads during resize.\n\nNull TypeConverters are now ignored with a warning rather than stored,\nsince ConcurrentHashMap forbids null values and a null converter left the\nholder in an inconsistent state.\n\n* WW-5539 Rename test to match what it actually covers\n\nThe method exercised only the unknown-mapping cache, not noMapping.\n\n* WW-5539 Add TypeConverterHolder#computeMappingIfAbsent\n\nAdds an atomic build-once-and-cache operation so callers no longer need\ncheck-then-act around the class mapping cache, and deprecates the three\nprimitives it subsumes: getMapping, addMapping and containsNoMapping.\n\nThe method is a default method delegating to those primitives, so\nthird-party TypeConverterHolder implementations keep working unchanged.\n\n* WW-5539 Deduplicate the no-mapping path in computeMappingIfAbsent\n\nConcurrentHashMap.computeIfAbsent stores nothing when the mapping function\nreturns null, so every concurrent caller re-ran the builder for a class\nwith no conversion mapping - the common case for an ordinary action, and\nthe exact thundering herd this method exists to prevent.\n\nNegative results now store a sentinel in the same map, so the builder runs\nonce per class either way. getMapping and containsNoMapping translate the\nsentinel, preserving their existing contracts.\n\n* WW-5539 docs: sync plan with negative-cache sentinel fix\n\n* WW-5539 Pin down addNoMapping\u0027s override semantics\n\nStoring the no-mapping sentinel deliberately replaces any mapping cached\nfor the class, matching the pre-7.3.0 effective behaviour where such a\nclass was short-circuited before its cached mapping was ever read.\nputIfAbsent would instead serve a stale mapping after a failed build.\n\nAlso asserts the sentinel translation in getMapping directly, and stops\nthe interface javadoc promising a specific empty-map instance that\nimplementations are not required to return.\n\n* WW-5539 Document that addNoMapping may replace a cached mapping\n\nThe behaviour was documented only on the Struts implementation, but\naddNoMapping stays a non-deprecated SPI primitive that third parties both\ncall and implement, so the contract belongs on the interface.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5539 Remove coarse locks from XWorkConverter\n\ngetConverter() synchronized on the Class object being converted, which is\na globally visible monitor any other library may contend on, and which\nserialised every conversion for a given action class including cache\nhits. It now delegates to TypeConverterHolder#computeMappingIfAbsent.\n\nregisterConverter and registerConverterNotFound drop their synchronized\nmodifier; they are single delegations to a concurrent map, and the lock\nnever covered the readers in lookup() in any case.\n\nbuildConverterMapping no longer stores its result - storage is owned by\ncomputeMappingIfAbsent.\n\n* WW-5539 Remove global lock from DefaultActionValidatorManager\n\ngetValidators() was synchronized on the singleton manager, so every\nvalidated request in the application serialised on it - and the lock\ncovered the per-request Validator construction loop, which operates on\nper-request objects and never needed mutual exclusion.\n\nBoth caches become ConcurrentHashMap and cached config lists are wrapped\nunmodifiable, since several threads now iterate them concurrently.\n\n* WW-5539 Make the validator concurrency test race a cold cache\n\nThe test computed its expected count with a getValidators call before\nstarting the threads, which warmed the cache and left all 16 workers on\nthe fast path - never exercising first-build contention, the race the\ntest is named for.\n\nAlso drops an unused import and awaits executor termination.\n\n* WW-5539 Address final review findings\n\nRestores the protected unknownMappings field verbatim as a deprecated,\nunused vestige: retyping it changed the field descriptor, so a subclass\ncompiled against 7.2.0 would have hit NoSuchFieldError on upgrade without\nrecompiling. Real storage moves to a private concurrent set.\n\nAlso stops conditionalReload running for negative-cached classes, which\nhad been costing a failed classloader resource scan per property per\nrequest in devMode, and restores the unknown-mapping clearing that the\nnull-converter guard was skipping.\n\n* WW-5539 Fix concurrency regressions from coarse-lock removal\n\nFour correctness fixes surfaced in PR review of the concurrent-collections\nrefactor:\n\n- StrutsTypeConverterHolder.addDefaultMapping: restore put-before-remove\n  ordering. The inverted order let a concurrent XWorkConverter.lookup\n  observe (unknown\u003dfalse, default\u003dfalse), sending it into lookupSuper()\n  and letting it overwrite the more specific converter being registered.\n- StrutsTypeConverterHolder.computeMappingIfAbsent: stop building inside a\n  ConcurrentHashMap bin lock. The builder reaches ObjectFactory.buildConverter,\n  which can autowire arbitrary user TypeConverters; running that under a CHM\n  bin lock risked a recursive-update exception or self-deadlock. Callers now\n  only get the guarantee that they converge on the same cached instance, not\n  that the builder runs exactly once - documented on the interface and\n  reflected in the concurrency tests.\n- DefaultValidatorFactory.validators: switch to ConcurrentHashMap now that\n  DefaultActionValidatorManager.getValidators is no longer synchronized,\n  so runtime registerValidator() calls no longer race unsynchronized reads\n  of a plain HashMap.\n- XWorkConverter.conditionalReload: route empty devMode rebuilds through\n  addNoMapping instead of addMapping, so an empty reload result is stored\n  as the NO_MAPPING sentinel rather than a plain empty map that would\n  silently disable further reloads for the class.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5539 Add tests closing coverage gaps from coarse-lock removal\n\nSonarCloud\u0027s quality gate failed at 59.8% coverage on new code (need\n\u003e\u003d80%). Adds tests for the specific lines JaCoCo identified as\nuncovered, without touching production code:\n\n- TypeConverterHolder.computeMappingIfAbsent\u0027s default method body\n  (the SPI compatibility fallback for third-party holders that predate\n  7.3.0 and don\u0027t override it) - new TypeConverterHolderTest against a\n  minimal non-overriding implementation.\n- StrutsTypeConverterHolder.getMapping/containsNoMapping\u0027s remaining\n  non-sentinel branch.\n- XWorkConverter.conditionalReload\u0027s reloadingConfigs\u003d\u003dtrue path (both\n  the addMapping and addNoMapping outcomes), buildConverterMappingUnchecked\u0027s\n  checked-to-IllegalStateException wrapping, and getConverter\u0027s\n  catch(Throwable) negative-caching.\n- DefaultActionValidatorManager\u0027s else-if(reloadingConfigs) cache\n  rebuild, loadFile\u0027s checkFile\u0026\u0026fileNeedsReloading re-parse, and\n  buildValidatorConfigs\u0027 already-checked short-circuit.\n\nTypeConverterHolder.java and StrutsTypeConverterHolder.java are now at\n0 missed lines/branches. XWorkConverter.java and\nDefaultActionValidatorManager.java have all requested target lines\ncovered; remaining misses are pre-existing, unrelated gaps left alone\nper scope. Full core suite: 3026 tests (3015 + 11 new), 0\nfailures/errors.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5539 Fix SonarCloud deprecation and test-hygiene issues\n\n- Add since/forRemoval attributes to the 7 @Deprecated elements on\n  TypeConverterHolder.getMapping/addMapping/containsNoMapping and\n  StrutsTypeConverterHolder\u0027s overrides plus the unknownMappings field\n  (java:S6355).\n- Add the missing @deprecated Javadoc tag to the three\n  StrutsTypeConverterHolder overrides, pointing at\n  computeMappingIfAbsent as the replacement (java:S1123).\n- Remove the unused throws Exception from\n  testGetConverterBuildsMappingExactlyOncePerClass (java:S1130).\n- Document why StubFileManager.setReloadingConfigs/monitorFile are\n  intentionally empty no-ops (java:S1186).\n- Rename a local variable that shadowed the converter field in\n  testConditionalReloadRebuildsEmptyMappingAndStoresItViaAddNoMapping\n  (java:S1117).\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5539 Suppress removal warnings for the deprecated holder primitives\n\njavac treats [removal] as a category separate from [deprecation], so\nmarking the three primitives forRemoval left four warnings behind: the\ndeliberate addMapping call in conditionalReload, and the three overrides\nthat must exist for as long as the interface declares them.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5539 Address Copilot review comments\n\nFixes getMapping\u0027s @return (it returns a Map, not a TypeConverter) and\ndrops the \"atomically\" wording from its @deprecated tag, which no longer\nmatches computeMappingIfAbsent\u0027s contract now that the builder may run\nmore than once under concurrent first access.\n\nSyncs the design and plan docs with the shipped approach: the\nunknownMappings field is kept for binary compatibility rather than\nretyped, and the override uses get/build/putIfAbsent rather than\ncomputeIfAbsent.\n\n* WW-5539 docs: correct the classloader out-of-scope note\n\nThe conversion caches are container-scoped singletons with no external\nreferences, so their Class keys do not independently pin the webapp\nclassloader - that is governed by whatever retains the container (WW-5537).\nReframed as optional defense-in-depth cache clearing, folded into WW-5537\nTask 5b, rather than a standalone leak fix.\n\n---------\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "73f17c1be7bbc4cde5b1ba007298a6cfefa5b4bf",
      "tree": "ae3a7836a25ea0e87375aa35ef8397183ac0ef01",
      "parents": [
        "eb69ad6f3af75e998364087ea833d1bffc585d4c"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Wed Jul 22 19:22:20 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 22 19:22:20 2026 +0200"
      },
      "message": "build(deps-dev): bump org.webjars:jquery from 3.7.1 to 4.0.0 (#1802)\n\nBumps [org.webjars:jquery](https://github.com/jquery/jquery) from 3.7.1 to 4.0.0.\n- [Release notes](https://github.com/jquery/jquery/releases)\n- [Changelog](https://github.com/jquery/jquery/blob/main/changelog.md)\n- [Commits](https://github.com/jquery/jquery/compare/3.7.1...4.0.0)\n\n---\nupdated-dependencies:\n- dependency-name: org.webjars:jquery\n  dependency-version: 4.0.0\n  dependency-type: direct:development\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "eb69ad6f3af75e998364087ea833d1bffc585d4c",
      "tree": "8320aa094bb32e92f603e6884e148d5423b26e02",
      "parents": [
        "88d95b2a5f207e2910aff20ec2a1c21b2fc3b530"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Wed Jul 22 18:57:16 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 22 18:57:16 2026 +0200"
      },
      "message": "build(deps): bump org.webjars:bootstrap from 5.3.7 to 5.3.8 (#1803)\n\nBumps [org.webjars:bootstrap](https://github.com/webjars/bootstrap) from 5.3.7 to 5.3.8.\n- [Commits](https://github.com/webjars/bootstrap/compare/bootstrap-5.3.7...bootstrap-5.3.8)\n\n---\nupdated-dependencies:\n- dependency-name: org.webjars:bootstrap\n  dependency-version: 5.3.8\n  dependency-type: direct:production\n  update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "88d95b2a5f207e2910aff20ec2a1c21b2fc3b530",
      "tree": "3a6b5f1b0ed125f80a367698c9157cd68a15625c",
      "parents": [
        "ef8361caa7a20a9410b2df6be83b9c8d9d0bf158"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Wed Jul 22 18:55:58 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 22 18:55:58 2026 +0200"
      },
      "message": "build(deps): bump org.webjars:webjars-locator-lite from 1.1.3 to 1.1.4 (#1801)\n\nBumps [org.webjars:webjars-locator-lite](https://github.com/webjars/webjars-locator-lite) from 1.1.3 to 1.1.4.\n- [Release notes](https://github.com/webjars/webjars-locator-lite/releases)\n- [Commits](https://github.com/webjars/webjars-locator-lite/compare/webjars-locator-lite-1.1.3...webjars-locator-lite-1.1.4)\n\n---\nupdated-dependencies:\n- dependency-name: org.webjars:webjars-locator-lite\n  dependency-version: 1.1.4\n  dependency-type: direct:production\n  update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "ef8361caa7a20a9410b2df6be83b9c8d9d0bf158",
      "tree": "a88a184584381d7a7484755b17678ca454172166",
      "parents": [
        "a324538650df9150d572eadb4d68bf95fd2e80c6"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Wed Jul 22 18:55:44 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 22 18:55:44 2026 +0200"
      },
      "message": "build(deps): bump github/codeql-action from 4.37.0 to 4.37.2 (#1800)\n\nBumps [github/codeql-action](https://github.com/github/codeql-action) from 4.37.0 to 4.37.2.\n- [Release notes](https://github.com/github/codeql-action/releases)\n- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)\n- [Commits](https://github.com/github/codeql-action/compare/v4.37.0...v4.37.2)\n\n---\nupdated-dependencies:\n- dependency-name: github/codeql-action\n  dependency-version: 4.37.2\n  dependency-type: direct:production\n  update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "a324538650df9150d572eadb4d68bf95fd2e80c6",
      "tree": "9e32b13e42017094c041003d1c630acbde5b5392",
      "parents": [
        "b154b7ca437b1a46f5b1668f1ad08ff55749d8f5"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Wed Jul 22 18:55:27 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 22 18:55:27 2026 +0200"
      },
      "message": "build(deps): bump org.htmlunit:htmlunit from 5.2.0 to 5.3.0 (#1804)\n\nBumps [org.htmlunit:htmlunit](https://github.com/HtmlUnit/htmlunit) from 5.2.0 to 5.3.0.\n- [Release notes](https://github.com/HtmlUnit/htmlunit/releases)\n- [Commits](https://github.com/HtmlUnit/htmlunit/compare/5.2.0...5.3.0)\n\n---\nupdated-dependencies:\n- dependency-name: org.htmlunit:htmlunit\n  dependency-version: 5.3.0\n  dependency-type: direct:production\n  update-type: version-update:semver-minor\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "b154b7ca437b1a46f5b1668f1ad08ff55749d8f5",
      "tree": "d7dfdf3c0e4866bcaa68d4e8e4f334d143337341",
      "parents": [
        "9f030f62be9cd5a83c9d62cdad4e34a338c0a8be"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Wed Jul 22 18:52:56 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 22 18:52:56 2026 +0200"
      },
      "message": "ci(owasp): cap job timeout and disable NVD auto-update in check step (#1807)\n\n* ci(owasp): cap job timeout and disable NVD auto-update in check step\n\nThe OWASP job intermittently failed with no reason other than timeouts.\nRoot cause is the unreliable NIST NVD feed (see dependency-check#8633):\nkeyless NVD downloads are heavily rate-limited and stall.\n\nTwo fixes:\n- Add timeout-minutes: 30 so a hung NVD download fails fast instead of\n  dragging to the 6h GitHub Actions default.\n- Add -DautoUpdate\u003dfalse to the check step so it reads only the cache\n  populated by the preceding update-only step. Previously the check step\n  carried neither the mirror datafeed URL nor the API key, so on any cache\n  staleness/miss it synced directly against NIST - the unreliable path.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* ci(owasp): fall back to NVD mirror when the API update fails\n\nThe NIST NVD API is unreliable even with an API key (retries exhausted,\nsee dependency-check#8633). Previously the mirror datafeed was used only\nwhen no API key was present, so apache/struts always took the flaky API\npath and never the mirror.\n\nMake the API update step continue-on-error and run the mirror update as a\nfallback when the API step fails (or when no API key is configured).\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "9f030f62be9cd5a83c9d62cdad4e34a338c0a8be",
      "tree": "4bb72620fbb31bb5113c44339e9ebc6074b956fb",
      "parents": [
        "f32270cdb0d3fcbe1aafaa30608865afb9639d24"
      ],
      "author": {
        "name": "William Dutton",
        "email": "will.dutt@gmail.com",
        "time": "Wed Jul 22 17:46:14 2026 +1000"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 22 07:46:14 2026 +0000"
      },
      "message": "Library updates for cve\u0027s, suppression cleanup and not fail github action job SonarCloud if SONARCLOUD_TOKEN not found (summary report instead). (#1667)\n\n* OWASP + Github workflow updates\n\n* Library updates\n* Dependancy suppression cleanup\n\n* #1667 PR Review updates, use NIST_NVD_API_KEY when available else use mirror for forks not configured, remove workflow_call for now since we don\u0027t on call"
    },
    {
      "commit": "f32270cdb0d3fcbe1aafaa30608865afb9639d24",
      "tree": "d651e3453aa52c5ccc6aa7616e705f912e2b3801",
      "parents": [
        "f5880be2efe860c7475dcbf88184cfd48a2cc7b0"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Tue Jul 21 11:32:34 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jul 21 09:32:34 2026 +0000"
      },
      "message": "WW-5580 chore(core): use Configuration.VERSION_2_3_34 for FreeMarker config (#1798)\n\nAligns the FreeMarker incompatible_improvements setting with the\nFreeMarker 2.3.34 dependency already declared in the build.\n\nFreeMarker 2.3.34 declares VERSION_2_3_34 as an incompatible improvements\nbreak-point but does not gate any behaviour on it, so this is a no-op at\nruntime and purely keeps the setting in sync with the dependency.\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "f5880be2efe860c7475dcbf88184cfd48a2cc7b0",
      "tree": "0fe42fe063f54d8b1543acea1690baa831869143",
      "parents": [
        "6d271329262bd82db732ee3b4facd053b8407dc0"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Tue Jul 21 11:26:22 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jul 21 09:26:22 2026 +0000"
      },
      "message": "WW-5591 chore(core): mark XWorkObjectPropertyAccessor as deprecated (#1797)\n\nThe class is no longer used by the framework and can be removed in a\nfuture version.\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "6d271329262bd82db732ee3b4facd053b8407dc0",
      "tree": "84af889f2ea66b102f58abe55667424e07500df9",
      "parents": [
        "f2c1f50da201eeeafc498287499906e3e75792b3"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Tue Jul 21 10:59:38 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jul 21 10:59:38 2026 +0200"
      },
      "message": "WW-4858 Evaluate JSON name allowlist at leaf keys only (#1784)\n\n* WW-4858 fix(json): evaluate name allowlist at leaf keys only\n\nThe JSON population filter walked the object tree and applied every name\ncheck at every node before recursing. Accepted name patterns and the\nParameterNameAware callback target the full dotted binding path, so gating\nan intermediate node (e.g. \"bean\") against a leaf-specific rule dropped the\nentire subtree before the leaf (\"bean.stringField\") was ever evaluated —\ndiverging from ParametersInterceptor, which only evaluates complete leaf\nnames. For arrays it also meant the accepted allowlist judged the container\nname instead of the element path.\n\nSplit the per-key gate: length, excluded patterns, @StrutsParameter\nauthorization and property filters stay per-node (exclusion is prefix-safe\nand authorization is intentionally hierarchical); accepted patterns and\nParameterNameAware move to leaf keys only, including scalar array elements\nat their indexed path (\"items[0]\"). This reproduces the flat-path semantics\nexactly. Excluded/include-property behavior is unchanged.\n\nTests: nested-object leaf populates under a leaf-targeting accepted pattern\nand a ParameterNameAware action that rejects the intermediate node; accepted\npatterns now apply to the array element path; nested include-property\nfiltering still works.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-4858 fix(json): apply per-node checks to scalar array elements\n\nScalar list elements were gated only by the leaf name-allowlist and value\nchecks, skipping the per-node checks (length, excluded patterns,\n@StrutsParameter authorization, property filters). That left the JSON path\nmore permissive than ParametersInterceptor, which evaluates all of these\nagainst the full indexed name \"items[0]\".\n\nApply isAcceptableNode(elementPrefix, ...) to scalar list elements so an\nelement is gated exactly as the flat path gates \"items[0]\". Note this makes\nscalar-list @StrutsParameter authorization use the element path (depth 1,\nread method) rather than only the container (depth 0), matching the flat\npath.\n\nTests: excluded name pattern and @StrutsParameter authorization now apply at\nthe list element path.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-4858 refactor(json): extract keyTypeName helper to lower cognitive complexity\n\nMove the non-String-key logging ternary out of filterUnacceptableKeysRecursive\ninto a keyTypeName helper. Pure extraction, no behavior change; drops the\nmethod\u0027s cognitive complexity from 17 to 14, under Sonar\u0027s S3776 threshold.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "f2c1f50da201eeeafc498287499906e3e75792b3",
      "tree": "a07b6dd42137dcf3743b5a6440d0e8383977ecc6",
      "parents": [
        "464817e0b005132182354c3b1a47cba8ebf4ec9c"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Tue Jul 21 10:58:28 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jul 21 10:58:28 2026 +0200"
      },
      "message": "WW-5604 Recognize CDI/Weld client proxies in SecurityMemberAccess (#1796)\n\n* WW-5604 Add CdiProxyService to detect Weld client proxies\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5604 Register CdiProxyService as the active ProxyService\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5604 Address review: positive allowlist test, guard Weld member check, fix javadoc\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5604 Add WELD_AVAILABLE guard and weld-api version property\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5604 Cover null, non-proxy, non-method and Weld-absent paths\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5604 Remove unreachable guard and cover unwrap fallbacks\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "464817e0b005132182354c3b1a47cba8ebf4ec9c",
      "tree": "ea6b2d63f5f411126a7dfa601c45b14077356e01",
      "parents": [
        "b2548542ecbba516a2e4f79d9260ad359e442c1c"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Mon Jul 20 18:16:03 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 20 18:16:03 2026 +0200"
      },
      "message": "WW-5653 Upgrade Bootstrap to 5.3.x in sample apps (#1793)\n\n* WW-5653 docs: add Bootstrap 5.3.x sample-app migration design\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5653 docs: add Bootstrap 5 migration implementation plan\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5653 build: add Bootstrap 5, Bootstrap Icons, showcase jQuery webjars\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5653 feat(showcase): serve Bootstrap 5 and jQuery via webjars\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5653 fix(showcase): serve html5 demo Bootstrap CSS via webjar\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5653 feat(showcase): migrate navbar and top-level pages to Bootstrap 5\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5653 fix(showcase): migrate leftover Bootstrap 2 icon and fixed-navbar classes\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5653 feat(showcase): migrate tag-demo pages to Bootstrap 5\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5653 feat(showcase): migrate validation-demo pages to Bootstrap 5\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5653 feat(showcase): migrate fileupload and conversion pages to Bootstrap 5\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5653 feat(showcase): migrate wait, token and empmanager pages to Bootstrap 5\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5653 fix(showcase): remove BS3 carets and well class\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5653 fix(showcase): use ms-auto for right nav and add nav-link to Home\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5653 feat(rest-showcase): serve Bootstrap 5 CSS via webjars\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5653 feat(rest-showcase): migrate JSP markup to Bootstrap 5\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5653 fix(rest-showcase): migrate legacy BS2/BS3 grid classes to Bootstrap 5\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5653 fix(showcase): drop obsolete css/js excludePattern so webjar JS is served\n\nThe old struts.action.excludePattern whitelisted the vendored /styles/*.css\nand /js/*.js layout for container serving. After moving Bootstrap/jQuery to\nwebjars under /static/webjars/**, the \u0027.*/js/.*\\.js\u0027 entry matched the webjar\nJS path (e.g. bootstrap.bundle.min.js) and excluded it from Struts\u0027 static\nhandler, so it fell through to the container and 404\u0027d. Remaining webapp assets\n(prettify.js, main.css) are served via default-servlet fall-through.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5653 fix(samples): migrate remaining BS2/BS3 classes (tables, buttons, progress, forms, navbar)\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5653 chore(showcase): comment out verbose debug loggers\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5653 fix(showcase): migrate Bootstrap 5 markup in ftl/vm/html templates\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5653 test(showcase): disable JS in FreeMarkerManagerTest for Bootstrap 5\n\nHtmlUnit\u0027s JS engine cannot parse Bootstrap 5\u0027s ES6 (bootstrap.bundle.min.js\nuses \u0027class\u0027), and the decorator now serves it, so the default WebClient threw\non script error. The test only asserts server-rendered FreeMarker output, so\nJavaScript is disabled (matching Html5TagExampleTest).\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5653 fix(showcase): replace dead Bootstrap 2 form and alert classes\n\nMigrate new-person.ftl form off BS2 control-group/controls/form-actions to\nBootstrap 5 (mb-3, form-label, form-control), and replace the dead alert-error\nclass with alert-danger across the showcase pages.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5653 test(showcase): make integration tests tolerate Bootstrap 5 under HtmlUnit\n\nHtmlUnit 5.2.0 cannot parse Bootstrap 5\u0027s minified ES6 (bootstrap.bundle.min.js\nuses \u0027class\u0027), which broke the showcase HtmlUnit integration tests once the\ndecorator started serving the bundle.\n\n- Add ParameterUtils.createWebClient() which disables throwExceptionOnScriptError,\n  and route all integration tests through it (they assert server-rendered output,\n  not Bootstrap\u0027s client-side behaviour).\n- Load bootstrap.bundle.min.js with \u0027defer\u0027 so a page\u0027s own inline scripts (e.g.\n  the async chat demo) still execute before HtmlUnit hits the bundle\u0027s parse\n  error; defer is also the recommended real-browser loading strategy.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5653 test(showcase): poll for async chat result instead of fixed sleep\n\nAsyncTest relied on a fixed Thread.sleep(4000) for the server-push chat\nround-trip, which is not enough on slower/newer JVMs (reproduced failing on\nJDK 25). Poll the result element for up to ~30s via waitForBackgroundJavaScript\ninstead, making the test robust across JVMs.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5653 test(showcase): drive AsyncTest via HTTP instead of HtmlUnit\n\nThe browser-driven chat test was flaky on JDK 25 in CI: HtmlUnit\u0027s handling of\nthe async server-push long-poll timed out (message never rendered), even with\npolling. The test\u0027s purpose is to validate the Servlet 3 async endpoints, which\nneeds no browser or JavaScript. Rewrite it to POST /async/sendMessage and read\n/async/receiveNewMessages directly over HTTP and assert the JSON, making it\ndeterministic and independent of Bootstrap/HtmlUnit JS parsing.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5653 fix(showcase): drop progressbar role from wait progress bar\n\nResolves a SonarCloud accessibility finding (S6819) introduced by the Bootstrap 5\nmigration. The BS5 progress component is styled on .progress/.progress-bar divs;\nthe role/aria attributes were newly added (the BS3 original had none), so removing\nthem clears the finding while keeping the Bootstrap 5 styling.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5653 fix(showcase): correct BS5 alert wrapper and drop stray table tag\n\nAddress Copilot review on PR #1793:\n\n- decorators/main.jsp: the dismissible wrapper was a second .alert with no\n  variant while the variant class (alert-danger, etc.) stayed on the inner\n  \u003cul\u003e, producing a nested, uncoloured alert box. Move the alert* classes\n  onto the wrapper and strip them from the \u003cul\u003e so the wrapper is the single\n  alert container.\n- orders-edit.jsp: remove the stray, unclosed \u003ctable\u003e start tag before\n  \u003c/s:form\u003e (pre-existing invalid markup carried over during the migration).\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5653 fix(showcase): remove BS3 navbar-header wrapper and orphan dropdown-submenu\n\nClean up two leftover Bootstrap 3 artifacts in the showcase decorator navbar:\n\n- Drop the `navbar-header` wrapper (no BS5 CSS behind it) and make the brand\n  and toggler direct children of the `.container-fluid` flex container\n  (justify-content: space-between), with the brand first per BS5 convention.\n- Remove the empty, unclosed `\u003cli class\u003d\"dropdown-submenu\"\u003e` orphan before the\n  first item in the Examples menu; BS5 has no dropdown-submenu feature.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5653 fix(showcase): replace soft-deprecated navbar-light with data-bs-theme\n\nnavbar-light is soft-deprecated in Bootstrap 5.3. Switch the showcase navbar\nto the current data-bs-theme\u003d\"light\" idiom; bg-light is retained for the\nbackground.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5653 fix(showcase): normalize page-header replacement to border-bottom utilities\n\nA handful of showcase pages replaced the BS3 page-header with a bare \u003cdiv\u003e\nwhile the rest used \u003cdiv class\u003d\"border-bottom pb-2 mb-3\"\u003e. Normalize those 35\nheader wrappers to the same border-bottom pb-2 mb-3 utilities so all showcase\npage headers render consistently.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5653 fix(rest-showcase): add mb-3 to page-header replacement\n\nAlign rest-showcase order page headers with the showcase standard by using\nborder-bottom pb-2 mb-3 (was border-bottom pb-2), so header spacing is\nconsistent across both sample apps.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "b2548542ecbba516a2e4f79d9260ad359e442c1c",
      "tree": "e46203e712e7a88bf09a8e1609f8ec74cf63aba1",
      "parents": [
        "11c10ee8f9259240285369c6feaae1fe6c06fc0d"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Mon Jul 20 13:58:04 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 20 11:58:04 2026 +0000"
      },
      "message": "WW-5640 refactor(webjars): rename DefaultWebJarUrlProvider to StrutsWebJarUrlProvider (#1795)\n\nUse the Struts* prefix convention for the framework\u0027s default\nWebJarUrlProvider implementation instead of Default*.\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "11c10ee8f9259240285369c6feaae1fe6c06fc0d",
      "tree": "4a161b0fcfe62f553879b40b557f4eb6b9b5e036",
      "parents": [
        "60bc7b8d7ac3aca7ee7c7c3ffbb13e1d6d1113a0"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Mon Jul 20 13:41:45 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 20 13:41:45 2026 +0200"
      },
      "message": "WW-5620 Standardize logging on Log4j2 (#1794)\n\n* WW-5620 docs: add Log4j2 logging standardization design spec\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5620 docs: add Log4j2 logging standardization implementation plan\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5620 Migrate FinalizableReferenceQueue to Log4j2\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5620 Migrate AbstractDefaultToStringRenderable to Log4j2\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5620 Remove unused injectable j.u.l.Logger DI factory from ContainerBuilder\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5620 Remove dead first-party SLF4J dependency declarations\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "60bc7b8d7ac3aca7ee7c7c3ffbb13e1d6d1113a0",
      "tree": "5eae5f0de8263b3a3153202b351a0e89b699c4f0",
      "parents": [
        "963a81f43df9b17a80f0892ff441d328f7979681"
      ],
      "author": {
        "name": "Sri lakshmi kanthan P",
        "email": "srilakshmikanthanp@gmail.com",
        "time": "Mon Jul 20 13:00:52 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 20 09:30:52 2026 +0200"
      },
      "message": "WW-5652 Add support for Java records and Optional serialization in the JSON plugin. (#1792)\n\n* feat(json): support serialization of Java records in JSON processing\n\n* feat(json): add support for serializing Optional values in JSON processing"
    },
    {
      "commit": "963a81f43df9b17a80f0892ff441d328f7979681",
      "tree": "82edff8f5ef2bd52beeec104cc55d0405895a669",
      "parents": [
        "cc00343f1b4ae763f3403f5678a9fdebb4c73665"
      ],
      "author": {
        "name": "Arun",
        "email": "thearunmanni@gmail.com",
        "time": "Mon Jul 20 00:10:25 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Jul 19 20:40:25 2026 +0200"
      },
      "message": "WW-5647 Use ConcurrentHashMap for XSLT template cache (#1781)\n\n* Use ConcurrentHashMap for XSLT template cache and add double-check locking\n\n* Prevent noCache from polluting shared template cache; add dedup and noCache regression tests"
    },
    {
      "commit": "cc00343f1b4ae763f3403f5678a9fdebb4c73665",
      "tree": "6148f8298614070a766f6a4a1fa576d97993b392",
      "parents": [
        "db64c103d83b61f1833303d013c3d0d6219787d3"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Sun Jul 19 20:25:47 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Jul 19 20:25:47 2026 +0200"
      },
      "message": "WW-5650 Obtain a fresh JSON reader/writer per request in JSONInterceptor (#1782)\n\n* WW-5650 revert StrutsJSONReader to plain single-use instance fields\n\n* WW-5650 revert StrutsJSONWriter to plain single-use instance fields\n\n* WW-5650 obtain a fresh JSONUtil per request in JSONInterceptor\n\n* WW-5650 resolve JSONUtil lazily only on JSON request paths\n\nMove getJSONUtil() into the JSON and JSON-RPC branches of intercept() so\nrequests with a non-JSON content type no longer construct and discard an\nunused JSONUtil/reader/writer graph. Also trim a stray trailing blank line\nin StrutsJSONWriter.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5650 test(json): assert JSONWriter bean stays prototype-scoped\n\nGuards the response-side invariant from WW-5644: StrutsJSONWriter now uses\nplain instance fields and is not thread-safe, so cross-request safety relies\nsolely on the writer bean being prototype-scoped. Assert distinct instances\nper container lookup so a future switch to singleton scope fails the build.\n\nAddresses review feedback on #1782 without adding a getWriter() accessor\npurely for tests.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "db64c103d83b61f1833303d013c3d0d6219787d3",
      "tree": "ee36575d192378f7fb4be1d0c12cee57902b0fef",
      "parents": [
        "31036a8a4979dcbe1bc80ad0acf7e8e55120e58d"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Fri Jul 17 20:35:40 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Jul 17 20:35:40 2026 +0200"
      },
      "message": "build(deps-dev): bump org.webjars:jquery from 3.7.1 to 4.0.0 (#1789)\n\nBumps [org.webjars:jquery](https://github.com/jquery/jquery) from 3.7.1 to 4.0.0.\n- [Release notes](https://github.com/jquery/jquery/releases)\n- [Changelog](https://github.com/jquery/jquery/blob/main/changelog.md)\n- [Commits](https://github.com/jquery/jquery/compare/3.7.1...4.0.0)\n\n---\nupdated-dependencies:\n- dependency-name: org.webjars:jquery\n  dependency-version: 4.0.0\n  dependency-type: direct:development\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "31036a8a4979dcbe1bc80ad0acf7e8e55120e58d",
      "tree": "1f70b1b01456ffa4b331021d88c92c15ad9e1e42",
      "parents": [
        "6cb6abf4bad51cc2d293e9b20087e129645655c4"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Fri Jul 17 20:24:32 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Jul 17 20:24:32 2026 +0200"
      },
      "message": "build(deps): bump github/codeql-action from 4.36.3 to 4.37.0 (#1786)\n\nBumps [github/codeql-action](https://github.com/github/codeql-action) from 4.36.3 to 4.37.0.\n- [Release notes](https://github.com/github/codeql-action/releases)\n- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)\n- [Commits](https://github.com/github/codeql-action/compare/v4.36.3...v4.37.0)\n\n---\nupdated-dependencies:\n- dependency-name: github/codeql-action\n  dependency-version: 4.37.0\n  dependency-type: direct:production\n  update-type: version-update:semver-minor\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "6cb6abf4bad51cc2d293e9b20087e129645655c4",
      "tree": "25313717595c3ff51ed038baca86be874a9c4c8c",
      "parents": [
        "de599c91d3519d216c4e048fc207717881a6144c"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Fri Jul 17 20:22:29 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Jul 17 20:22:29 2026 +0200"
      },
      "message": "build(deps): bump com.fasterxml.jackson:jackson-bom (#1787)\n\nBumps [com.fasterxml.jackson:jackson-bom](https://github.com/FasterXML/jackson-bom) from 2.22.0 to 2.22.1.\n- [Commits](https://github.com/FasterXML/jackson-bom/compare/jackson-bom-2.22.0...jackson-bom-2.22.1)\n\n---\nupdated-dependencies:\n- dependency-name: com.fasterxml.jackson:jackson-bom\n  dependency-version: 2.22.1\n  dependency-type: direct:production\n  update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "de599c91d3519d216c4e048fc207717881a6144c",
      "tree": "49f913eff5db914eefb237d782530260e01b756b",
      "parents": [
        "e5eb01abdab0aefdaf5c2ce53704bd6b21203f39"
      ],
      "author": {
        "name": "Arun",
        "email": "thearunmanni@gmail.com",
        "time": "Fri Jul 17 15:48:08 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Jul 17 12:18:08 2026 +0200"
      },
      "message": "WW-5646 Modernize path normalization in Include component (#1780)\n\n* Modernize path normalization in Include component and handle edge cases\n\n* Add tests for edge case dot-dot handling in Include path normalization\n\n* Fix reversed path segment order in getContextRelativePath()\n\nThe for-each loop iterated the ArrayDeque head-to-tail (most\r\nrecently pushed first), which is the reverse of the old Stack\u0027s\r\ninsertion-order iteration. This caused rebuilt paths like\r\n\"car/view.jsp\" to come out as \"view.jsp/car\".\r\n\r\nUse descendingIterator() to restore the original oldest-first\r\nordering when rebuilding the flat path string."
    },
    {
      "commit": "e5eb01abdab0aefdaf5c2ce53704bd6b21203f39",
      "tree": "1c5474a7f786844a7a5344cd8266ba8ba4a9530d",
      "parents": [
        "cf22320e33ec8da5bdb831ed2ee579017adc1dda"
      ],
      "author": {
        "name": "ⳕⲛτⲉⲅⲥⲉⳏτⲟⲅ 🕵🏻",
        "email": "192411347+g0w6y@users.noreply.github.com",
        "time": "Fri Jul 17 15:21:47 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Jul 17 11:51:47 2026 +0200"
      },
      "message": "WW-5642 fix(rest): authorize @StrutsParameter on record/creator-bound REST body properties (#1774)\n\n* fix(rest): authorize @StrutsParameter on record/creator-bound REST body properties\n\nParameterAuthorizingModule enforces @StrutsParameter on REST/JSON body\ndeserialization by wrapping each property\u0027s deserializeAndSet/\ndeserializeSetAndReturn. Jackson never calls either method for\ncreator-bound properties (Java records, @JsonCreator constructors,\n@ConstructorProperties) — it calls SettableBeanProperty#deserialize\ndirectly, which is declared final and bypasses the wrapper entirely.\nWith struts.parameters.requireAnnotations enabled, any record-typed\nfield anywhere in a REST action\u0027s request body was populated with no\nauthorization check at all.\n\nAdd AuthorizingValueDeserializer, which wraps the property\u0027s value\ndeserializer instead of the property itself, and install it from\nAuthorizingSettableBeanProperty#withValueDeserializer — scoped to\nCreatorProperty so ordinary setter/field/builder properties, already\nauthorized via the existing wrapper, aren\u0027t checked twice.\n\n* fix(rest): treat redaction-induced construction failures as unauthorized, not fatal\n\nAuthorizingValueDeserializer substitutes null for a rejected creator-bound\nproperty (record component, @JsonCreator/@ConstructorProperties param).\nFor reference-typed, unvalidated components this is a harmless stand-in\nfor \"not set\" -- but two cases turn that substitution into an unhandled\nexception that crashes deserialization of the entire request body instead\nof just dropping the unauthorized subtree:\n\n- A record/constructor with its own non-null validation (e.g. a compact\n  constructor doing Objects.requireNonNull) throws\n  ValueInstantiationException when the redacted component reaches it.\n- With DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES enabled, Jackson\n  itself throws MismatchedInputException when a primitive-typed creator\n  component is redacted to null.\n\nAdd RedactionAwareDeserializer, wrapping every bean-type deserializer via\na new BeanDeserializerModifier#modifyDeserializer hook. It tracks (via a\nnew redaction-scope stack in ParameterAuthorizationContext) whether the\nobject currently under construction had a property redacted by\nauthorization; if construction then throws, the object is treated as\nunauthorized (returns null) instead of propagating the raw exception --\nmatching the same fail-closed outcome already used when a non-creator\nnested property is rejected outright. A guard test confirms genuine,\nunrelated validation failures (nothing redacted) still propagate\nnormally, so real client errors aren\u0027t masked.\n\nAlso verified (and added regression coverage for) the other gaps raised\nin review: static factory-method @JsonCreator, @ConstructorProperties,\ntop-level records, 3-level nested creator chains, and List/Map creator\nparams whose elements are further creator-bound or plain-POJO types --\nall of these were already handled correctly by the existing\nwithValueDeserializer interception.\n\n* test(rest): cover array creator param; document redaction edge cases\n\nAddresses the three non-blocking review notes on WW-5642:\n\n- Add testArrayOfRecordsAsCreatorParam_elementsAuthorizedByIndexedPath\n  and a WithArray fixture, exercising the type.isArray() branch of\n  AuthorizingValueDeserializer#prefixForNested so the collection matrix\n  (List/Map/array) is fully covered.\n- Document in AuthorizingValueDeserializer that redacting a primitive\n  creator component becomes the type default (0/false) when\n  FAIL_ON_NULL_FOR_PRIMITIVES is off -- a deliberate choice, the client\n  value never lands either way.\n- Document in RedactionAwareDeserializer that a redaction co-located with\n  an unrelated mapping error is folded into \"object dropped\" -- a\n  deliberate fail-closed trade-off, never exposing a partial object.\n\n---------\n\nCo-authored-by: g0w6y \u003cg0w6y@users.noreply.github.com\u003e"
    },
    {
      "commit": "cf22320e33ec8da5bdb831ed2ee579017adc1dda",
      "tree": "76c35f0f82052236c9579288aecdb49ec5cd067a",
      "parents": [
        "40fcae310178d5ac307722dd70394c6f5bdb9660"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Tue Jul 14 20:25:56 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jul 14 20:25:56 2026 +0200"
      },
      "message": "docs(security): treat a fix or PR as disclosure, require private report first (#1785)\n\nThe disclosure rules only forbade publishing exploit/PoC code, so a\ncontributor who opens a public PR that fixes or hints at a suspected\nvulnerability reads them as satisfied — the fix itself telegraphs the\nweakness before a fixed release exists.\n\nAdd a dedicated \"Do not disclose through a pull request, commit, or issue\"\nsection directing reporters to email security@struts.apache.org first, and\nextend the PoC rule in Report Quality Rules to state that a fix, patch, or\nhardening change is a public disclosure in the same way a PoC is. Aligns\nSECURITY.md with the rule already stated in CLAUDE.md/AGENTS.md.\n\n🤖 Generated by AI Assistant"
    },
    {
      "commit": "40fcae310178d5ac307722dd70394c6f5bdb9660",
      "tree": "a31bbb572b48083d7e96c54e5eeba04f4382575e",
      "parents": [
        "b70ecc8e15df9e5cb734be2d87cc3e24a99a78ef"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Tue Jul 14 13:31:06 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jul 14 13:31:06 2026 +0200"
      },
      "message": "WW-4858 test(json): cover nested-leaf accepted-name and include patterns (#1783)\n\nAdd two tests to JSONInterceptorTest exercising the nested-object path for\nthe name/value filtering added in WW-4858:\n\n- testAcceptedNamePatternRejectsNestedKey: accepted name patterns are raw\n  full-match regexes with no hierarchy expansion, so the intermediate node\n  (\"bean\") must itself match an accepted pattern or the whole subtree is\n  dropped before the leaf is visited.\n- testIncludePropertiesAppliedToNestedInputWhenEnabled: include patterns do\n  expand across the hierarchy, so \"bean.stringField\" also matches the\n  intermediate \"bean\" and the nested leaf populates while the excluded\n  sibling \"bean.intField\" is dropped.\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "b70ecc8e15df9e5cb734be2d87cc3e24a99a78ef",
      "tree": "4739e4fdd0b822b97f71d6798b8cc41aba9fdb5d",
      "parents": [
        "18955b98a4c05fd697711007988893916140c92d"
      ],
      "author": {
        "name": "Arun",
        "email": "thearunmanni@gmail.com",
        "time": "Tue Jul 14 15:07:21 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jul 14 09:37:21 2026 +0000"
      },
      "message": "WW-5645 Canonicalise static content paths and remove redundant URL decode (#1777)\n\n* Add path segment validation utility to StaticContentLoader\n\n* Remove redundant URL decode in buildPath and reject malformed path segments early\n\n* Use shared path segment validation in WebJar URL provider\n\n* Document encoding contract on RequestUtils.getServletPath\n\n* Add tests for path segment validation in static content loader\n\n* Add encoded traversal test for WebJar static content serving\n\n* Add encoded traversal tests for WebJar URL provider\n\n* Fix missing closing brace in StaticContentLoader causing compile failure\n\n* Use per-segment matching in containsMalformedPathSegment to avoid false positives\n\n* Remove redundant dot-segment check now handled by containsMalformedPathSegment\n\n* Fix indentation on validateStaticContentPath closing brace\n\n* Remove unused encoding field and setter from DefaultStaticContentLoader\n\n* Replace denylist with path canonicalisation in Validator\n\n* Wire canonicalisePath into static content serving\n\n* Wire canonicalisePath into WebJar URL provider\n\n* Update tests for canonicalise approach and remove unused setEncoding call\n\n* Remove setEncoding calls from tests to match updated DefaultStaticContentLoader\n\n* Remove setEncoding calls from tests to match updated DefaultStaticContentLoader\n\n* Remove redundant encoded-traversal tests per maintainer review — end-to-end 404 already covered"
    },
    {
      "commit": "18955b98a4c05fd697711007988893916140c92d",
      "tree": "38101dbb80aa81942876ee2935d25d1711f02d13",
      "parents": [
        "75a285a1065313e082d4ecf4e678211ee65331e0"
      ],
      "author": {
        "name": "ⳕⲛτⲉⲅⲥⲉⳏτⲟⲅ 🕵🏻",
        "email": "192411347+g0w6y@users.noreply.github.com",
        "time": "Tue Jul 14 12:19:07 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jul 14 08:49:07 2026 +0200"
      },
      "message": "WW-5644 fix(json): confine StrutsJSONWriter write state to the writing thread (#1776)\n\n* fix(json): confine StrutsJSONWriter write state to the writing thread\n\nJSONUtil obtains its JSONWriter once via @Inject and reuses that same\ninstance across every concurrent response handled by that JSONResult/\nJSONInterceptor configuration. StrutsJSONWriter kept its output buffer,\ncyclic-reference stack, root object, and expression-path state\n(buf/stack/root/buildExpr/exprStack/excludeProperties/\nincludeProperties/excludeNullProperties) as plain instance fields, all\nreset in place at the start of write().\n\nTwo concurrent write() calls on the same instance therefore race on\nthat reset: one call\u0027s in-progress buffer can be wiped and overwritten\nby a second, unrelated concurrent call before the first call reads it\nback via buf.toString(), so one request\u0027s serialized JSON can be\nreturned as a completely different, concurrently-served request\u0027s\nresponse body.\n\nMove buf/stack/root/buildExpr/exprStack/excludeProperties/\nincludeProperties/excludeNullProperties into a WriteState confined to\na ThreadLocal, scoped to a single write() call. Method signatures and\nbehavior are otherwise unchanged so existing StrutsJSONWriter\nsubclasses keep working; ignoreHierarchy/dateFormat/enumAsBean/\nexcludeProxyProperties stay as plain instance fields since they are\nset to the same value on every call for a given writer configuration\nand are safe to share.\n\n* test(json): raise writer concurrency test to 16 threads for reliable repro\n\nVerified independently that the 2-thread version can miss the race on\nmachines with more cores than contending threads (with no CPU\ncontention, the OS scheduler has no need to preempt either thread\nmid-call, so the corruption window is rarely hit): 0 reproductions in\n8 reruns against unpatched code on a 10-core machine. Sixteen threads\nreproduced it reliably (44,646/320,000 corrupted responses against\nunpatched StrutsJSONWriter), and confirmed zero corruption against the\nfix under the same load.\n\n---------\n\nCo-authored-by: g0w6y \u003cg0w6y@users.noreply.github.com\u003e"
    },
    {
      "commit": "75a285a1065313e082d4ecf4e678211ee65331e0",
      "tree": "167c34828137da4ac4287c15526ad7d06059ad2c",
      "parents": [
        "8fb8bcbcf0cbda94932fd4061597e29734b3fbf9"
      ],
      "author": {
        "name": "ⳕⲛτⲉⲅⲥⲉⳏτⲟⲅ 🕵🏻",
        "email": "192411347+g0w6y@users.noreply.github.com",
        "time": "Tue Jul 14 12:18:46 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jul 14 08:48:46 2026 +0200"
      },
      "message": "WW-5643 fix(json): confine StrutsJSONReader parse state to the parsing thread (#1775)\n\n* fix(json): confine StrutsJSONReader parse state to the parsing thread\n\nJSONInterceptor obtains its JSONReader once via @Inject and reuses that\nsame instance across every concurrent request handled by that\ninterceptor. StrutsJSONReader kept its parse cursor, token buffer and\nnesting-depth counter (used to enforce maxDepth/maxElements/\nmaxStringLength/maxKeyLength) as plain instance fields, so two\nconcurrent read() calls on the same instance tore each other\u0027s state:\none request\u0027s depth counter could be decremented by an unrelated\nconcurrent request finishing its own parse, letting payloads deeper\nthan the configured maxDepth through, and the shared character cursor\nand string/number buffer let fragments of one request\u0027s JSON body leak\ninto a different, concurrently-parsed request\u0027s result.\n\nMove the cursor, current character, token, buffer and depth into a\nParseState confined to a ThreadLocal, scoped to a single read() call.\nMethod signatures and behavior are otherwise unchanged so existing\nStrutsJSONReader subclasses keep working; the limit fields\n(maxElements/maxDepth/maxStringLength/maxKeyLength) stay as plain\ninstance fields since they are set to the same value on every call for\na given interceptor configuration and are safe to share.\n\n* test(json): raise reader concurrency test to 16 threads for reliable repro\n\nVerified independently that the 2-thread version can miss the race on\nmachines with more cores than contending threads (with no CPU\ncontention, the OS scheduler has no need to preempt either thread\nmid-call, so the corruption window is rarely hit): 0 reproductions in\n8 reruns against unpatched code on a 10-core machine. Sixteen threads\nreproduced both symptoms reliably against unpatched StrutsJSONReader\n(81 cross-thread data leaks and 79 maxDepth bypasses out of 160,000\nattempts), and confirmed zero of either against the fix under the\nsame load. Combined the two prior tests into one, since both symptoms\ncome from the same shared parse state and are naturally checked\ntogether per thread.\n\n---------\n\nCo-authored-by: g0w6y \u003cg0w6y@users.noreply.github.com\u003e"
    },
    {
      "commit": "8fb8bcbcf0cbda94932fd4061597e29734b3fbf9",
      "tree": "4781c1b71abdad29e7ffcfe616b8bc0d99f78167",
      "parents": [
        "f03198c6f828ee0fdbd64ee4cf30f45157384c5c"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Sun Jul 12 11:40:42 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Jul 12 11:40:42 2026 +0200"
      },
      "message": "build(deps): bump net.sf.jasperreports:jasperreports (#1779)\n\nBumps [net.sf.jasperreports:jasperreports](https://github.com/Jaspersoft/jasperreports) from 7.0.4 to 7.0.7.\n- [Release notes](https://github.com/Jaspersoft/jasperreports/releases)\n- [Changelog](https://github.com/Jaspersoft/jasperreports/blob/master/changes.txt)\n- [Commits](https://github.com/Jaspersoft/jasperreports/compare/7.0.4...7.0.7)\n\n---\nupdated-dependencies:\n- dependency-name: net.sf.jasperreports:jasperreports\n  dependency-version: 7.0.7\n  dependency-type: direct:production\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "f03198c6f828ee0fdbd64ee4cf30f45157384c5c",
      "tree": "a8a2f9b8ebae15fd15d335339a9b2f34162cf948",
      "parents": [
        "525c7dae3796e92910ac73bfe0891a02f2eecbe2"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Sun Jul 12 11:37:11 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Jul 12 11:37:11 2026 +0200"
      },
      "message": "build(deps-dev): bump commons-logging:commons-logging (#1760)\n\nBumps [commons-logging:commons-logging](https://github.com/apache/commons-logging) from 1.3.6 to 1.4.0.\n- [Changelog](https://github.com/apache/commons-logging/blob/master/RELEASE-NOTES.txt)\n- [Commits](https://github.com/apache/commons-logging/compare/rel/commons-logging-1.3.6...rel/commons-logging-1.4.0)\n\n---\nupdated-dependencies:\n- dependency-name: commons-logging:commons-logging\n  dependency-version: 1.4.0\n  dependency-type: direct:development\n  update-type: version-update:semver-minor\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "525c7dae3796e92910ac73bfe0891a02f2eecbe2",
      "tree": "543c94889197077fec36c2b237d9b150bacc1f0a",
      "parents": [
        "5c130f9411a5c8f5c8bbb60744e25ce6d31ddd9b"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Sun Jul 12 11:32:12 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Jul 12 11:32:12 2026 +0200"
      },
      "message": "WW-4858 Honor parameter filtering during JSON population (#1773)\n\n* WW-4858 docs(json): design for honoring parameter filtering during JSON population\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-4858 docs(json): implementation plan for JSON parameter filtering\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-4858 feat(json): enforce excluded/accepted name patterns on JSON population\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-4858 feat(json): enforce param-name max length on JSON population\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-4858 feat(json): honor ParameterNameAware and ParameterValueAware on JSON population\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-4858 feat(json): add opt-in excluded/accepted value patterns on JSON population\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-4858 feat(json): opt-in applying excludeProperties/includeProperties to JSON input\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-4858 test(json): cover nested and list-element paths; clarify filter comments\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "5c130f9411a5c8f5c8bbb60744e25ce6d31ddd9b",
      "tree": "2c44867c3e13126c52b9590cba6ca72ec8864367",
      "parents": [
        "08fba5980767612cddc69c54acbde856222fdcee"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Sun Jul 12 11:29:41 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Jul 12 11:29:41 2026 +0200"
      },
      "message": "WW-5641 Restore struts.json.writer / struts.json.reader override in JSON plugin (#1766)\n\n* WW-5641 docs: design spec for JSON writer/reader override regression\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5641 docs: implementation plan for JSON writer/reader override fix\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5641 fix: run JSON bean-selection from struts-deferred.xml\n\nThe JSON plugin declared \u003cbean-selection\u003e in struts-plugin.xml, which runs\nat plugin-parse time, before the application struts.xml is folded in. That\nfroze the JSONWriter/JSONReader default binding to StrutsJSONWriter/Reader,\nso struts.json.writer / struts.json.reader overrides were ignored.\n\nMove the element to struts-deferred.xml, which Dispatcher loads last (after\nthe app config and core\u0027s StrutsBeanSelectionProvider), so the alias honors\nthe override. Mirrors the velocity plugin. JSONUtil is unchanged from main.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "08fba5980767612cddc69c54acbde856222fdcee",
      "tree": "00fa45321f872a9b4332a93e05c68f2759de4d3e",
      "parents": [
        "30cbd1c3c494c631c3b15254755848b02bed7dcd"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Thu Jul 09 09:25:44 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jul 09 09:25:44 2026 +0200"
      },
      "message": "build(deps): bump org.apache.logging.log4j:log4j-bom (#1767)\n\nBumps [org.apache.logging.log4j:log4j-bom](https://github.com/apache/logging-log4j2) from 2.26.0 to 2.26.1.\n- [Release notes](https://github.com/apache/logging-log4j2/releases)\n- [Changelog](https://github.com/apache/logging-log4j2/blob/2.x/RELEASE-NOTES.adoc)\n- [Commits](https://github.com/apache/logging-log4j2/compare/rel/2.26.0...rel/2.26.1)\n\n---\nupdated-dependencies:\n- dependency-name: org.apache.logging.log4j:log4j-bom\n  dependency-version: 2.26.1\n  dependency-type: direct:production\n  update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "30cbd1c3c494c631c3b15254755848b02bed7dcd",
      "tree": "c6f30a8eedf0f6c12eab0d048da0d3715f34a13b",
      "parents": [
        "16aa43137d99ef86b2382b3af8e4cf667bd7d67d"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Thu Jul 09 09:25:31 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jul 09 09:25:31 2026 +0200"
      },
      "message": "build(deps): bump github/codeql-action/upload-sarif (#1768)\n\nBumps [github/codeql-action/upload-sarif](https://github.com/github/codeql-action) from 628fc3f124e68b0151f0d2a5d81e864ee1e42335 to f58f0d11ebf5dedd870fab2f999275f7602cfa46.\n- [Release notes](https://github.com/github/codeql-action/releases)\n- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)\n- [Commits](https://github.com/github/codeql-action/compare/628fc3f124e68b0151f0d2a5d81e864ee1e42335...f58f0d11ebf5dedd870fab2f999275f7602cfa46)\n\n---\nupdated-dependencies:\n- dependency-name: github/codeql-action/upload-sarif\n  dependency-version: f58f0d11ebf5dedd870fab2f999275f7602cfa46\n  dependency-type: direct:production\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "16aa43137d99ef86b2382b3af8e4cf667bd7d67d",
      "tree": "8c01b20e4123622497837e98c8354b35a8c64d83",
      "parents": [
        "745c08c7ac4b4a91dc7f964c4dd6c2ff5aab7936"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Thu Jul 09 09:25:18 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jul 09 09:25:18 2026 +0200"
      },
      "message": "build(deps): bump github/codeql-action from 4.36.2 to 4.36.3 (#1769)\n\nBumps [github/codeql-action](https://github.com/github/codeql-action) from 4.36.2 to 4.36.3.\n- [Release notes](https://github.com/github/codeql-action/releases)\n- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)\n- [Commits](https://github.com/github/codeql-action/compare/v4.36.2...v4.36.3)\n\n---\nupdated-dependencies:\n- dependency-name: github/codeql-action\n  dependency-version: 4.36.3\n  dependency-type: direct:production\n  update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "745c08c7ac4b4a91dc7f964c4dd6c2ff5aab7936",
      "tree": "ff882c52f4a88732751f7836d80e145ab1c92658",
      "parents": [
        "789dbf3cd253d528af230272805b3ce392005fb1"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Thu Jul 09 09:25:01 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jul 09 09:25:01 2026 +0200"
      },
      "message": "build(deps-dev): bump byte-buddy.version from 1.18.10 to 1.18.11 (#1771)\n\nBumps `byte-buddy.version` from 1.18.10 to 1.18.11.\n\nUpdates `net.bytebuddy:byte-buddy` from 1.18.10 to 1.18.11\n- [Release notes](https://github.com/raphw/byte-buddy/releases)\n- [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md)\n- [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.18.10...byte-buddy-1.18.11)\n\nUpdates `net.bytebuddy:byte-buddy-agent` from 1.18.10 to 1.18.11\n- [Release notes](https://github.com/raphw/byte-buddy/releases)\n- [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md)\n- [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.18.10...byte-buddy-1.18.11)\n\n---\nupdated-dependencies:\n- dependency-name: net.bytebuddy:byte-buddy\n  dependency-version: 1.18.11\n  dependency-type: direct:development\n  update-type: version-update:semver-patch\n- dependency-name: net.bytebuddy:byte-buddy-agent\n  dependency-version: 1.18.11\n  dependency-type: direct:development\n  update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "789dbf3cd253d528af230272805b3ce392005fb1",
      "tree": "2ad9f3508e59f039506c25d1c2d1e13cc4f7155c",
      "parents": [
        "a09f2a93aff910e93ebc9b83df89b3dd1ef08963"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Mon Jul 06 15:03:05 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 06 15:03:05 2026 +0200"
      },
      "message": "WW-5640 Add WebJars support to Struts core (#1765)\n\n* WW-5640 docs: design for WebJars support in Struts core\n\nAdds first-class WebJars support so client-side libraries can be\nreferenced by a version-less logical path and served through the\nexisting static-content pipeline. Grounded against 7.2.x source.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5640 docs: implementation plan for WebJars support\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5640 build: add webjars-locator-lite dependency\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5640 feat: add webjars config constants and defaults\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5640 docs: correct plan test framework to JUnit 4\n\ncore uses JUnit 4 + AssertJ + Mockito, not JUnit 5 Jupiter (no\nJupiter engine on the classpath). Test tasks translate accordingly.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5640 feat: add WebJarUrlProvider resolution seam\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5640 feat: register WebJarUrlProvider bean\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5640 feat: extend static content-type map for webjar assets\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5640 feat: serve webjar assets via static content loader\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5640 feat: add \u003cs:webjar\u003e tag and \u003c@s.webjar\u003e macro\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5640 docs: add generated tag reference for \u003cs:webjar\u003e\n\nAnnotation-processor-generated tag reference (attributes + description),\ntracked like every other tag\u0027s docs under core/src/site/resources/tags/.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5640 fix: address final review (log level, resolveUrl traversal test, javadoc)\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5640 refactor: address SonarCloud code smells\n\n- getContentType: replace long if/else chain with a static extension-\u003e\n  MIME map (S3776 cognitive complexity)\n- DefaultWebJarUrlProvider.split: return Optional\u003cString[]\u003e instead of a\n  null sentinel (S1168; Optional fits the reject semantics, empty-array\n  would not)\n- serving tests: rename local \u0027loader\u0027 -\u003e \u0027webJarLoader\u0027 to stop hiding\n  the ContentTypeProbe field (S1117)\n- WebJarTest: use assertThat(writer).hasToString(...) (S5838)\n\nS110 (WebJarTag inheritance depth) is inherent to the Struts tag base\nclass hierarchy shared by every tag; left as-is.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "a09f2a93aff910e93ebc9b83df89b3dd1ef08963",
      "tree": "afb973b9d7b1d079a46cb7362a5d9fe6f816267a",
      "parents": [
        "29f75f2dc04ae5a06b961e8291711bb24172555e"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Wed Jul 01 12:57:29 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 01 12:57:29 2026 +0200"
      },
      "message": "build(deps): bump github/codeql-action/upload-sarif (#1758)\n\nBumps [github/codeql-action/upload-sarif](https://github.com/github/codeql-action) from eda5730a8bfb740e03a28087a958444c646e5842 to 628fc3f124e68b0151f0d2a5d81e864ee1e42335.\n- [Release notes](https://github.com/github/codeql-action/releases)\n- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)\n- [Commits](https://github.com/github/codeql-action/compare/eda5730a8bfb740e03a28087a958444c646e5842...628fc3f124e68b0151f0d2a5d81e864ee1e42335)\n\n---\nupdated-dependencies:\n- dependency-name: github/codeql-action/upload-sarif\n  dependency-version: 628fc3f124e68b0151f0d2a5d81e864ee1e42335\n  dependency-type: direct:production\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "29f75f2dc04ae5a06b961e8291711bb24172555e",
      "tree": "215e7f48e7ce2fd975ee63ee179fd89a65ca0569",
      "parents": [
        "dc90e489099c47b6d5367302e0344dc878d3a352"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Wed Jul 01 12:57:04 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jul 01 12:57:04 2026 +0200"
      },
      "message": "build(deps): bump org.htmlunit:htmlunit from 5.1.0 to 5.2.0 (#1762)\n\nBumps [org.htmlunit:htmlunit](https://github.com/HtmlUnit/htmlunit) from 5.1.0 to 5.2.0.\n- [Release notes](https://github.com/HtmlUnit/htmlunit/releases)\n- [Commits](https://github.com/HtmlUnit/htmlunit/compare/5.1.0...5.2.0)\n\n---\nupdated-dependencies:\n- dependency-name: org.htmlunit:htmlunit\n  dependency-version: 5.2.0\n  dependency-type: direct:production\n  update-type: version-update:semver-minor\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "dc90e489099c47b6d5367302e0344dc878d3a352",
      "tree": "ed6ce4d131be04c53e52460be373b2fd62d5a5fe",
      "parents": [
        "50c16e93b12ba68479bdf35b6ec8941efd1798e0"
      ],
      "author": {
        "name": "Jarek Potiuk",
        "email": "jarek@potiuk.com",
        "time": "Mon Jun 29 21:10:42 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 29 21:10:42 2026 +0200"
      },
      "message": "Add draft security threat model (THREAT_MODEL.md) + discoverability pointer (#1756)\n\n* Add draft security threat model (THREAT_MODEL.md)\n\nGenerated-by: Claude Opus 4.8 (1M context)\n\n* SECURITY.md: link to THREAT_MODEL.md for scanner/triager discoverability\n\nGenerated-by: Claude Opus 4.8 (1M context)\n\n* Fix dangling §14 refs and tighten provenance in threat model\n\nAddress code-review findings on the THREAT_MODEL.md draft:\n\n- Add the missing §14 Q-env and Q-egress open questions, so every\n  *(inferred)* claim that cites them now resolves (restores the\n  \"each inferred claim has a matching §14 question\" invariant).\n- Tag the two previously bare *(inferred)* claims (examples/showcase,\n  on-path attacker) with their matching question IDs.\n- Soften the §1 header: drop the ASF Security team / PMC authorship\n  attribution on an unratified draft; state it is drafted for PMC review.\n- Correct the OGNL Java Security Manager wording: SecurityManager is\n  deprecated for removal since JDK 17 and permanently disabled in JDK 24,\n  not simply \"does not work on JDK 21+\".\n- AGENTS.md: point the Assess step at THREAT_MODEL.md\u0027s disposition guide\n  so the triage wrapper links the model directly.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Lukasz Lenart \u003clukaszlenart@apache.org\u003e\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "50c16e93b12ba68479bdf35b6ec8941efd1798e0",
      "tree": "8b35028501b0bd4337db84c88aad63ea9c78a39f",
      "parents": [
        "07afccbb2f563e464d7fecd24848d008c4fbfcf7"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Fri Jun 26 07:18:26 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Jun 26 07:18:26 2026 +0200"
      },
      "message": "Release 7.2.1-RC1 (#1744)\n\n* [maven-release-plugin] prepare release STRUTS_7_2_1\n\n* [maven-release-plugin] prepare for next development iteration"
    },
    {
      "commit": "07afccbb2f563e464d7fecd24848d008c4fbfcf7",
      "tree": "0dedfa3cfa1cd15cded43747fd4b9db74e3d14ea",
      "parents": [
        "153e42bb9b258c878c24c6b91eb77cf61c69ca67"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Fri Jun 26 07:18:09 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Jun 26 07:18:09 2026 +0200"
      },
      "message": "build(deps): bump actions/checkout from 6 to 7 (#1755)\n\nBumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7.\n- [Release notes](https://github.com/actions/checkout/releases)\n- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)\n- [Commits](https://github.com/actions/checkout/compare/v6...v7)\n\n---\nupdated-dependencies:\n- dependency-name: actions/checkout\n  dependency-version: \u00277\u0027\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "153e42bb9b258c878c24c6b91eb77cf61c69ca67",
      "tree": "cb01f41aa993e29be3d45d0ecf492419ebd58fc9",
      "parents": [
        "46ba90f797cffc804c979bc50e23524da53f6e8d"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Fri Jun 26 07:17:43 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Jun 26 07:17:43 2026 +0200"
      },
      "message": "build(deps): bump org.glassfish.jaxb:jaxb-bom from 4.0.8 to 4.0.9 (#1753)\n\nBumps org.glassfish.jaxb:jaxb-bom from 4.0.8 to 4.0.9.\n\n---\nupdated-dependencies:\n- dependency-name: org.glassfish.jaxb:jaxb-bom\n  dependency-version: 4.0.9\n  dependency-type: direct:production\n  update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "46ba90f797cffc804c979bc50e23524da53f6e8d",
      "tree": "b495edc397dce538bb5539b80e0eaaa582c64f0a",
      "parents": [
        "22243e75f53e78df7f593ee135b7c661c6a7c2ff"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Fri Jun 26 07:17:30 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Jun 26 07:17:30 2026 +0200"
      },
      "message": "build(deps-dev): bump org.jacoco:jacoco-maven-plugin (#1751)\n\nBumps [org.jacoco:jacoco-maven-plugin](https://github.com/jacoco/jacoco) from 0.8.14 to 0.8.15.\n- [Release notes](https://github.com/jacoco/jacoco/releases)\n- [Commits](https://github.com/jacoco/jacoco/compare/v0.8.14...v0.8.15)\n\n---\nupdated-dependencies:\n- dependency-name: org.jacoco:jacoco-maven-plugin\n  dependency-version: 0.8.15\n  dependency-type: direct:development\n  update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "22243e75f53e78df7f593ee135b7c661c6a7c2ff",
      "tree": "ed7d89496f318482fe7e8ee37fcb52a9474907c3",
      "parents": [
        "1e3fa9a2d7c64a3763058990015c8c020773eeb9"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Fri Jun 26 07:17:04 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Jun 26 07:17:04 2026 +0200"
      },
      "message": "build(deps): bump org.sitemesh:sitemesh from 3.2.2 to 3.2.3 (#1749)\n\nBumps [org.sitemesh:sitemesh](https://github.com/sitemesh/sitemesh3) from 3.2.2 to 3.2.3.\n- [Release notes](https://github.com/sitemesh/sitemesh3/releases)\n- [Commits](https://github.com/sitemesh/sitemesh3/compare/3.2.2...3.2.3)\n\n---\nupdated-dependencies:\n- dependency-name: org.sitemesh:sitemesh\n  dependency-version: 3.2.3\n  dependency-type: direct:production\n  update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "1e3fa9a2d7c64a3763058990015c8c020773eeb9",
      "tree": "ec0f318a25149bc93ff2ccc18129eaf660768427",
      "parents": [
        "01a561d9d8b9ed63fdd2a9935065abff076ec617"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Fri Jun 26 07:16:26 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Jun 26 07:16:26 2026 +0200"
      },
      "message": "build(deps-dev): bump byte-buddy.version from 1.18.8 to 1.18.10 (#1745)\n\nBumps `byte-buddy.version` from 1.18.8 to 1.18.10.\n\nUpdates `net.bytebuddy:byte-buddy` from 1.18.8 to 1.18.10\n- [Release notes](https://github.com/raphw/byte-buddy/releases)\n- [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md)\n- [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.18.8...byte-buddy-1.18.10)\n\nUpdates `net.bytebuddy:byte-buddy-agent` from 1.18.8 to 1.18.10\n- [Release notes](https://github.com/raphw/byte-buddy/releases)\n- [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md)\n- [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.18.8...byte-buddy-1.18.10)\n\n---\nupdated-dependencies:\n- dependency-name: net.bytebuddy:byte-buddy\n  dependency-version: 1.18.10\n  dependency-type: direct:development\n  update-type: version-update:semver-patch\n- dependency-name: net.bytebuddy:byte-buddy-agent\n  dependency-version: 1.18.10\n  dependency-type: direct:development\n  update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "01a561d9d8b9ed63fdd2a9935065abff076ec617",
      "tree": "af74a5a5a0c8755e87bb12123964bff5409839d4",
      "parents": [
        "59e342488d9ee6c67861eef1753aeffb09125939"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Thu Jun 25 21:43:45 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Jun 25 19:43:45 2026 +0000"
      },
      "message": "Add CONTRIBUTING.md for new contributors (#1757)"
    },
    {
      "commit": "59e342488d9ee6c67861eef1753aeffb09125939",
      "tree": "054880c97c1389064fb2b2281d733e904e1f8a99",
      "parents": [
        "489445c78d90b1bbe4e3915ac1caac829ee8ddab"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Mon Jun 15 12:22:51 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 15 12:22:51 2026 +0200"
      },
      "message": "WW-5256 Decouple FreeMarker whitespace stripping from devMode (#1743)\n\n* WW-5256 docs: design to decouple FreeMarker whitespace stripping from devMode\n\nFixes s:textarea rendering blank lines and HTML whitespace bloat in devMode\nby honoring struts.freemarker.whitespaceStripping unconditionally.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5256 docs: implementation plan to decouple whitespace stripping from devMode\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5256 test: prove whitespace stripping wrongly disabled in devMode\n\n* WW-5256 fix(freemarker): honor whitespaceStripping regardless of devMode\n\n* WW-5256 docs: drop devMode note from whitespaceStripping constant\n\n---------\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "489445c78d90b1bbe4e3915ac1caac829ee8ddab",
      "tree": "0814b498882a1a9ec7a6c7c1be78b9c718b712d1",
      "parents": [
        "cc3ebc3c1f4a1ca36c02cadaa483524d6b813362"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Mon Jun 15 12:07:02 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 15 12:07:02 2026 +0200"
      },
      "message": "Release 7.2.0-RC1 (#1742)\n\n* [maven-release-plugin] prepare release STRUTS_7_2_0\n\n* [maven-release-plugin] prepare for next development iteration"
    },
    {
      "commit": "cc3ebc3c1f4a1ca36c02cadaa483524d6b813362",
      "tree": "74064b19319ac9f85e818a487167a188eb99d8c0",
      "parents": [
        "8f9b4b8a90fec8df59b037599e57a3f65680c9ef"
      ],
      "author": {
        "name": "Arun",
        "email": "thearunmanni@gmail.com",
        "time": "Sun Jun 14 22:38:39 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Jun 14 17:08:39 2026 +0000"
      },
      "message": "WW-5635 Avoid logging sensitive token values in TokenHelper (#1738)\n\n* Avoid logging sensitive token values in TokenHelper\n\nRedact form and session token values from WARN-level log output\r\nin TokenHelper.validToken() and update corresponding i18n message\r\nproperties. Detailed diagnostics moved to DEBUG level with\r\nsanitized input.\n\n* Update struts-messages.properties\n\n* Update invalid token error message for clarity\n\n* Update struts-messages_da.properties\n\n* Update struts-messages_de.properties\n\n* Update Polish translation for invalid token message\n\n* Update invalid token message in Portuguese properties\n\n* Improve token mismatch warning logging\n\nUpdated warning message to include the form token in the log.\n\n* Update struts-messages.properties\n\n* Update invalid token message format in properties file\n\n* Update invalid token message for clarity\n\n* Update struts-messages_de.properties\n\n* Update struts-messages_pl.properties\n\n* Update invalid token message format in properties file\n\n* Update TokenHelper.java\n\n* Refactor token mismatch logging for development mode"
    },
    {
      "commit": "8f9b4b8a90fec8df59b037599e57a3f65680c9ef",
      "tree": "0bf428504b4a02645b531bd7ef4f7b4711fbb142",
      "parents": [
        "dd830dca804082dc7c8c18dce8c9db397bbbc94b"
      ],
      "author": {
        "name": "Arun",
        "email": "thearunmanni@gmail.com",
        "time": "Sun Jun 14 22:14:43 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Jun 14 18:44:43 2026 +0200"
      },
      "message": "WW-5636 Harden redirect URL escaping in non-302 response body (#1737)\n\n* Implement test for status code 200 with HTML escaping\n\n* Escape HTML in ServletRedirectResult response\n\nEscape HTML in the final location before writing to the response."
    },
    {
      "commit": "dd830dca804082dc7c8c18dce8c9db397bbbc94b",
      "tree": "5d5579e7ca936e683f298d020d50510401ff1cdc",
      "parents": [
        "289bcaa922c4c6721ae37ef59db38846610b8984"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Sun Jun 14 11:16:39 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Jun 14 09:16:39 2026 +0000"
      },
      "message": "WW-5630 test: streamline ConfigParseUtilTest and convert to JUnit 4 (#1740)\n\nCollapse 12 overlapping cache tests to 5 focused ones, replace the\n~80-entry JDK class-name literal with a synthetic-name loop bounded by\nthe inner-cache limit, and drop reflection from the behavioral tests\n(load-count assertions only). Reflection is retained solely in the two\nsize-bound tests, where Caffeine exposes no public seam.\n\nProduction ConfigParseUtil caching logic is unchanged.\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "289bcaa922c4c6721ae37ef59db38846610b8984",
      "tree": "9ac33d9e0921e58a996e7f8f50fac1bf56ab3d37",
      "parents": [
        "b808dcb8457f1b16f43a83fef25486f1673612b5"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Sat Jun 13 08:34:36 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Jun 13 08:34:36 2026 +0200"
      },
      "message": "build(deps): bump org.htmlunit:htmlunit from 4.21.0 to 5.1.0 (#1733)\n\nBumps [org.htmlunit:htmlunit](https://github.com/HtmlUnit/htmlunit) from 4.21.0 to 5.1.0.\n- [Release notes](https://github.com/HtmlUnit/htmlunit/releases)\n- [Commits](https://github.com/HtmlUnit/htmlunit/compare/4.21.0...5.1.0)\n\n---\nupdated-dependencies:\n- dependency-name: org.htmlunit:htmlunit\n  dependency-version: 5.1.0\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "b808dcb8457f1b16f43a83fef25486f1673612b5",
      "tree": "bb9e4061ab473c9795df0f4a7303b0a153099874",
      "parents": [
        "4f3fd69aa60b7487c83bc478f1aa93bf04985c58"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Sat Jun 13 08:33:28 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Jun 13 08:33:28 2026 +0200"
      },
      "message": "build(deps): bump com.fasterxml.jackson:jackson-bom (#1729)\n\nBumps [com.fasterxml.jackson:jackson-bom](https://github.com/FasterXML/jackson-bom) from 2.21.3 to 2.22.0.\n- [Commits](https://github.com/FasterXML/jackson-bom/compare/jackson-bom-2.21.3...jackson-bom-2.22.0)\n\n---\nupdated-dependencies:\n- dependency-name: com.fasterxml.jackson:jackson-bom\n  dependency-version: 2.22.0\n  dependency-type: direct:production\n  update-type: version-update:semver-minor\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "4f3fd69aa60b7487c83bc478f1aa93bf04985c58",
      "tree": "0c0841a24d51ec7b5a9ac541d6f4e3b279cf4836",
      "parents": [
        "210dc86b8840e55ac02c2fe88a56a48b0c898a62"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Sat Jun 13 08:28:47 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Jun 13 08:28:47 2026 +0200"
      },
      "message": "WW-5632 Harden commons-fileupload2 dependency against milestone binary-incompatibility (#1735)\n\n* WW-5632 docs: add commons-fileupload2 milestone-hardening design spec\n\nDesign for hardening the commons-fileupload2 dependency against\nmilestone binary-incompatibility (manage -core, activate a scoped\nenforcer rule, add a runtime API guard in AbstractMultiPartRequest).\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5632 docs: add implementation plan for fileupload2 milestone hardening\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5632 build(deps): manage commons-fileupload2-core alongside jakarta-servlet6\n\nPin both commons-fileupload2 artifacts to a single\ncommons-fileupload2.version property so the volatile -core API can no\nlonger skew from -jakarta-servlet6 in the reactor.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5632 build: enforce a single commons-fileupload2 version\n\nActivate maven-enforcer-plugin (previously dormant in pluginManagement)\nwith a fileupload-scoped bannedDependencies rule so any divergent\ncommons-fileupload2 version fails the build early.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5632 fix(fileupload): fail fast on incompatible commons-fileupload2 API\n\nVerify once per JVM that the fileupload size-limit setters exist and\nthrow a clear StrutsException reporting the core/jakarta version skew,\nreplacing an opaque deep-stack NoSuchMethodError in downstream runtimes.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n* WW-5632 fix(fileupload): make API-verification guard static\n\nResolve Sonar java:S2696 (instance method writing a static field) by\nmaking ensureFileUploadApiVerified() static; verification is JVM-global.\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "210dc86b8840e55ac02c2fe88a56a48b0c898a62",
      "tree": "b6936212de9ddf9838b43dff2144941d9abaea44",
      "parents": [
        "7c6034c66707ad3751b9710cca6b3b09a76863fc"
      ],
      "author": {
        "name": "brianandle",
        "email": "106392082+brianandle@users.noreply.github.com",
        "time": "Fri Jun 12 10:12:26 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Jun 12 17:12:26 2026 +0000"
      },
      "message": "WW-5630 - Performance Issue SecurityMemberAccess (#1721)\n\n* WW-5630 - Performance Issue SecurityMemberAccess\n* Add size bound cache, 50, for Class lookup\n* Add unit test\n\nCode generated by Copilot\n\n* WW-5630 - Add additional UT\n\n* WW-5630 - Add UT for non-existent class\n\n* WW-5630 - Review feedback changes\n* Cache ClassLoader directly\n* Use weakKeys and weakValues\n* Comment on the ClassLookupException\n* Additional Unit Tests\n\nAssistance in coding using co-pilot\n\n* WW-5630 - Additional review\n* Limit outer, Classloader, to 25. Ensure memory bounding.\n* Limit inner, Classes, to 50. Ensure memory bounding.\n* Additional UTs\n\nWith co-pilot assitance"
    },
    {
      "commit": "7c6034c66707ad3751b9710cca6b3b09a76863fc",
      "tree": "17f49410551d12187543a8e41e782437d9b8df44",
      "parents": [
        "5e725a72cdc02337afa58f3331aee67f715e5a96"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Wed Jun 10 13:23:50 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jun 10 13:23:50 2026 +0200"
      },
      "message": "build(deps-dev): bump org.apache.maven.plugins:maven-failsafe-plugin (#1732)\n\nBumps [org.apache.maven.plugins:maven-failsafe-plugin](https://github.com/apache/maven-surefire) from 3.5.5 to 3.5.6.\n- [Release notes](https://github.com/apache/maven-surefire/releases)\n- [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.5.5...surefire-3.5.6)\n\n---\nupdated-dependencies:\n- dependency-name: org.apache.maven.plugins:maven-failsafe-plugin\n  dependency-version: 3.5.6\n  dependency-type: direct:development\n  update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "5e725a72cdc02337afa58f3331aee67f715e5a96",
      "tree": "2fe56dc4e315e13951c0a6472093165f0be2dfa7",
      "parents": [
        "53ef1ad88311508c3162d74183a53052834fc9d6"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Wed Jun 10 13:23:21 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jun 10 13:23:21 2026 +0200"
      },
      "message": "build(deps): bump github/codeql-action from 4.36.0 to 4.36.2 (#1734)\n\nBumps [github/codeql-action](https://github.com/github/codeql-action) from 4.36.0 to 4.36.2.\n- [Release notes](https://github.com/github/codeql-action/releases)\n- [Commits](https://github.com/github/codeql-action/compare/v4.36.0...v4.36.2)\n\n---\nupdated-dependencies:\n- dependency-name: github/codeql-action\n  dependency-version: 4.36.2\n  dependency-type: direct:production\n  update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "53ef1ad88311508c3162d74183a53052834fc9d6",
      "tree": "a1245ac0cff7d318c0026e377ea80061ef80db63",
      "parents": [
        "0972a608a3b09c985bb5112dcf235d0bb9b98fa8"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Wed Jun 10 13:22:50 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jun 10 13:22:50 2026 +0200"
      },
      "message": "build(deps): bump org.apache.maven.plugins:maven-dependency-plugin (#1730)\n\nBumps [org.apache.maven.plugins:maven-dependency-plugin](https://github.com/apache/maven-dependency-plugin) from 3.10.0 to 3.11.0.\n- [Release notes](https://github.com/apache/maven-dependency-plugin/releases)\n- [Commits](https://github.com/apache/maven-dependency-plugin/compare/maven-dependency-plugin-3.10.0...maven-dependency-plugin-3.11.0)\n\n---\nupdated-dependencies:\n- dependency-name: org.apache.maven.plugins:maven-dependency-plugin\n  dependency-version: 3.11.0\n  dependency-type: direct:production\n  update-type: version-update:semver-minor\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "0972a608a3b09c985bb5112dcf235d0bb9b98fa8",
      "tree": "f19d78540f58088f00882e4316c6922ac78fc076",
      "parents": [
        "9011c32b38598181c5535580e3084932a59d2472"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Wed Jun 10 13:22:32 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jun 10 13:22:32 2026 +0200"
      },
      "message": "build(deps): bump maven-surefire-plugin.version from 3.5.5 to 3.5.6 (#1731)\n\nBumps `maven-surefire-plugin.version` from 3.5.5 to 3.5.6.\n\nUpdates `org.apache.maven.surefire:surefire-junit47` from 3.5.5 to 3.5.6\n\nUpdates `org.apache.maven.plugins:maven-surefire-plugin` from 3.5.5 to 3.5.6\n- [Release notes](https://github.com/apache/maven-surefire/releases)\n- [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.5.5...surefire-3.5.6)\n\n---\nupdated-dependencies:\n- dependency-name: org.apache.maven.surefire:surefire-junit47\n  dependency-version: 3.5.6\n  dependency-type: direct:production\n  update-type: version-update:semver-patch\n- dependency-name: org.apache.maven.plugins:maven-surefire-plugin\n  dependency-version: 3.5.6\n  dependency-type: direct:production\n  update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "9011c32b38598181c5535580e3084932a59d2472",
      "tree": "a0227ae615dca653a11965a12f6747245a1ea4b8",
      "parents": [
        "69cb2964f03ebe46b26deda0763145aa1e5ddd52"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Wed Jun 10 13:19:43 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jun 10 13:19:43 2026 +0200"
      },
      "message": "WW-5631 Add opt-in @StrutsParameter enforcement to ChainingInterceptor (#1719)\n\n* WW-5631 feat(chaining): add struts.chaining.requireAnnotations constant\n\nCo-Authored-By: Claude Opus 4.7 \u003cnoreply@anthropic.com\u003e\n\n* WW-5631 feat(chaining): default struts.chaining.requireAnnotations\u003dfalse\n\nCo-Authored-By: Claude Opus 4.7 \u003cnoreply@anthropic.com\u003e\n\n* WW-5631 test(chaining): add annotated/unannotated chaining fixtures\n\nCo-Authored-By: Claude Opus 4.7 \u003cnoreply@anthropic.com\u003e\n\n* WW-5631 test(chaining): add failing @StrutsParameter enforcement tests\n\nCo-Authored-By: Claude Opus 4.7 \u003cnoreply@anthropic.com\u003e\n\n* WW-5631 feat(chaining): enforce @StrutsParameter on target when opted in\n\nCo-Authored-By: Claude Opus 4.7 \u003cnoreply@anthropic.com\u003e\n\n* WW-5631 refactor(chaining): align requireAnnotations parsing with BooleanUtils\n\nUse BooleanUtils.toBoolean for the chaining requireAnnotations flag so it\naccepts the same values (yes/on/1) as the sibling\nstruts.parameters.requireAnnotations switch, and unify the enforcement WARN\nmessage prefix.\n\nCo-Authored-By: Claude Opus 4.7 \u003cnoreply@anthropic.com\u003e\n\n* WW-5631 test(chaining): cover includes interaction and proxied target\n\nCo-Authored-By: Claude Opus 4.7 \u003cnoreply@anthropic.com\u003e\n\n* WW-5631 docs(chaining): document struts.chaining.requireAnnotations\n\nCo-Authored-By: Claude Opus 4.7 \u003cnoreply@anthropic.com\u003e\n\n* WW-5631 test(chaining): cover fail-closed introspection; clarify target\u003d\u003daction\n\nAdd a test asserting nothing is copied when the target action cannot be\nintrospected (fail-closed), and document why isAuthorized is called with\ntarget \u003d\u003d action for chaining (no ModelDriven exemption).\n\nCo-Authored-By: Claude Opus 4.7 \u003cnoreply@anthropic.com\u003e\n\n* WW-5631 fix(chaining): address SonarCloud findings\n\n- Mark injected parameterAuthorizer/ognlUtil fields transient (S1948);\n  they are re-injected by the container, not serialized.\n- Extract per-object copy into copyObjectToAction so the copyStack loop\n  uses no break/continue (S135); fail-closed path now returns from the\n  helper instead of continuing the loop.\n\nCo-Authored-By: Claude Opus 4.7 \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Claude Opus 4.7 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "69cb2964f03ebe46b26deda0763145aa1e5ddd52",
      "tree": "db937ef0b97ab9ce91a2af42470dc3ff696c3fc0",
      "parents": [
        "e637bb16a3f1a011a1bd627b7c06fbf54be06525"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Tue Jun 02 08:44:18 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jun 02 08:44:18 2026 +0200"
      },
      "message": "docs(skills): add triaging-security-reports skill (#1722)\n\nAdd an agent skill for triaging privately-disclosed security reports:\nresearch each claim from source without trusting the reporter, verify\neffective runtime defaults (config overrides field initializers), avoid\nintroducing unverified facts into responses, and frame findings as\nvulnerability vs. operator responsibility.\n\nDeveloped test-first: a baseline run produced contradictory, unverified\nclaims about defaults; the skill closes that gap and was verified to also\navoid over-correcting into reflexive rejection of valid reports.\n\nCo-authored-by: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "e637bb16a3f1a011a1bd627b7c06fbf54be06525",
      "tree": "42d81314c62091995514f6d91a199c2f4ea35dad",
      "parents": [
        "1555cabff1e1f55a1eba50d702e7f00d5cf1b390"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Thu May 28 07:17:30 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu May 28 07:17:30 2026 +0200"
      },
      "message": "build(deps): bump asm.version from 9.10 to 9.10.1 (#1713)\n\nBumps `asm.version` from 9.10 to 9.10.1.\n\nUpdates `org.ow2.asm:asm` from 9.10 to 9.10.1\n\nUpdates `org.ow2.asm:asm-commons` from 9.10 to 9.10.1\n\n---\nupdated-dependencies:\n- dependency-name: org.ow2.asm:asm\n  dependency-version: 9.10.1\n  dependency-type: direct:production\n  update-type: version-update:semver-patch\n- dependency-name: org.ow2.asm:asm-commons\n  dependency-version: 9.10.1\n  dependency-type: direct:production\n  update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "1555cabff1e1f55a1eba50d702e7f00d5cf1b390",
      "tree": "d34ff0b2f4c251dceedfb26d49ac5e514b666f0f",
      "parents": [
        "c7b1ca3f0a943877c8cc12648ec16a3122cd6f9d"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Thu May 28 07:17:16 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu May 28 07:17:16 2026 +0200"
      },
      "message": "build(deps): bump org.apache.maven.plugins:maven-site-plugin (#1711)\n\nBumps [org.apache.maven.plugins:maven-site-plugin](https://github.com/apache/maven-site-plugin) from 3.21.0 to 3.22.0.\n- [Release notes](https://github.com/apache/maven-site-plugin/releases)\n- [Commits](https://github.com/apache/maven-site-plugin/compare/maven-site-plugin-3.21.0...maven-site-plugin-3.22.0)\n\n---\nupdated-dependencies:\n- dependency-name: org.apache.maven.plugins:maven-site-plugin\n  dependency-version: 3.22.0\n  dependency-type: direct:production\n  update-type: version-update:semver-minor\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "c7b1ca3f0a943877c8cc12648ec16a3122cd6f9d",
      "tree": "d7ec6b35201e59cf49d3d0e1ecd5caf80d308c5c",
      "parents": [
        "26756ce85c3cfd08dcaf721b21c15e6fcce9d1c3"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Thu May 28 07:16:52 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu May 28 07:16:52 2026 +0200"
      },
      "message": "build(deps): bump github/codeql-action from 4.35.5 to 4.36.0 (#1710)\n\nBumps [github/codeql-action](https://github.com/github/codeql-action) from 4.35.5 to 4.36.0.\n- [Release notes](https://github.com/github/codeql-action/releases)\n- [Commits](https://github.com/github/codeql-action/compare/v4.35.5...v4.36.0)\n\n---\nupdated-dependencies:\n- dependency-name: github/codeql-action\n  dependency-version: 4.36.0\n  dependency-type: direct:production\n  update-type: version-update:semver-minor\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "26756ce85c3cfd08dcaf721b21c15e6fcce9d1c3",
      "tree": "c1dfd3398cdb00f88d800a5862ed3425d1aa4411",
      "parents": [
        "d628d949a089d912399ae14065747f8d66facb17"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Fri May 22 06:47:05 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri May 22 04:47:05 2026 +0000"
      },
      "message": "security(versions): updates list of supported versions (#1708)"
    },
    {
      "commit": "d628d949a089d912399ae14065747f8d66facb17",
      "tree": "22cdcd806a26dab711a93b4ab31f16472204ddae",
      "parents": [
        "cc6b4cfd6ee64962ab7c3d9974003faa16172101"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Wed May 20 10:06:49 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed May 20 10:06:49 2026 +0200"
      },
      "message": "build(deps): bump org.apache.logging.log4j:log4j-bom (#1685)\n\nBumps [org.apache.logging.log4j:log4j-bom](https://github.com/apache/logging-log4j2) from 2.25.4 to 2.26.0.\n- [Release notes](https://github.com/apache/logging-log4j2/releases)\n- [Changelog](https://github.com/apache/logging-log4j2/blob/2.x/RELEASE-NOTES.adoc)\n- [Commits](https://github.com/apache/logging-log4j2/compare/rel/2.25.4...rel/2.26.0)\n\n---\nupdated-dependencies:\n- dependency-name: org.apache.logging.log4j:log4j-bom\n  dependency-version: 2.26.0\n  dependency-type: direct:production\n  update-type: version-update:semver-minor\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "cc6b4cfd6ee64962ab7c3d9974003faa16172101",
      "tree": "d8f19403d9f58f2fdb7ed5465a3f6122dbcb62a5",
      "parents": [
        "f26325ff90039a96d9e6d27c11bab9a5eada98eb"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Wed May 20 09:18:23 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed May 20 09:18:23 2026 +0200"
      },
      "message": "build(deps): bump asm.version from 9.9.1 to 9.10 (#1695)\n\nBumps `asm.version` from 9.9.1 to 9.10.\n\nUpdates `org.ow2.asm:asm` from 9.9.1 to 9.10\n\nUpdates `org.ow2.asm:asm-commons` from 9.9.1 to 9.10\n\n---\nupdated-dependencies:\n- dependency-name: org.ow2.asm:asm\n  dependency-version: \u00279.10\u0027\n  dependency-type: direct:production\n  update-type: version-update:semver-minor\n- dependency-name: org.ow2.asm:asm-commons\n  dependency-version: \u00279.10\u0027\n  dependency-type: direct:production\n  update-type: version-update:semver-minor\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "f26325ff90039a96d9e6d27c11bab9a5eada98eb",
      "tree": "f991d95c6d00b86540e07b399d5def6405e133e4",
      "parents": [
        "1bd2951f7e1c1cb2a5818f0940c9a714bcd74059"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Wed May 20 09:07:05 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed May 20 07:07:05 2026 +0000"
      },
      "message": "ci(dependabot): fix cooldown property name (#1702)\n\nThe `cooldown` block expects `default-days`, not `default`. Using the\nwrong key fails schema validation, causing Dependabot to silently fall\nback to the previously valid config — which still targets\n`release/struts-6-8-x` instead of `support/struts-6-x-x`.\n\nAlso adds a 3-day cooldown to the `main` maven entry for consistency.\n\nCo-authored-by: Claude Opus 4.7 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "1bd2951f7e1c1cb2a5818f0940c9a714bcd74059",
      "tree": "4cb121c9e9135871f2d467e9b9a7d885e4873a17",
      "parents": [
        "16618e0236f478c874da37b879c51b70ea7f6b18"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Wed May 20 08:59:25 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed May 20 08:59:25 2026 +0200"
      },
      "message": "build(deps-dev): bump org.apache.maven.plugins:maven-enforcer-plugin (#1698)\n\nBumps [org.apache.maven.plugins:maven-enforcer-plugin](https://github.com/apache/maven-enforcer) from 3.6.2 to 3.6.3.\n- [Release notes](https://github.com/apache/maven-enforcer/releases)\n- [Commits](https://github.com/apache/maven-enforcer/compare/enforcer-3.6.2...enforcer-3.6.3)\n\n---\nupdated-dependencies:\n- dependency-name: org.apache.maven.plugins:maven-enforcer-plugin\n  dependency-version: 3.6.3\n  dependency-type: direct:development\n  update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "16618e0236f478c874da37b879c51b70ea7f6b18",
      "tree": "d9ba9a0a1592cc5951cf2957688ba60077f0c4f8",
      "parents": [
        "419fb1f5c6a17a68ae7083b3a200d4c72cb7201d"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Wed May 20 08:58:58 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed May 20 08:58:58 2026 +0200"
      },
      "message": "build(deps): bump github/codeql-action from 4.35.4 to 4.35.5 (#1694)\n\nBumps [github/codeql-action](https://github.com/github/codeql-action) from 4.35.4 to 4.35.5.\n- [Release notes](https://github.com/github/codeql-action/releases)\n- [Commits](https://github.com/github/codeql-action/compare/v4.35.4...v4.35.5)\n\n---\nupdated-dependencies:\n- dependency-name: github/codeql-action\n  dependency-version: 4.35.5\n  dependency-type: direct:production\n  update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "419fb1f5c6a17a68ae7083b3a200d4c72cb7201d",
      "tree": "370afe2ce23d262ebf5ec74e14e664f3ed107c9e",
      "parents": [
        "213b83f64f8fc83f9988edad56f2ad10a246ffeb"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Tue May 19 10:26:49 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue May 19 08:26:49 2026 +0000"
      },
      "message": "WW-5535 test(core): cover wildcard-resolved unannotated methods via real proxy (#1692)\n\nCloses the test gap noted in the WW-5535 research: no integration test\nexercised HttpMethodInterceptor against a real DefaultActionProxy resolving\na wildcard action with an unannotated method.\n\nUses xwork-test-allowed-methods.xml\u0027s existing \u003caction name\u003d\"Wild-*\"\nmethod\u003d\"{1}\"\u003e on HttpMethodsTestAction. URL \"Wild-execute\" resolves to\nActionSupport.execute() (no method-level HTTP annotation); the class-level\n@AllowedHttpMethod(POST) must still reject GET end-to-end.\n\nTogether with the prior MockActionProxy regression tests, this locks in\nboth halves of the fix:\n- DefaultActionProxy.resolveMethod() sets isMethodSpecified()\u003dtrue for\n  wildcard-resolved methods (WW-5535 / #1592)\n- HttpMethodInterceptor falls back to class-level annotations when the\n  resolved method is unannotated (#1690)"
    },
    {
      "commit": "213b83f64f8fc83f9988edad56f2ad10a246ffeb",
      "tree": "13646bfcd3c59aac211f9832f85e4b86f4f995dd",
      "parents": [
        "7ea7911cf8d2a3fab00f1409b3f8d6b779daef18"
      ],
      "author": {
        "name": "ⳕⲛτⲉⲅⲥⲉⳏτⲟⲅ 🕵🏻",
        "email": "192411347+g0w6y@users.noreply.github.com",
        "time": "Tue May 19 11:35:21 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue May 19 08:05:21 2026 +0200"
      },
      "message": "fix(core): enforce class-level HTTP method annotations for wildcard-resolved unannotated methods (#1690)\n\nThe WW-5535 fix (commit 4d2eb93) corrected isMethodSpecified() for wildcard-resolved\nmethods but introduced a structural gap in HttpMethodInterceptor.intercept().\n\nThe if/else-if structure made the class-level annotation check unreachable whenever\nisMethodSpecified()\u003dtrue and the resolved method carries no method-level annotation:\n\n  if (isMethodSpecified()) {\n      if (isAnnotatedBy(method)) { ... }\n      // falls through silently\n  } else if (isAnnotatedBy(class)) { ... }  // never reached\n  return invocation.invoke();               // no enforcement\n\nFix: convert else-if to standalone if so the class-level check is always evaluated\nas a fallback when the method itself has no annotation. Method-level annotations\nstill take precedence (checked first).\n\nAdd two regression tests covering the wildcard-resolved unannotated method scenario.\n\nCo-authored-by: g0w6y \u003cg0w6y@users.noreply.github.com\u003e"
    },
    {
      "commit": "7ea7911cf8d2a3fab00f1409b3f8d6b779daef18",
      "tree": "85d88ef3db5076c135ec469422c1f451d877bb11",
      "parents": [
        "e83487f0e19f4000cf46a88fc6f06bd549ad9d7b"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Mon May 18 15:04:58 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon May 18 13:04:58 2026 +0000"
      },
      "message": "chore(rat): excludes Markdown files in docs/ folder (#1691)"
    },
    {
      "commit": "e83487f0e19f4000cf46a88fc6f06bd549ad9d7b",
      "tree": "0b0445ec9d6d2b162c590211ce9f5d4b8ea2ad97",
      "parents": [
        "3432433e38b64628a93e44f9e0c28cf2bb27f756"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Mon May 18 13:52:17 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon May 18 13:52:17 2026 +0200"
      },
      "message": "WW-5627 Gate CookieInterceptor through ParameterAuthorizer (#1681)\n\n* WW-5627 add ParameterAllowlister interface and STRUTS_PARAMETER_ALLOWLISTER constant\n\nCo-Authored-By: Claude Sonnet 4.6 \u003cnoreply@anthropic.com\u003e\n\n* WW-5627 add OgnlParameterAllowlister default implementation\n\nCo-Authored-By: Claude Sonnet 4.6 \u003cnoreply@anthropic.com\u003e\n\n* WW-5627 register ParameterAllowlister bean in struts-default DI\n\n* WW-5627 delegate ParametersInterceptor OGNL allowlisting to OgnlParameterAllowlister\n\nAlso register ParameterAllowlister in DefaultConfiguration bootstrap\nfactories so it is available in test containers (parallel to how\nParameterAuthorizer was already registered there).\n\nCo-Authored-By: Claude Sonnet 4.6 \u003cnoreply@anthropic.com\u003e\n\n* WW-5627 test(cookie): failing test for unannotated setter skip\n\nCo-Authored-By: Claude Sonnet 4.6 \u003cnoreply@anthropic.com\u003e\n\n* WW-5627 gate CookieInterceptor cookie injection through ParameterAuthorizer\n\nAdds a 5-arg `populateCookieValueIntoStack(name, value, map, stack, action)` hook\nthat runs cookie writes through `ParameterAuthorizer.isAuthorized` and primes\n`ThreadAllowlist` via `ParameterAllowlister` for nested paths, then delegates\nto the legacy 4-arg form. The 4-arg form is `@Deprecated(since\u003d\"7.2.0\")` but\nits body is unchanged, so existing subclass overrides automatically receive\nonly authorized cookies. Default-config behavior is preserved because the\nauthorizer short-circuits when `requireAnnotations\u003dfalse`.\n\nExisting `CookieInterceptorTest` instantiates `new CookieInterceptor()` rather\nthan going through the container, leaving the new injected fields null. Wires\nexplicit pass-through lambdas through a `disableAuthorizationGate(...)` helper\nso those tests continue to exercise default-config behavior.\n\n* WW-5627 cover CookieInterceptor authorization matrix in CookieInterceptorAnnotationTest\n\n* WW-5627 docs(cookie): document new 5-arg extension hook and deprecation\n\n* WW-5627 wire OgnlParameterAllowlister in StrutsParameterAnnotationTest fixture\n\n* WW-5627 address SonarCloud findings on PR #1681\n\n- S1948: mark transient on the new ParameterAuthorizer/ParameterAllowlister\n  fields in CookieInterceptor and ParametersInterceptor (the host classes\n  are Serializable; the injected services are not).\n- S1874: suppress the deprecation warning on the new 5-arg\n  populateCookieValueIntoStack — the delegation to the deprecated 4-arg\n  form is the contract that lets existing subclass overrides participate.\n- S3776: extract `allowlistViaPropertyDescriptor` and\n  `allowlistViaPublicField` from `OgnlParameterAllowlister.allowlistAuthorizedPath`\n  to drop cognitive complexity below the threshold.\n- S1068: remove the unused `mapping` test fixture field.\n\n* WW-5627 clarify ParameterAllowlister contract and tidy ParametersInterceptor\n\nRename `ParameterAllowlister#allowlistAuthorizedPath` to `primeAllowlistForPath`\nto make the contract explicit: the SAM is a side-effect-only priming hook that\nruns after `ParameterAuthorizer#isAuthorized` has already decided. A no-op\nreturn means \"no priming needed or possible\", never \"rejected\". The interface\nname stays channel-agnostic; only the impl class (`OgnlParameterAllowlister`)\nbinds the priming to OGNL\u0027s `ThreadAllowlist`.\n\nAdd a `LOG.debug` in `OgnlParameterAllowlister` for the case where authorization\npassed but no `@StrutsParameter` could be located on the root property\n(e.g. `ModelDriven` models without per-property annotations) so the\nauthorize-vs-prime gap is observable instead of surfacing later as an opaque\nOGNL traversal failure.\n\nDrop the dead `performOgnlAllowlisting` pass-through and its unused `paramDepth`\nparameter from `ParametersInterceptor` — the depth check is already enforced\ninside `OgnlParameterAllowlister.primeAllowlistForPath`, so the outer guard was\na redundant computation.\n\nNo behavior change.\n\n---------\n\nCo-authored-by: Claude Sonnet 4.6 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "3432433e38b64628a93e44f9e0c28cf2bb27f756",
      "tree": "88c9125555ebc82da5d680a5c7b793f217376de9",
      "parents": [
        "09d03286f81d16ee4212a03f4881bf5abf0cdd80"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Fri May 15 13:27:53 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri May 15 13:27:53 2026 +0200"
      },
      "message": "chore(agents): defines a new AGENTS.md focused on reporting vulnerabilities (#1680)\n\n* chore(agents): defines a new AGENTS.md focused on reporting vulnerabilities\n\n* chore(security): makes SECURITY.md the source of truth\n\n* Fix typos in SECURITY.md and minor grammar correction\n\n* chore(security): clarify public GitHub repo PoC counts as disclosure\n\nPer @ppkarwasz review on #1680: expand the PoC bullet to make explicit\nthat pushing a PoC to a public GitHub repo, gist, fork, or branch is\npublic disclosure, and note that private repos require granting access\nto each PMC member individually.\n\nCo-Authored-By: Claude Opus 4.7 \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: René Gielen \u003crgielen@apache.org\u003e\nCo-authored-by: Claude Opus 4.7 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "09d03286f81d16ee4212a03f4881bf5abf0cdd80",
      "tree": "4d38d514972ecf04ac5034d0fa389b232fe2b554",
      "parents": [
        "a48f57d16163980587f16859f8941904502e02ee"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Thu May 14 15:01:00 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu May 14 15:01:00 2026 +0200"
      },
      "message": "WW-5626 per-property authorization for Jackson REST handlers (#1674)\n\n* WW-5626 spike: validate Jackson per-property authorization mechanism\n\nValidates that the Approach C design is feasible before committing to a detailed\nimplementation plan. Wraps each SettableBeanProperty via BeanDeserializerModifier;\nintercepts deserializeAndSet to authorize against a path built from a ThreadLocal\nDeque; uses skipChildren() to discard unauthorized values; uses [0] suffix for\ncollection/map/array elements to match ParametersInterceptor depth semantics.\n\nFindings:\n- Delegating base class via \u0027protected delegate\u0027 field is the right pattern\n- addOrReplaceProperty(prop, true) is the correct builder API\n- Reject-at-parent skips all nested deserialization (better security than two-phase\n  copy: setter side effects on unauthorized properties never fire)\n- JavaType#isCollectionLikeType/isMapLikeType/isArrayType detects the indexed-path case\n\nSpike is kept under .../spike/ as a learning artifact; it will be replaced by\nproduction code + tests in subsequent commits.\n\n* WW-5626 add ParameterAuthorizationContext for deserializer-level authorization\n\nCo-Authored-By: Claude Sonnet 4.6 \u003cnoreply@anthropic.com\u003e\n\n* WW-5626 address review feedback on ParameterAuthorizationContext\n\nCo-Authored-By: Claude Sonnet 4.6 \u003cnoreply@anthropic.com\u003e\n\n* WW-5626 add AuthorizationAwareContentTypeHandler marker interface\n\n* WW-5626 add AuthorizingSettableBeanProperty for Jackson per-property authorization\n\n* WW-5626 add ParameterAuthorizingModule installing the property wrapper on Jackson mappers\n\n* WW-5626 register ParameterAuthorizingModule on default Jackson REST handlers\n\n* WW-5626 use AuthorizationAwareContentTypeHandler path when handler supports it\n\n* WW-5626 add integration tests proving the new Jackson authorization path is used\n\n* WW-5626 deprecate XStreamHandler in favor of JacksonXmlHandler\n\n* WW-5626 remove Jackson auth spike; replaced by production tests\n\n* WW-5626 make JuneauXmlHandler authorization-aware via post-parse walk\n\nImplements AuthorizationAwareContentTypeHandler. When ParameterAuthorizationContext\nis active (set by ContentTypeInterceptor when requireAnnotations\u003dtrue), the handler\nwalks the parsed result tree and copies only authorized properties to the target,\ndescending into nested beans/collections/maps/arrays with indexed-path semantics\n([0] suffix) for parity with ParametersInterceptor.\n\nNote: Juneau parses the entire result tree before our walk runs, so setter side\neffects on transient nested objects can fire even for unauthorized properties —\nthose transient objects are then discarded. This is functionally equivalent to the\nlegacy two-phase copy in ContentTypeInterceptor; only the Jackson handlers achieve\nthe stronger guarantee where unauthorized subtrees are never instantiated at all\n(they use Jackson\u0027s BeanDeserializerModifier + skipChildren).\n\nWhen no context is bound (default config), behavior is unchanged: parser.parse +\nBeanUtils.copyProperties.\n\n* WW-5626 add JuneauXmlHandler integration tests for @StrutsParameter authorization\n\n* WW-5626 test(rest): cover JuneauXmlHandler post-parse walk for collections, maps, arrays\n\nSonar reported 51 uncovered new lines in JuneauXmlHandler (48.8% coverage on the\npost-parse authorization walk — the security-critical code path the branch exists\nto introduce). Add integration coverage for the previously-uncovered branches:\n\n- collection-of-scalars (List\u003cString\u003e tags)\n- collection-of-beans (List\u003cAddress\u003e addresses)\n- map-of-scalars (Map\u003cString,String\u003e attributes)\n- array-of-scalars (String[] aliases)\n- empty collection\n- malformed XML wrapped as IOException\n\nAlso drop two unnecessary casts (Sonar S1905) on lines 243/252 — the unchecked\nconversion happens at the return statement, the explicit casts were redundant\nunder the existing @SuppressWarnings(\"unchecked\").\n\nAdd @Override on the inline AnyConstraintMatcher.matches override (Sonar S1161).\n\nCo-Authored-By: Claude Opus 4.7 \u003cnoreply@anthropic.com\u003e\n\n* WW-5626 test(rest): cover AuthorizingSettableBeanProperty builder-path deserialization\n\nSonar reported 11 uncovered new lines on AuthorizingSettableBeanProperty (66.7%\ncoverage). All 11 are in deserializeSetAndReturn — the alternate Jackson entry\npoint used for builder-pattern deserialization, never triggered by setter-based\nfixtures like Person.\n\nAdd an @JsonDeserialize(builder\u003d...) fixture (ImmutablePerson) that forces\nJackson to use BuilderBasedDeserializer, which dispatches property writes\nthrough deserializeSetAndReturn. Three new tests exercise the path:\ninactive-context pass-through, top-level authorization, and full rejection.\n\nCo-Authored-By: Claude Opus 4.7 \u003cnoreply@anthropic.com\u003e\n\n* WW-5626 refactor(rest): extract helpers from ContentTypeInterceptor.intercept\n\nSonar S3776 flagged intercept() at cognitive complexity 16 (limit 15). Extract\nthe body-handling branches into named helpers:\n\n- openBodyReader: encoding-aware reader from the request InputStream\n- applyRequestBody: dispatcher between requireAnnotations on/off paths\n- applyWithAuthorizationContext: bind + delegate + unbind for AuthorizationAware handlers\n- applyTwoPhaseDeserialize: legacy fresh-instance + copyAuthorizedProperties path\n\nintercept() drops to ~12 lines and reads as a flat sequence: resolve target,\ndelegate body application, invoke. Each helper carries the comment that\nexplains the security model for its branch.\n\nAdd @Override on the inline AnyConstraintMatcher.matches override (Sonar S1161).\n\nCo-Authored-By: Claude Opus 4.7 \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Claude Sonnet 4.6 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "a48f57d16163980587f16859f8941904502e02ee",
      "tree": "5ee348f97e850ef971920031ec6d461b867f7fbe",
      "parents": [
        "9c8d3de0e4ba79d9dcc6b6f58d07594bbd4b9ace"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Thu May 14 14:58:10 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu May 14 14:58:10 2026 +0200"
      },
      "message": "build(deps): bump slf4j.version from 2.0.17 to 2.0.18 (#1683)\n\nBumps `slf4j.version` from 2.0.17 to 2.0.18.\n\nUpdates `org.slf4j:slf4j-api` from 2.0.17 to 2.0.18\n\nUpdates `org.slf4j:slf4j-simple` from 2.0.17 to 2.0.18\n\n---\nupdated-dependencies:\n- dependency-name: org.slf4j:slf4j-api\n  dependency-version: 2.0.18\n  dependency-type: direct:production\n  update-type: version-update:semver-patch\n- dependency-name: org.slf4j:slf4j-simple\n  dependency-version: 2.0.18\n  dependency-type: direct:development\n  update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "9c8d3de0e4ba79d9dcc6b6f58d07594bbd4b9ace",
      "tree": "c138f25c0488a943350b88a58ed7e47776e1323d",
      "parents": [
        "e5aa3e98a668f14ba7a7db54d9111611347f4844"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Thu May 14 14:57:50 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu May 14 14:57:50 2026 +0200"
      },
      "message": "build(deps): bump github/codeql-action from 4.35.3 to 4.35.4 (#1682)\n\nBumps [github/codeql-action](https://github.com/github/codeql-action) from 4.35.3 to 4.35.4.\n- [Release notes](https://github.com/github/codeql-action/releases)\n- [Commits](https://github.com/github/codeql-action/compare/v4.35.3...v4.35.4)\n\n---\nupdated-dependencies:\n- dependency-name: github/codeql-action\n  dependency-version: 4.35.4\n  dependency-type: direct:production\n  update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "e5aa3e98a668f14ba7a7db54d9111611347f4844",
      "tree": "afa1fbf4040d1bf67f50a92a0e4f87925abcf9c6",
      "parents": [
        "56b9b6807ed33da0e0721ed4a897d02f04ac1d0c"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Sat May 09 13:49:55 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat May 09 13:49:55 2026 +0200"
      },
      "message": "build(deps): bump com.github.ben-manes.caffeine:caffeine (#1679)\n\nBumps [com.github.ben-manes.caffeine:caffeine](https://github.com/ben-manes/caffeine) from 3.2.3 to 3.2.4.\n- [Release notes](https://github.com/ben-manes/caffeine/releases)\n- [Commits](https://github.com/ben-manes/caffeine/compare/v3.2.3...v3.2.4)\n\n---\nupdated-dependencies:\n- dependency-name: com.github.ben-manes.caffeine:caffeine\n  dependency-version: 3.2.4\n  dependency-type: direct:production\n  update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "56b9b6807ed33da0e0721ed4a897d02f04ac1d0c",
      "tree": "4f6158285a407f5f54e925bd80fb86196e6c9df6",
      "parents": [
        "fd05da73fa58a419b68dd0ca3211d38245714f23"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Sat May 09 13:49:01 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat May 09 13:49:01 2026 +0200"
      },
      "message": "build(deps): bump org.owasp:dependency-check-maven from 12.2.1 to 12.2.2 (#1677)\n\nBumps [org.owasp:dependency-check-maven](https://github.com/dependency-check/DependencyCheck) from 12.2.1 to 12.2.2.\n- [Release notes](https://github.com/dependency-check/DependencyCheck/releases)\n- [Changelog](https://github.com/dependency-check/DependencyCheck/blob/main/CHANGELOG.md)\n- [Commits](https://github.com/dependency-check/DependencyCheck/compare/v12.2.1...v12.2.2)\n\n---\nupdated-dependencies:\n- dependency-name: org.owasp:dependency-check-maven\n  dependency-version: 12.2.2\n  dependency-type: direct:production\n  update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "fd05da73fa58a419b68dd0ca3211d38245714f23",
      "tree": "bb7ed967421030853c8e4f66464359718e20de32",
      "parents": [
        "c9cfa36cb21b236bd6cc9836170b959ea67290e7"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Sat May 09 13:48:48 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat May 09 13:48:48 2026 +0200"
      },
      "message": "build(deps): bump org.glassfish.jaxb:jaxb-bom from 4.0.7 to 4.0.8 (#1676)\n\nBumps org.glassfish.jaxb:jaxb-bom from 4.0.7 to 4.0.8.\n\n---\nupdated-dependencies:\n- dependency-name: org.glassfish.jaxb:jaxb-bom\n  dependency-version: 4.0.8\n  dependency-type: direct:production\n  update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "c9cfa36cb21b236bd6cc9836170b959ea67290e7",
      "tree": "8a163d1e25d03c45dd25b963cc30a715411ac92c",
      "parents": [
        "690c4c27371aca74abbc0c824d40764a237c7bef"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Sat May 09 13:48:28 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat May 09 13:48:28 2026 +0200"
      },
      "message": "build(deps): bump github/codeql-action from 4.35.2 to 4.35.3 (#1675)\n\nBumps [github/codeql-action](https://github.com/github/codeql-action) from 4.35.2 to 4.35.3.\n- [Release notes](https://github.com/github/codeql-action/releases)\n- [Commits](https://github.com/github/codeql-action/compare/v4.35.2...v4.35.3)\n\n---\nupdated-dependencies:\n- dependency-name: github/codeql-action\n  dependency-version: 4.35.3\n  dependency-type: direct:production\n  update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "690c4c27371aca74abbc0c824d40764a237c7bef",
      "tree": "b468ef9dbbbe8b0a0ed1dab155642d00321016de",
      "parents": [
        "7799c0fd2a2fd82079cc235d8f00dfb4b4b9b9f0"
      ],
      "author": {
        "name": "Lukasz Lenart",
        "email": "lukaszlenart@apache.org",
        "time": "Sat May 09 12:09:03 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat May 09 12:09:03 2026 +0200"
      },
      "message": "WW-5626 cleanup follow-ups for @StrutsParameter JSON/REST enforcement (#1673)\n\n* WW-5626 add ParameterAuthorizer#resolveTarget for centralized ModelDriven resolution\n\nMove the ValueStack peek logic that derives the target object from action+ModelDriven\nstate out of ParametersInterceptor and into ParameterAuthorizer. Callers that need\nboth authorization and the resolved target (for downstream OGNL allowlisting) can\nnow call resolveTarget once and reuse the result.\n\n* WW-5626 delegate ModelDriven target resolution to ParameterAuthorizer\n\nReplace the inline ValueStack peek in ParametersInterceptor#isParameterAnnotatedAndAllowlist\nwith a call to ParameterAuthorizer#resolveTarget. The ModelDriven import is no longer\nneeded in this class.\n\n* WW-5626 defensively skip non-String JSON keys in authorization filter\n\nThe (String) cast in filterUnauthorizedKeysRecursive threw ClassCastException\nfor any custom JSONReader producing non-String keys. Replace with an instanceof\npattern that debug-logs and skips entries whose key cannot be converted to a\nparameter path.\n\n* WW-5626 add real JacksonJsonHandler integration tests for @StrutsParameter filtering\n\nThe existing ContentTypeInterceptorTest uses mock ContentTypeHandlers, so its\nrequireAnnotations\u003dtrue tests verify only that intercept() returns SUCCESS — they\nassert nothing about which properties were actually filtered. These integration\ntests use a real JacksonJsonHandler + a real StrutsParameterAuthorizer to verify\nend-to-end property-level filtering for top-level annotated/unannotated properties\nand nested properties at varying authorized depths.\n\nThe SecureRestAction fixture documents a semantic divergence: REST\u0027s recursive\ncopy authorizes each path level independently, so depth-0 authorization on the\ntop-level property requires @StrutsParameter on the setter even when nested\nfield access is the actual goal. ParametersInterceptor only requires the getter\nannotation. This divergence is tracked for the Approach C refactor.\n\n* WW-5626 make ParameterAuthorizer#resolveTarget a default method to preserve SAM\n\nMaking resolveTarget abstract broke ParameterAuthorizer as a functional interface,\nwhich the existing JSON and REST plugin tests rely on for lambda-based stubs:\n  interceptor.setParameterAuthorizer((parameterName, target, action) -\u003e true);\n\nThe default returns the action unchanged — adequate for lambda test stubs whose\nauthorization decisions don\u0027t depend on the resolved target. The production\nimplementation (StrutsParameterAuthorizer) overrides this with the proper\nModelDriven value-stack peek."
    },
    {
      "commit": "7799c0fd2a2fd82079cc235d8f00dfb4b4b9b9f0",
      "tree": "ffe9216fb28c4a8c4e8eb31e12d86d7377c33729",
      "parents": [
        "11e815f26b1f7b63918a49f4ee79d9710c26726e"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Sat May 02 16:00:19 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat May 02 16:00:19 2026 +0200"
      },
      "message": "build(deps): bump commons-io:commons-io from 2.21.0 to 2.22.0 (#1672)\n\nBumps commons-io:commons-io from 2.21.0 to 2.22.0.\n\n---\nupdated-dependencies:\n- dependency-name: commons-io:commons-io\n  dependency-version: 2.22.0\n  dependency-type: direct:production\n  update-type: version-update:semver-minor\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "11e815f26b1f7b63918a49f4ee79d9710c26726e",
      "tree": "0550916c8718e04b918a861af76d8e782b2d61ce",
      "parents": [
        "664540cc8437c065f4a95ecd9e4e7bef8bc325a2"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Sat May 02 15:56:23 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat May 02 15:56:23 2026 +0200"
      },
      "message": "build(deps): bump com.fasterxml.jackson:jackson-bom (#1669)\n\nBumps [com.fasterxml.jackson:jackson-bom](https://github.com/FasterXML/jackson-bom) from 2.21.2 to 2.21.3.\n- [Commits](https://github.com/FasterXML/jackson-bom/compare/jackson-bom-2.21.2...jackson-bom-2.21.3)\n\n---\nupdated-dependencies:\n- dependency-name: com.fasterxml.jackson:jackson-bom\n  dependency-version: 2.21.3\n  dependency-type: direct:production\n  update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "664540cc8437c065f4a95ecd9e4e7bef8bc325a2",
      "tree": "cf6ad53144d968db45785c750f5da5d167a75330",
      "parents": [
        "c3a887085d4b11dd4290cc0fd1ef34a1707baa1f"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Sat May 02 15:56:03 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat May 02 15:56:03 2026 +0200"
      },
      "message": "build(deps): bump github/codeql-action from 4.35.1 to 4.35.2 (#1668)\n\nBumps [github/codeql-action](https://github.com/github/codeql-action) from 4.35.1 to 4.35.2.\n- [Release notes](https://github.com/github/codeql-action/releases)\n- [Commits](https://github.com/github/codeql-action/compare/v4.35.1...v4.35.2)\n\n---\nupdated-dependencies:\n- dependency-name: github/codeql-action\n  dependency-version: 4.35.2\n  dependency-type: direct:production\n  update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "c3a887085d4b11dd4290cc0fd1ef34a1707baa1f",
      "tree": "eecc0b5a61983769146bbe335223bc602e8b9854",
      "parents": [
        "8d6f13904f9b1cd59fb96dcb62a85fa36f8d79c6"
      ],
      "author": {
        "name": "quactv",
        "email": "51528368+tranquac@users.noreply.github.com",
        "time": "Fri May 01 15:46:38 2026 +0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri May 01 10:46:38 2026 +0200"
      },
      "message": "WW-5624: Enforce @StrutsParameter on JSON/REST body deserialization (#1657)\n\n* WW-5624 fix(security): enforce @StrutsParameter on JSON/REST body deserialization\n\nExtract ParameterAuthorizer service from ParametersInterceptor to share\n@StrutsParameter annotation enforcement across all input channels.\n\nThe json-plugin (JSONInterceptor) and rest-plugin (ContentTypeInterceptor)\npreviously bypassed @StrutsParameter checks when deserializing request\nbodies, allowing mass assignment even when\nstruts.parameters.requireAnnotations\u003dtrue.\n\nChanges:\n- New ParameterAuthorizer interface and DefaultParameterAuthorizer impl\n- JSONInterceptor: filter unauthorized Map keys before populateObject()\n- ContentTypeInterceptor: two-phase deserialization (fresh instance then\n  copy authorized properties) when requireAnnotations\u003dtrue; direct\n  deserialization for backward compat when disabled\n- OGNL ThreadAllowlist side effects remain in ParametersInterceptor only\n- Full DI wiring: struts-beans.xml + StrutsBeanSelectionProvider +\n  DefaultConfiguration\n- 15 new unit tests for ParameterAuthorizer, 2 for JSON plugin,\n  2 for REST plugin; 32 existing regression tests verified\n\n* WW-5624 address review feedback from lukaszlenart on PR #1657\n\n1. Rename DefaultParameterAuthorizer → StrutsParameterAuthorizer\n   per Struts naming convention (inline suggestion)\n\n2. Narrow ModelDriven exemption: require action instanceof ModelDriven\n   before exempting target from @StrutsParameter checks. Prevents\n   non-ModelDriven root objects (e.g. JSONInterceptor.root) from\n   bypassing annotation enforcement.\n\n3. Recursive JSON key filtering: filterUnauthorizedKeys() now recurses\n   into nested Maps and Lists, building dot-notation paths (e.g.\n   \"address.city\") for path-aware @StrutsParameter(depth\u003dN) checks.\n\n4. Deep REST property copy: copyAuthorizedProperties() now recurses\n   into nested bean types with path-aware authorization. Collections,\n   Maps, primitives, and java.lang/java.time types are copied directly.\n\n5. Null-skip semantics preserved and documented: in two-phase\n   deserialization, null in freshInstance is indistinguishable from\n   \"not present in request\" — clearing would destroy pre-initialized\n   fields. Kept as intentional design choice with inline documentation.\n\n6. No-arg constructor fallback: when target class lacks a no-arg\n   constructor, falls back to single-phase deserialization with\n   post-scrub of unauthorized properties, preserving backward compat.\n\n7. New regression tests:\n   - Non-ModelDriven target with different object (must not exempt)\n   - Nested JSON keys recursively filtered\n   - Non-action root object still checked by authorizer\n\nAll 280+ core tests, 124 JSON tests, 76 REST tests pass with 0 regressions.\n\n* WW-5624: v3 — fix indexed-path depth parity with ParametersInterceptor\n\nFour gaps identified by lukaszlenart\u0027s April 10 review are now fully addressed:\n\n1. JSON filterUnauthorizedList: pass prefix+\"[0]\" instead of bare prefix so\n   that list element properties gain one extra \u0027[\u0027 in their path — e.g.\n   \"publicPojoListDepthOne[0].key\" (depth\u003d2) is now correctly rejected when\n   @StrutsParameter(depth\u003d1), matching ParametersInterceptor semantics.\n   Also recurse into nested List\u003cList\u003cMap\u003e\u003e via an else-if branch.\n\n2. REST copyAuthorizedProperties: add authTarget parameter (always \u003d root\n   action/model, passed unchanged through all recursion levels).\n   isAuthorized() now checks the full path against the root class, so\n   \"address.city\" is looked up on the action, not on the Address object.\n\n3. REST Collection/Map/array deep authorization: replaced the as-is copy\n   with deepCopyAuthorizedCollection(), deepCopyAuthorizedMap(), and\n   deepCopyAuthorizedArray() helpers — each iterates elements with\n   path+\"[0]\" prefix, authorizing every complex element individually.\n   No-arg fallback skips the element rather than copying an unfiltered\n   object graph (security fix over plan\u0027s original as-is suggestion).\n\n4. REST scrubUnauthorizedProperties: now fully recursive via\n   scrubUnauthorizedPropertiesRecursive() — visits nested beans,\n   collection elements, and map values with authTarget always pointing\n   to the root. Includes identity-based visited-set to guard against\n   circular reference cycles.\n\nTests: core 2920 + json 124 + rest 76 \u003d 3120, 0 failures.\n\nCo-Authored-By: Claude Sonnet 4.6 \u003cnoreply@anthropic.com\u003e\n\n* WW-5624: v3.1 — fix collection type, identity set, isNestedBeanType coverage\n\nThree correctness/security issues identified by independent review:\n\n1. deepCopyAuthorizedCollection/deepCopyAuthorizedMap type preservation:\n   Previously always returned ArrayList/LinkedHashMap. If the action field\n   is typed Set\u003cPojo\u003e or SortedMap\u003cK,V\u003e, writeMethod.invoke would throw\n   IllegalArgumentException. Now: SortedSet→TreeSet, Set→LinkedHashSet,\n   List→ArrayList; SortedMap→TreeMap, Map→LinkedHashMap.\n\n2. scrubUnauthorizedPropertiesRecursive visited-set identity safety:\n   Replaced Set\u003cInteger\u003e+System.identityHashCode (not collision-safe) with\n   Collections.newSetFromMap(new IdentityHashMap\u003c\u003e()) which uses reference\n   equality (\u003d\u003d). A hash collision could have caused a valid nested object\n   to be skipped, leaving unauthorized properties un-scrubbed.\n\n3. isNestedBeanType now excludes all standard-library leaf packages:\n   java.util.* non-Collection/Map types (UUID, Currency, Locale, Date),\n   java.time.* (all temporal types, not just Temporal subinterface),\n   java.net.*, java.io.*, java.nio.*. Previously UUID etc. would return\n   true, causing the code to recurse into their internal fields and silently\n   drop the value when no @StrutsParameter annotation matched.\n\nTests: json 124 + rest 76 \u003d 200, 0 failures.\n\nCo-Authored-By: Claude Sonnet 4.6 \u003cnoreply@anthropic.com\u003e\n\n* WW-5624: v4 — close bulk-copy fallback, reject body when no no-arg ctor\n\nTwo remaining gaps addressed per lukaszlenart\u0027s April 11 review:\n\n1. copyAuthorizedProperties bulk-copy fallback removed:\n   When a nested target bean is null and createFreshInstance fails (no\n   no-arg constructor), the previous code fell back to\n   writeMethod.invoke(target, sourceValue) — copying the whole nested\n   object graph without per-path authorization. Now logs a warning and\n   skips the property entirely (same policy as deepCopyAuthorizedCollection\n   elements with no no-arg constructor).\n\n2. Top-level no-arg constructor fallback changed from scrub to reject:\n   When requireAnnotations\u003dtrue and the target class has no no-arg\n   constructor, body deserialization is now rejected entirely\n   (handler.toObject is never called). The previous best-effort scrub\n   path could not guarantee that all nested unauthorized properties were\n   nulled out. scrubUnauthorizedProperties and its recursive helper are\n   removed as dead code.\n\nTests: rest 76, 0 failures.\n\n---------\n\nCo-authored-by: tranquac \u003ctranquac@users.noreply.github.com\u003e\nCo-authored-by: Claude Sonnet 4.6 \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "8d6f13904f9b1cd59fb96dcb62a85fa36f8d79c6",
      "tree": "1e036cc9ac606646d9093c3a53591f69073a34a9",
      "parents": [
        "f4c634928337f8114b03be2adcc8349e9d6c7358"
      ],
      "author": {
        "name": "quactv",
        "email": "51528368+tranquac@users.noreply.github.com",
        "time": "Fri May 01 15:42:25 2026 +0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri May 01 10:42:25 2026 +0200"
      },
      "message": "fix(core): WW-5623 HTML-encode form action in PostbackResult to prevent XSS (#1653)\n\n* fix(core): HTML-encode form action in PostbackResult to prevent XSS\n\nPostbackResult.doExecute() embeds finalLocation into a \u003cform action\u003d\"\"\u003e\nattribute via raw string concatenation without HTML encoding. A double\nquote in the location breaks out of the attribute, enabling reflected\nXSS. The response Content-Type is text/html (line 103).\n\nThis is an encoding inconsistency: form field names and values at lines\n218-219 ARE properly URL-encoded via URLEncoder.encode(), but the form\naction attribute was not encoded at all.\n\nAdd encodeHtml() to escape \u0026, \", \u003c, \u003e in finalLocation before embedding\nit in the HTML form tag, consistent with the existing encoding approach\nfor form field values in the same class.\n\n* fix(core): WW-5623 use StringEscapeUtils and add regression tests\n\nAddress review feedback from @lukaszlenart:\n\n- Replace custom encodeHtml() with StringEscapeUtils.escapeHtml4()\n  for consistency with the rest of Struts core (DefaultActionProxy,\n  Property, TextProviderHelper all use StringEscapeUtils)\n- Add 3 focused unit tests in PostbackResultTest:\n  - testFormActionHtmlEscaping: XSS payload with attribute breakout\n  - testFormActionEscapesAllHtmlSpecialChars: covers \", \u0026, \u003c, \u003e\n  - testFormActionCleanLocationUnchanged: regression for clean URLs\n\n---------\n\nCo-authored-by: tranquac \u003ctranquac@users.noreply.github.com\u003e"
    },
    {
      "commit": "f4c634928337f8114b03be2adcc8349e9d6c7358",
      "tree": "281acc60a9910c9076198779d6ca8f3e3ca19b50",
      "parents": [
        "71d4e062d33e305a2d0609518b12bab052160be4"
      ],
      "author": {
        "name": "aaaZayne",
        "email": "1138069338@qq.com",
        "time": "Mon Apr 20 14:57:28 2026 +1000"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Apr 20 04:57:28 2026 +0000"
      },
      "message": "introduce private method to remove clones (#1666)\n\n* introduce private method to remove clones\n\n* Update naming"
    }
  ],
  "next": "71d4e062d33e305a2d0609518b12bab052160be4"
}
