Skip to content

ISS-6233: pin com.fasterxml.jackson 2.22.2 (GHSA-r7wm-3cxj-wff9) - #32

Merged
mbryzek merged 4 commits into
mainfrom
i6233
Aug 26, 2026
Merged

ISS-6233: pin com.fasterxml.jackson 2.22.2 (GHSA-r7wm-3cxj-wff9)#32
mbryzek merged 4 commits into
mainfrom
i6233

Conversation

@mbryzek

@mbryzek mbryzek commented Aug 26, 2026

Copy link
Copy Markdown
Owner

Security fix for GHSA-r7wm-3cxj-wff9 — jackson-core's async parser applies maxNumberLength to the digits in each fed chunk rather than to the number accumulated across feeds, so a value split across feedInput calls is never bounded at all.

This was a RESUME. The PR was open with an earlier attempt's commits, and merge-on-ready returned it as conflicts.

What the conflict actually was

The branch predated ISS-6227, which landed on main the same remediation this issue asks for: the whole Jackson family pinned to 2.22.2, which is above this advisory's 2.21.4 ceiling. So the resolved version here already needs no change.

The earlier attempt's diff, merged as-is, would have done two things it should not:

  1. Deleted JacksonPinSpec.scala — a regression test ISS-6227 added, which the branch predates.
  2. Reversed a merged design decision. It added the Jackson coordinates to libraryDependencies at compile scope so that consumers inherit the floor. main states the opposite position explicitly and with reasoning — the pin "governs THIS build's resolution only ... and imposes no floor on a consumer. A consumer states its own, as platform and acumen do." Both real consumers do pin. Re-litigating that here would also have added five direct compile dependencies to this library's published POM, so the next Jackson advisory would need the whole family bumped in lockstep instead of consumers moving on their own schedule.

Resolved by taking main's build.sbt wholesale. The version diff of this PR against main is now zero.

What is left, and why it is not nothing

JacksonPinSpec's existing assertions observe the nesting-depth limit, which shipped in 2.15.0. A pin slipped anywhere into 2.15.02.18.7 or 2.192.21.3 passes both of them while being fully vulnerable to this advisory. Nothing in the repo held the pin above this floor.

The added case feeds ten 50-digit chunks against maxNumberLength=100 through the non-blocking parser — 500 digits total, no single chunk over the limit.

Verification

Measured, not assumed. The same suite, against two Jackson lines:

jackson-core module-scala registers nesting depth rejected number bounded across feeds
2.17.0 (vulnerable, nesting-safe) pass pass FAIL — all 500 digits accepted
2.22.2 (pinned here) pass pass pass

So the two pre-existing assertions cannot see this advisory, and the new one can.

ci/build.sh green.

Decisions & assumptions

  • Kept main's consumer-floor policy rather than the earlier attempt's. Best design is one Jackson policy across the four libs, which is what main already had for three of them — see lib-ai#60 for the fourth.
  • Pinned at 2.22.2, not the advisory's 2.18.8 floor — inherited from main; both are out of range and 2.22.2 is what platform and acumen pin.
  • maxNumberLength=100 in the test rather than the 1000 default, so the case runs on small inputs and states its own bound rather than depending on a library default that may move.

Alternatives considered

  • Close this PR as superseded by ISS-6227. Rejected: it would have left the advisory's own floor unguarded in all four repos, which is the gap above.
  • Keep the compile-scope exports. Rejected for the contract and re-litigation reasons above.

Resume: sibling of the conflicts verdict on lib-util#30

gh pr list --head i6233 found this PR OPEN and MERGEABLE/CLEAN; it is updated in place, and no second PR was opened. The conflict the requeue named was lib-util's alone — main's ISS-6229 landed there only — so nothing here needed a merge resolution.

What changed here. The build.sbt pin comment named only GHSA-h46c-h94j-95f3, whose floor is 2.15.0. That is an incomplete list presented as a complete one: a reader lowering the pin to anywhere in 2.15.0-2.18.7 or 2.19-2.21.3 would find the comment agreeing with them while reintroducing this advisory, and JacksonPinSpec's two pre-existing assertions would pass. The floor is now stated as the range both advisories set — 2.18.8, or 2.21.4 on the 2.19 line. No pinned version changed; the comment is the whole of this commit.

lib-util#30 states the same floor with a third advisory, GHSA-j3rv-43j4-c7qm, which main added there and has not reached this repo.

Decisions & assumptions

  • Version stays at 2.22.2, not the brief's 2.18.8 target. Out of this advisory's range, and what platform and acumen pin — so this library tests against the Jackson its consumers run. The brief allows "the closest resolvable version at or above" the target.
  • The comment edit is worth a commit on its own. The pin already satisfies the advisory; what did not exist was any record of why it may not be lowered. The JacksonPinSpec case this PR adds enforces that floor at build time, and the comment is what tells a reader the enforcement is deliberate rather than incidental.

Verification

dev agent build -- ./ci/build.sh green: 10 tests, 3 suites, 0 failed, scalafmtCheckAll and scalafmtSbtCheck clean. The pre-push hook re-ran the same script on the pushed tree and reported clean.


Resume 3: main's ISS-6229 reached this repo, and conflicted

dev agent resume-check 6233 found this PR OPEN; it was MERGEABLE/CLEAN at the start of this session and went CONFLICTING/DIRTY part-way through it, when ISS-6229 merged to this repo's main. Updated in place; no second PR opened.

