What Happened
Spring Boot version 2.1.7 shipped with jackson-databind 2.9.9, a JSON parsing library containing two high-severity deserialization vulnerabilities (CVE-2019-14379 and CVE-2019-14439). These vulnerabilities allowed attackers to execute arbitrary code by sending malicious JSON payloads to applications that deserialize untrusted data. Although the jackson-databind maintainers released version 2.9.9.3 with a fix, applications using Spring Boot 2.1.7 remained vulnerable until teams explicitly identified and upgraded the transitive dependency.
This wasn't a zero-day. The vulnerabilities were public, documented, and fixed. Yet many production applications remained exposed because their dependency trees buried jackson-databind several layers deep, invisible to teams that only tracked direct dependencies.
Timeline
- Pre-July 2019: jackson-databind versions through 2.9.9.2 contain exploitable deserialization flaws.
- July 2019: CVE-2019-14379 and CVE-2019-14439 published as high-severity vulnerabilities.
- July 23, 2019: jackson-databind 2.9.9.3 released with patches.
- July 30, 2019: Spring Boot 2.1.7 released, still depending on vulnerable jackson-databind 2.9.9.
- Post-release: Applications using Spring Boot 2.1.7 remain vulnerable until teams discover the transitive dependency issue and force an upgrade.
The gap between the jackson-databind fix and Spring Boot's dependency update created a window where using the latest stable Spring Boot release actually introduced a known vulnerability into your application.
Which Controls Failed or Were Missing
Dependency visibility: Teams tracking only their pom.xml or build.gradle direct dependencies had no visibility into jackson-databind. Spring Boot pulled it in transitively through spring-boot-starter-web, which depends on spring-web, which depends on jackson-databind. Your dependency tree looked clean while shipping RCE vulnerabilities to production.
Vulnerability scanning cadence: Organizations running scans only at build time or during major releases missed this entirely. The vulnerability existed in a stable, released version of Spring Boot that had already passed your pipeline checks.
Transitive dependency policies: Most teams had no policy for handling vulnerabilities in indirect dependencies. When jackson-databind 2.9.9.3 shipped, there was no automated process to force-upgrade it within the Spring Boot dependency tree.
Deserialization input validation: Applications accepting and deserializing JSON from untrusted sources without type whitelisting or validation provided the attack surface. Even with a patched library, this architectural pattern remains risky.
What the Relevant Standards Require
PCI DSS v4.0.1 Requirement 6.3.2 mandates that you identify security vulnerabilities using reputable sources and assign a risk ranking to newly discovered vulnerabilities. The failure mode here is the "identify" step. You can't identify what you can't see, and transitive dependencies are invisible without tooling.
OWASP Top 10 2021 A06:2021 – Vulnerable and Outdated Components directly addresses this scenario. The guidance explicitly calls out the risk of not knowing the versions of all components you use, both client-side and server-side, including nested dependencies. It's not enough to track Spring Boot 2.1.7. You need visibility into every library Spring Boot brings along.
ISO/IEC 27001:2022 Control 8.8 requires you to manage technical vulnerabilities. The control specifies obtaining timely information about technical vulnerabilities, evaluating exposure, and taking appropriate action. "Timely" matters here. The jackson-databind fix shipped on July 23. Spring Boot 2.1.7 shipped on July 30 with the old version. Your vulnerability scanning needs to catch that seven-day gap.
OWASP ASVS v4.0.3 Section 5.5 covers deserialization specifically. Requirement 5.5.1 states that you must verify the application deserializes untrusted data safely or not at all. This is defense in depth. Even if you patch jackson-databind, you shouldn't blindly deserialize JSON from untrusted sources without type restrictions.
Lessons and Action Items for Your Team
Map your entire dependency tree. Run mvn dependency:tree or gradle dependencies and pipe the output to a file. Review it. You'll find libraries you've never heard of. Those libraries have vulnerabilities too. Make this part of your quarterly security reviews.
Scan dependencies on every build. Integrate dependency checking into your CI pipeline. Tools that can help include OWASP Dependency-Check, Snyk, or GitHub's Dependabot. Configure them to fail builds on high-severity findings. Yes, this will break your build. That's the point.
Create override policies for transitive dependencies. Learn how to force dependency versions in your build tool. In Maven, use <dependencyManagement>. In Gradle, use resolutionStrategy. When jackson-databind 2.9.9.3 ships and Spring Boot hasn't caught up yet, you need the ability to force the upgrade yourself.
Implement deserialization controls at the application layer. Don't rely on library patches alone. If you're using jackson-databind, configure it with enableDefaultTyping() disabled and maintain an explicit whitelist of allowed classes for polymorphic deserialization. Better yet, avoid deserializing untrusted data entirely.
Define SLAs for transitive dependency patching. Your policy might say "patch direct dependencies within 30 days of a high-severity CVE." You need a parallel policy for indirect dependencies: "Scan transitive dependencies weekly. Force-upgrade or isolate high-severity findings within 14 days, regardless of upstream framework release schedules."
Test your dependency upgrades. When you force jackson-databind to 2.9.9.3 while running Spring Boot 2.1.7, you're creating an untested configuration. Run your integration tests. Check for breaking changes. This is why you can't wait until the next major release -- you need the testing capacity to validate emergency dependency upgrades.
The Spring Boot and jackson-databind scenario repeats constantly across the ecosystem. Log4j. Spring Framework. Apache Commons. The pattern is always the same: a vulnerability ships in a widely-used library, a patch releases, and thousands of applications remain vulnerable because teams only see the top layer of their dependency stack. Your controls need to see the whole tree.
CVE Details



