-
Notifications
You must be signed in to change notification settings - Fork 0
fix(security): move Jackson to fixed 2.21.5 baseline #160
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
b60d7a6
c6ddc23
c312563
2905c41
6adb4dc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| # Jackson 2.21.5 Security Baseline Evidence | ||
|
|
||
| **Status:** `active_pr` #160 | ||
| **Protected baseline assessed:** `develop@622e5e6c3d534f230c390f10e3832efadfc01825` | ||
| **Assessment date:** 2026-08-09 | ||
|
|
||
| ## Trigger | ||
|
|
||
| PR #158 Security Scan run `31312834418`, Trivy filesystem job `93243221587`, completed the Trivy scan and then failed the repository's findings gate on three inherited `com.fasterxml.jackson.core:jackson-databind` findings: | ||
|
|
||
| - `CVE-2026-54515`; | ||
| - `CVE-2026-59889`; | ||
| - `GHSA-mhm7-754m-9p8w`. | ||
|
|
||
| The scanner checked out synthetic merge `6f0669cc1c3b36f14296ab839154991c298a7eec`, which merged #158 head `3b501f823fa0d3d93a5a95c714efdd79242a8a58` into protected `develop@622e5e6c3d534f230c390f10e3832efadfc01825`. #158 itself changes MySQL CDC source discovery and does not modify Jackson or Maven dependency management. Re-running #158 against the unchanged base therefore cannot change this failure. | ||
|
|
||
| Synthetic-merge scanning is useful evidence that the combined tree contains the vulnerable dependency, but it does not satisfy mightyETL's separate literal-source security-evidence requirement. | ||
|
|
||
| ## Dependency root cause | ||
|
|
||
| Protected root `pom.xml` imports `org.springframework.boot:spring-boot-dependencies:3.5.16` and does not inherit `spring-boot-starter-parent`. Spring Boot 3.5.16 manages the Jackson 2.21 line at 2.21.4. The project has no explicit Jackson override on protected develop, so the vulnerable Databind patch is inherited through dependency management rather than declared by #158. | ||
|
|
||
| FasterXML designates Jackson 2.21 as an LTS line and released 2.21.5 on 2026-07-06. Upstream advisory/release material identifies 2.21.5 as the patched 2.21.x line for the Databind issues that triggered this scan. | ||
|
|
||
| ## Remediation options | ||
|
|
||
| ### Execute now — import Jackson BOM 2.21.5 before Spring Boot dependency management | ||
|
|
||
| Selected. mightyETL uses the Spring Boot BOM without the Boot parent POM. Spring Boot's Maven documentation explicitly states that property-only version overrides do not apply in this mode; an overriding dependency or BOM must appear before the `spring-boot-dependencies` import. | ||
|
|
||
| The root POM therefore imports: | ||
|
|
||
| ```xml | ||
| <dependency> | ||
| <groupId>com.fasterxml.jackson</groupId> | ||
| <artifactId>jackson-bom</artifactId> | ||
| <version>${jackson-bom.version}</version> | ||
| <type>pom</type> | ||
| <scope>import</scope> | ||
| </dependency> | ||
| ``` | ||
|
|
||
| with `jackson-bom.version=2.21.5` before the Spring Boot BOM. This keeps Jackson modules on one upstream 2.21 security patch line rather than pinning only `jackson-databind` and risking patch-line skew. | ||
|
|
||
| ### Reject — set only `<jackson-bom.version>` in project properties | ||
|
|
||
| Rejected for this repository topology. That is the convenient override path when the project inherits Spring Boot's parent; Spring Boot documents that projects importing the BOM without the parent need dependency-management entries before the Boot import. | ||
|
|
||
| ### Reject — suppress or ignore the Trivy findings | ||
|
|
||
| Rejected. The fixed LTS patch is available and the findings arise from an actual inherited package. Suppression would leave the root cause unchanged and weaken a security gate. | ||
|
|
||
| ### Reject — patch only PR #158 | ||
|
|
||
| Rejected. The dependency belongs to shared `develop`; a PR-local patch would duplicate the override across branches and leave the protected base and other open PRs vulnerable. | ||
|
|
||
| ### Defer — wait for a later Spring Boot release | ||
|
|
||
| Not selected. Spring Boot 3.5.16 is the current project baseline and Jackson 2.21.5 is already an upstream patch in the same minor/LTS line. Waiting preserves a known shared security failure without a compatibility benefit proven by evidence. | ||
|
|
||
| ## TDD and acceptance | ||
|
|
||
| Fail-first commit `b60d7a6ef6b8a7fd8faa4cf3598bc25768a1847b` adds `JacksonSecurityBaselineTest` before dependency management changes. The test binds the root Maven security contract to: | ||
|
|
||
| - exact `jackson-bom.version` 2.21.5; | ||
| - explicit Jackson BOM import; | ||
| - Jackson override ordering before `spring-boot-dependencies`. | ||
|
|
||
| The protected POM fails those assertions by inspection because no Jackson BOM/property exists there. The GREEN candidate adds only the supported BOM override before documentation/evidence updates. | ||
|
|
||
| Final acceptance requires fresh exact-head/base evidence rather than source inspection alone: | ||
|
|
||
| - full Maven reactor compatibility; | ||
| - Dependency Review; | ||
| - generated SBOM resolving Jackson Databind outside the reported vulnerable range; | ||
| - Trivy no longer reporting the three inherited Databind findings; | ||
| - SAST/security gates; | ||
| - current review and qualifying independent approval where required; | ||
| - literal-source security evidence once the repository/central control plane provides it. | ||
|
|
||
| A synthetic merge becoming clean after this patch proves the merged dependency graph changed, but it must not be misreported as literal-source scanner proof. | ||
|
|
||
| ## Compatibility and rollback | ||
|
|
||
| The remediation stays inside Jackson 2.21 LTS and changes only the patch baseline. Spring Boot cautions that overriding curated dependencies can cause compatibility problems, so the full multi-module reactor remains mandatory acceptance evidence. | ||
|
|
||
| Rollback is the single Jackson BOM override removal, but rollback knowingly restores the inherited vulnerable Databind baseline while Spring Boot 3.5.16 remains unchanged. Therefore rollback is not a security-safe steady state; if compatibility fails, the next remediation must evaluate a supported alternate Jackson/Spring Boot patch path rather than silently accepting the advisories. | ||
|
|
||
| ## References — APA 7th | ||
|
|
||
| FasterXML. (2026, July 7). *Jackson release 2.21*. GitHub. https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.21 | ||
|
|
||
| FasterXML. (2026). *Case-insensitive deserialization bypasses per-property @JsonIgnoreProperties in jackson-databind* (GHSA-5jmj-h7xm-6q6v; CVE-2026-54515). GitHub Security Advisory. https://github.com/FasterXML/jackson-databind/security/advisories/GHSA-5jmj-h7xm-6q6v | ||
|
|
||
| FasterXML. (2026, July 8). *Jackson release 2.22.1*. GitHub. https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.22.1 | ||
|
|
||
| Spring Boot. (2026). *Using the plugin: Using Spring Boot without the parent POM*. Spring Documentation. https://docs.spring.io/spring-boot/maven-plugin/using.html | ||
|
|
||
| Spring Boot. (2026). *Version properties: Jackson BOM*. Spring Documentation. https://docs.spring.io/spring-boot/3.5/appendix/dependency-versions/properties.html | ||
|
Comment on lines
+97
to
+99
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- AGENTS.md files ---'
find .. -name AGENTS.md -print
printf '%s\n' '--- target document ---'
target='docs/doctoring/jackson-2.21.5-security-baseline.md'
if [ -f "$target" ]; then
sed -n '1,120p' "$target"
else
printf 'Missing target: %s\n' "$target"
fi
printf '%s\n' '--- relevant repository references ---'
rg -n -i '3\.5(\.16)?|4\.1\.0|maven-plugin/using\.html|Jackson BOM|2\.21\.5|GHSA-mhm7-754m-9p8w' -- "$target" docs README.md 2>/dev/null || true
printf '%s\n' '--- link status and redirects ---'
for url in \
'https://docs.spring.io/spring-boot/maven-plugin/using.html' \
'https://docs.spring.io/spring-boot/3.5/maven-plugin/using.html' \
'https://docs.spring.io/spring-boot/3.5/appendix/dependency-versions/properties.html'
do
printf '\n%s\n' "$url"
curl -L -sS -o /dev/null -w 'status=%{http_code} final=%{url_effective}\n' "$url" || true
doneRepository: ContextualWisdomLab/mightyETL Length of output: 14116 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- repository guidance ---'
cat ../git/AGENTS.md
printf '%s\n' '--- unversioned page metadata ---'
curl -L -sS 'https://docs.spring.io/spring-boot/maven-plugin/using.html' |
tr '\n' ' ' |
grep -oE '<title>[^<]*|Spring Boot [0-9]+\.[0-9]+(\.[0-9]+)?' |
head -20 || true
printf '%s\n' '--- versioned page metadata ---'
curl -L -sS 'https://docs.spring.io/spring-boot/3.5/maven-plugin/using.html' |
tr '\n' ' ' |
grep -oE '<title>[^<]*|Spring Boot [0-9]+\.[0-9]+(\.[0-9]+)?' |
head -20 || true
printf '%s\n' '--- relevant page wording ---'
for url in \
'https://docs.spring.io/spring-boot/maven-plugin/using.html' \
'https://docs.spring.io/spring-boot/3.5/maven-plugin/using.html'
do
printf '\nURL: %s\n' "$url"
curl -L -sS "$url" |
sed 's/<[^>]*>/ /g' |
tr -s '[:space:]' ' ' |
grep -oE '.{0,120}(without the parent POM|dependency management|version).{0,240}' |
head -8 || true
doneRepository: ContextualWisdomLab/mightyETL Length of output: 8498 Spring Boot 문서 링크를 3.5 버전으로 고정하세요. 기준선이 Spring Boot 3.5.16이므로, 최신 4.1.0 문서를 가리키는 비버전 경로 대신 🤖 Prompt for AI AgentsSource: Coding guidelines |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| package com.xtrmetl.etl.documentation; | ||
|
|
||
| import org.junit.jupiter.api.Test; | ||
|
|
||
| import java.io.IOException; | ||
| import java.nio.charset.StandardCharsets; | ||
| import java.nio.file.Files; | ||
| import java.nio.file.Path; | ||
| import java.nio.file.Paths; | ||
|
|
||
| import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
|
||
| /** | ||
| * Guards the shared Maven dependency-management boundary against Jackson Databind versions that | ||
| * remain inside the currently known vulnerable 2.21.x range. | ||
| */ | ||
| class JacksonSecurityBaselineTest { | ||
|
|
||
| private static final Path PROJECT_ROOT = projectRoot(); | ||
|
|
||
| @Test | ||
| void jacksonSecurityBomPrecedesImportedSpringBootDependencyManagement() throws IOException { | ||
| String pom = Files.readString(PROJECT_ROOT.resolve("pom.xml"), StandardCharsets.UTF_8); | ||
|
|
||
| assertTrue( | ||
| pom.contains("<jackson-bom.version>2.21.5</jackson-bom.version>"), | ||
| "Root dependency management must pin the first Jackson 2.21 security patch that fixes the current Databind advisories" | ||
| ); | ||
|
|
||
| String jacksonBom = "<artifactId>jackson-bom</artifactId>"; | ||
| String springBootBom = "<artifactId>spring-boot-dependencies</artifactId>"; | ||
| int jacksonIndex = pom.indexOf(jacksonBom); | ||
| int springBootIndex = pom.indexOf(springBootBom); | ||
|
|
||
| assertTrue(jacksonIndex >= 0, "Root dependencyManagement must import the Jackson BOM explicitly"); | ||
| assertTrue(springBootIndex >= 0, "Root dependencyManagement must continue importing Spring Boot dependencies"); | ||
| assertTrue( | ||
| jacksonIndex < springBootIndex, | ||
| "Without the Spring Boot parent POM, the explicit Jackson override BOM must precede spring-boot-dependencies" | ||
|
Comment on lines
+30
to
+39
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- AGENTS.md files ---'
find .. -name AGENTS.md -print
printf '%s\n' '--- target and nearby Maven files ---'
git ls-files | grep -E '(^|/)(pom\.xml|JacksonSecurityBaselineTest\.java|AGENTS\.md)$' | sed -n '1,120p'
printf '%s\n' '--- test outline ---'
ast-grep outline etl-service/src/test/java/com/xtrmetl/etl/documentation/JacksonSecurityBaselineTest.java || true
printf '%s\n' '--- test source ---'
cat -n etl-service/src/test/java/com/xtrmetl/etl/documentation/JacksonSecurityBaselineTest.java
printf '%s\n' '--- Maven POM locations/content around Jackson ---'
for f in $(git ls-files '*pom.xml'); do
if grep -q -E 'jackson|spring-boot-dependencies|jackson-bom' "$f"; then
echo "### $f"
grep -n -C 6 -E 'jackson|spring-boot-dependencies|dependencyManagement|jackson-databind' "$f" | sed -n '1,240p'
fi
doneRepository: ContextualWisdomLab/mightyETL Length of output: 6518 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- repository guidance ---'
cat -n AGENTS.md
printf '%s\n' '--- focused verifier ---'
python3 - <<'PY'
from pathlib import Path
import xml.etree.ElementTree as ET
test = Path("etl-service/src/test/java/com/xtrmetl/etl/documentation/JacksonSecurityBaselineTest.java").read_text()
pom_text = Path("pom.xml").read_text(encoding="utf-8")
root = ET.fromstring(pom_text)
ns = {"m": "http://maven.apache.org/POM/4.0.0"}
management = root.find("m:dependencyManagement/m:dependencies", ns)
imports = []
for dep in management.findall("m:dependency", ns):
imports.append({
"groupId": dep.findtext("m:groupId", namespaces=ns),
"artifactId": dep.findtext("m:artifactId", namespaces=ns),
"version": dep.findtext("m:version", namespaces=ns),
"type": dep.findtext("m:type", namespaces=ns),
"scope": dep.findtext("m:scope", namespaces=ns),
})
print("current raw checks:", {
"version_contains": "<jackson-bom.version>2.21.5</jackson-bom.version>" in pom_text,
"jackson_index": pom_text.find("<artifactId>jackson-bom</artifactId>"),
"spring_boot_index": pom_text.find("<artifactId>spring-boot-dependencies</artifactId>"),
})
print("parsed jackson import:", next((d for d in imports if d["artifactId"] == "jackson-bom"), None))
print("parsed spring boot import:", next((d for d in imports if d["artifactId"] == "spring-boot-dependencies"), None))
print("test uses XML parser:", "XML" in test or "DocumentBuilder" in test or "MavenXpp3Reader" in test)
print("test resolves dependency graph:", any(x in test for x in ("dependency:tree", "effective-pom", "jackson-databind")))
counterexample = """<project>
<properties>
<!-- <jackson-bom.version>2.21.5</jackson-bom.version> -->
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<artifactId>unrelated-jackson-bom</artifactId>
</dependency>
<dependency>
<artifactId>spring-boot-dependencies</artifactId>
</dependency>
</dependencies>
</dependencyManagement>
</project>"""
print("counterexample raw checks:", {
"version_contains": "<jackson-bom.version>2.21.5</jackson-bom.version>" in counterexample,
"jackson_index": counterexample.find("<artifactId>jackson-bom</artifactId>"),
"spring_boot_index": counterexample.find("<artifactId>spring-boot-dependencies</artifactId>"),
})
PYRepository: ContextualWisdomLab/mightyETL Length of output: 3322 🏁 Script executed: #!/bin/bash
set -eu
python3 - <<'PY'
from pathlib import Path
source = Path("etl-service/src/test/java/com/xtrmetl/etl/documentation/JacksonSecurityBaselineTest.java").read_text()
fake_pom = """<project>
<!--
<jackson-bom.version>2.21.5</jackson-bom.version>
<artifactId>jackson-bom</artifactId>
-->
<dependencyManagement>
<dependencies>
<!-- The marker below is not a dependency-management entry. -->
<dependency>
<artifactId>spring-boot-dependencies</artifactId>
</dependency>
</dependencies>
</dependencyManagement>
</project>"""
predicates = [
"<jackson-bom.version>2.21.5</jackson-bom.version>" in fake_pom,
fake_pom.find("<artifactId>jackson-bom</artifactId>") >= 0,
fake_pom.find("<artifactId>spring-boot-dependencies</artifactId>") >= 0,
fake_pom.find("<artifactId>jackson-bom</artifactId>") < fake_pom.find("<artifactId>spring-boot-dependencies</artifactId>"),
]
print("all raw test predicates pass:", all(predicates))
print("fake POM has parsed Jackson dependency-management import:", False)
print("test source contains XML parser/model:", any(x in source for x in ("DocumentBuilder", "MavenXpp3Reader", "XmlPullParser")))
print("test source checks resolved jackson-databind version:", "jackson-databind" in source)
PYRepository: ContextualWisdomLab/mightyETL Length of output: 371 XML 구조와 해석된 의존성으로 Jackson 보안 계약을 검사하세요.
🤖 Prompt for AI AgentsSource: Coding guidelines |
||
| ); | ||
| } | ||
|
|
||
| /** Finds the repository root from root- or module-scoped Maven execution. */ | ||
| private static Path projectRoot() { | ||
| Path current = Paths.get(System.getProperty("user.dir")).toAbsolutePath(); | ||
| Path lastPomParent = null; | ||
| while (current != null) { | ||
| if (Files.exists(current.resolve(".git"))) { | ||
| return current; | ||
| } | ||
| if (Files.exists(current.resolve("pom.xml"))) { | ||
| lastPomParent = current; | ||
| } | ||
| current = current.getParent(); | ||
| } | ||
| if (lastPomParent != null) { | ||
| return lastPomParent; | ||
| } | ||
| throw new IllegalStateException("Could not find project root"); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: ContextualWisdomLab/mightyETL
Length of output: 15161
🏁 Script executed:
Repository: ContextualWisdomLab/mightyETL
Length of output: 4459
🌐 Web query:
"GHSA-mhm7-754m-9p8w" "CVE-2026-59889" Jackson💡 Result:
GHSA-mhm7-754m-9p8w and CVE-2026-59889 are two distinct security vulnerabilities affecting the Jackson Databind library, both related to the bypass of
@JsonViewannotations [1][2][3]. CVE-2026-59889 CVE-2026-59889 is an authorization-bypass vulnerability where the@JsonViewannotation is bypassed for@JsonUnwrappedproperties [2][3][4]. Specifically, the UnwrappedPropertyHandler fails to enforce visibility checks when replaying buffered JSON, allowing an attacker to write to properties that should have been restricted by the active view [3][5][4]. This issue is fixed in Jackson Databind versions 2.18.9, 2.21.5, 2.22.1, 3.1.5, and 3.2.1 [2][3][5]. GHSA-mhm7-754m-9p8w GHSA-mhm7-754m-9p8w describes a separate@JsonViewbypass vulnerability involving creator properties (e.g., constructor parameters) combined with@JsonTypeInfo(include=As.EXTERNAL_PROPERTY) [1][6][7]. The deserializer failed to apply view-based filtering in the creator-property branch, which allowed unauthorized modification of restricted creator parameters [1][6]. This is categorized as a "patch gap" because while fixes were initially applied to the 3.x line, they were not correctly backported to earlier versions [1][6][7]. This was addressed in the same release cycle as CVE-2026-59889 (e.g., 2.18.9, 2.21.5, 2.22.1) [8][9]. In summary, both identifiers represent critical@JsonViewbypass flaws that allow unauthorized mass-assignment of sensitive fields, and both were addressed in the July 2026 release of Jackson Databind versions [2][3]. Users are advised to upgrade to the latest versions (2.18.9, 2.21.5, 2.22.1, 3.1.5, 3.2.1 or newer) to mitigate these risks [2][3][5].Citations:
🏁 Script executed:
Repository: ContextualWisdomLab/mightyETL
Length of output: 32751
참고문헌에 누락된 advisory 식별자를 추가하세요.
Trigger의
CVE-2026-59889와GHSA-mhm7-754m-9p8w를 별도 참고문헌으로 추가하세요.CVE-2026-59889의 FasterXML advisory는GHSA-5gvw-p9qm-jgwh이며,GHSA-mhm7-754m-9p8w와는 별도 취약점입니다.🤖 Prompt for AI Agents
Source: Coding guidelines