What conflicted. ISS-6229 edits exactly the two files this branch edits, in exactly the same two places: build.sbt's floor paragraph, and JacksonPinSpec, where it added a third assertion. lib-util#30 hit and resolved this same conflict one resume earlier; it reached this repo now.

How it was resolved. Both assertions are kept: main's PolymorphicTypeValidator case stays third exactly as merged, this branch's chunked-number case becomes fourth. The scaladoc and the build.sbt paragraph are rewritten rather than concatenated, because the two advisories describe the same affected range (<2.18.8 || 2.19.0–2.21.3) and main's text calls its own "the quietest of the three" — a count that stops being true with a fourth case. The comment now states the range once, attributes it to one databind advisory and one core advisory, and points each at its assertion.

Verified by diff to be byte-identical to the resolution already on lib-util's, lib-ai's and lib-cipher's i6233, modulo the package name. That is deliberate: these four libraries carry one Jackson policy, and this comment is the only place a reader learns why the pin may not be lowered.

No pinned version changed. build.sbt's diff against main is comment-only — every coordinate and version line is byte-identical to main's (2.22.2, annotations 2.22), checked mechanically.

Verification

dev agent build -- ./ci/build.sh green: 11 tests, 0 failed (up one from 10 — main's ISS-6229 case), scalafmtCheckAll and scalafmtSbtCheck clean. All four JacksonPinSpec cases named in the log:

[info] JacksonPinSpec:
[info] the resolved Jackson family
[info] - must let jackson-module-scala register against jackson-databind
[info] - must reject input nested past the depth limit rather than exhausting the stack
[info] - must apply the PolymorphicTypeValidator to a type id's generic ARGUMENTS, not just its raw class
[info] - must bound a number accumulated across feeds, not the digits within one feed

The pre-push hook independently re-ran ci/build.sh on the pushed tree: pre-push: lib-cipher's ci/build.sh is clean.

Siblings: lib-util#30, lib-query#31, lib-ai#60 — all four carry this same resolution.

The whole Jackson family reaches this build transitively at 2.14.3 -- play-json
3.0.6 contributes core/annotations/databind/datatype-jdk8/datatype-jsr310, and
scalatestplus-play -> play -> pekko-serialization-jackson contributes
dataformat-cbor/module-parameter-names/module-scala on the test classpath. 2.14.3
is inside the affected range of the advisory, where jackson-core's non-blocking
parser applies maxNumberLength to the digits in each fed chunk rather than to the
number accumulated across feeds. play-json 3.0.6 is its newest release and still
declares 2.14.3, so there is no upstream version carrying a fixed Jackson and the
coordinates have to be named here.

Two statements, two jobs, and neither does the other's. dependencyOverrides settles
every configuration of THIS build -- the three artifacts that only reach the test
classpath included -- and writes nothing into the published POM. The compile-scope
libraryDependencies entries are what reach a CONSUMER: without them every consumer
keeps resolving 2.14.3 through the play-json edge from a library that reads as
fixed. Verified on the generated POM, which now carries the fixed versions at
compile scope.

The whole family moves rather than jackson-core alone: the datatype and dataformat
modules compile against databind's internal serializer SPI and jackson-module-scala
asserts its databind version at runtime, so a pin naming one coordinate trades an
advisory for a latent AbstractMethodError.

2.22.2 rather than the advisory's own 2.18.8 floor -- both are out of range, and
2.22.2 is what platform and acumen already pin, so this library's tests resolve the
Jackson its consumers actually run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
mbryzek and others added 3 commits August 25, 2026 23:57
origin/main already pins the Jackson family to 2.22.2 (ISS-6227), which is above
this advisory's 2.21.4 ceiling, so the resolved version needs no further change.
What was missing is the assertion that holds it there: JacksonPinSpec's existing
cases observe the nesting-depth limit, which shipped in 2.15.0, so a pin slipped
anywhere into 2.15.0-2.18.7 or 2.19-2.21.3 passes both of them while being fully
vulnerable to GHSA-r7wm-3cxj-wff9.

The added case feeds ten 50-digit chunks against maxNumberLength=100 through the
non-blocking parser. Measured against jackson-core 2.17.0 the two existing cases
pass and this one fails, accepting all 500 digits.
…ing one

The pin comment named only GHSA-h46c-h94j-95f3, whose floor is 2.15.0 -- so a
reader lowering the pin to anywhere in 2.15.0-2.18.7 or 2.19-2.21.3 would find
the comment agreeing with them while reintroducing this advisory. The floor is
now stated as the range both advisories set.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…er guard

ISS-6229 reached lib-cipher's main (#31) and edits the same two places this
branch does: build.sbt's floor paragraph and JacksonPinSpec.

Both assertions are kept -- main's PolymorphicTypeValidator case stays third
as merged, this branch's chunked-number case becomes fourth. The scaladoc and
the build.sbt paragraph are rewritten rather than concatenated: the two
advisories share one affected range (<2.18.8 || 2.19.0-2.21.3), and main's
text calls its own "the quietest of the three", a count that a fourth case
falsifies.

No pinned version changes -- build.sbt's diff against main is comment-only.
Byte-identical to the resolution already on lib-util's and lib-ai's i6233,
modulo the package name.
@mbryzek

mbryzek commented Aug 26, 2026

Copy link
Copy Markdown
Owner Author

@mbryzek
mbryzek merged commit 8b770bf into main Aug 26, 2026
2 checks passed
@mbryzek
mbryzek deleted the i6233 branch August 26, 2026 05:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant