Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package jackson-databind for
openSUSE:Factory checked in at 2026-07-15 16:56:19
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/jackson-databind (Old)
and /work/SRC/openSUSE:Factory/.jackson-databind.new.1991 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "jackson-databind"
Wed Jul 15 16:56:19 2026 rev:21 rq:1365955 version:2.18.9
Changes:
--------
--- /work/SRC/openSUSE:Factory/jackson-databind/jackson-databind.changes
2026-06-27 18:09:48.295500083 +0200
+++
/work/SRC/openSUSE:Factory/.jackson-databind.new.1991/jackson-databind.changes
2026-07-15 17:12:20.569053071 +0200
@@ -1,0 +2,17 @@
+Wed Jul 15 10:52:47 UTC 2026 - Fridrich Strba <[email protected]>
+
+- Update to 2.18.8
+ * Changes
+ + #5962: Case-insensitive deserialization may use wrong
+ '@JsonIgnoreProperties' (bsc#1268902, CVE-2026-54515)
+ + #6054: Honor '@JsonView' for external-type-id
+ ('EXTERNAL_PROPERTY') properties [GHSA-mhm7-754m-9p8w]
+ + #6058: Do not allow DNS resolution when deserializing
+ 'InetAddress'
+ + #6060: '@JsonView' by-passed for '@JsonUnwrapped'
+ Field/Setter properties (bsc#1271440, CVE-2026-59889)
+- Removed patch
+ * jackson-databind-CVE-2026-54515.patch
+ + integrated in this version
+
+-------------------------------------------------------------------
@@ -13 +30 @@
- 'PropertyNamingStrategy'
+ 'PropertyNamingStrategy' (bsc#1271435, CVE-2026-59888)
@@ -57 +74,2 @@
- + #4787: Wrong 'String.format()' in 'StdDelegatingDeserializer'
+ + #4787: Wrong 'String.format()' in 'StdDelegatingDeserializer' *
jackson-databind-CVE-2026-54515.patch
+
Old:
----
jackson-databind-2.18.8.tar.gz
jackson-databind-CVE-2026-54515.patch
New:
----
jackson-databind-2.18.9.tar.gz
----------(Old B)----------
Old:- Removed patch
* jackson-databind-CVE-2026-54515.patch
+ integrated in this version
----------(Old E)----------
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ jackson-databind.spec ++++++
--- /var/tmp/diff_new_pack.HFgb1e/_old 2026-07-15 17:12:21.129072348 +0200
+++ /var/tmp/diff_new_pack.HFgb1e/_new 2026-07-15 17:12:21.129072348 +0200
@@ -20,14 +20,13 @@
# binaries are java 8 compatible
%define __requires_exclude java-headless
Name: jackson-databind
-Version: 2.18.8
+Version: 2.18.9
Release: 0
Summary: General data-binding package for Jackson (2.x)
License: Apache-2.0
URL: https://github.com/FasterXML/jackson-databind/
Source0:
https://github.com/FasterXML/jackson-databind/archive/%{name}-%{version}.tar.gz
Source1: %{name}-build.xml
-Patch0: %{name}-CVE-2026-54515.patch
BuildRequires: ant
BuildRequires: fdupes
BuildRequires: jackson-annotations
@@ -51,7 +50,6 @@
%prep
%setup -q -n %{name}-%{name}-%{version}
cp %{SOURCE1} build.xml
-%patch -P 0 -p1
mkdir -p lib
cp -p src/main/resources/META-INF/NOTICE .
++++++ _scmsync.obsinfo ++++++
--- /var/tmp/diff_new_pack.HFgb1e/_old 2026-07-15 17:12:21.161073449 +0200
+++ /var/tmp/diff_new_pack.HFgb1e/_new 2026-07-15 17:12:21.165073587 +0200
@@ -1,6 +1,6 @@
-mtime: 1782492145
-commit: 44f91217e3255f9588452ecc3f12d95b60d8920ab601e4947d5b83e8b62a1d00
+mtime: 1784114255
+commit: ccc864724b1ee59fbaf8e3346c1f8fa57fb0d8b7fea1f9b073905a98567985fa
url: https://src.opensuse.org/java-packages/jackson-databind
-revision: 44f91217e3255f9588452ecc3f12d95b60d8920ab601e4947d5b83e8b62a1d00
+revision: ccc864724b1ee59fbaf8e3346c1f8fa57fb0d8b7fea1f9b073905a98567985fa
projectscmsync: https://src.opensuse.org/java-packages/_ObsPrj
++++++ build.specials.obscpio ++++++
++++++ build.specials.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/.gitignore new/.gitignore
--- old/.gitignore 1970-01-01 01:00:00.000000000 +0100
+++ new/.gitignore 2026-07-15 13:17:35.000000000 +0200
@@ -0,0 +1 @@
+.osc
++++++ jackson-databind-2.18.8.tar.gz -> jackson-databind-2.18.9.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/jackson-databind-jackson-databind-2.18.8/pom.xml
new/jackson-databind-jackson-databind-2.18.9/pom.xml
--- old/jackson-databind-jackson-databind-2.18.8/pom.xml 2026-05-29
02:39:50.000000000 +0200
+++ new/jackson-databind-jackson-databind-2.18.9/pom.xml 2026-07-08
02:18:37.000000000 +0200
@@ -9,11 +9,11 @@
<parent>
<groupId>com.fasterxml.jackson</groupId>
<artifactId>jackson-base</artifactId>
- <version>2.18.8</version>
+ <version>2.18.9</version>
</parent>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
- <version>2.18.8</version>
+ <version>2.18.9</version>
<name>jackson-databind</name>
<packaging>jar</packaging>
<description>General data-binding functionality for Jackson: works on core
streaming API</description>
@@ -31,7 +31,7 @@
<connection>scm:git:[email protected]:FasterXML/jackson-databind.git</connection>
<developerConnection>scm:git:[email protected]:FasterXML/jackson-databind.git</developerConnection>
<url>https://github.com/FasterXML/jackson-databind</url>
- <tag>jackson-databind-2.18.8</tag>
+ <tag>jackson-databind-2.18.9</tag>
</scm>
<properties>
@@ -68,7 +68,7 @@
<packageVersion.package>com.fasterxml.jackson.databind.cfg</packageVersion.package>
<!-- for Reproducible Builds -->
-
<project.build.outputTimestamp>2026-05-29T00:39:06Z</project.build.outputTimestamp>
+
<project.build.outputTimestamp>2026-07-08T00:18:17Z</project.build.outputTimestamp>
</properties>
<dependencyManagement>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/jackson-databind-jackson-databind-2.18.8/release-notes/CREDITS-2.x
new/jackson-databind-jackson-databind-2.18.9/release-notes/CREDITS-2.x
--- old/jackson-databind-jackson-databind-2.18.8/release-notes/CREDITS-2.x
2026-05-29 02:39:50.000000000 +0200
+++ new/jackson-databind-jackson-databind-2.18.9/release-notes/CREDITS-2.x
2026-07-08 02:18:37.000000000 +0200
@@ -1895,7 +1895,7 @@
(2.18.4)
Omkhar Arasaratnam (@omkhar)
- * Reported #5950: Improve `UUIDeserializer` error handling
+ * Reported #5950: Improve `UUIDDeserializer` error handling
(2.18.8)
* Reported #5951: Improve `InetSocketAddress` deserialization
(2.18.8)
@@ -1910,4 +1910,7 @@
should validate element type
(2.18.8)
* Reported #5988: `PolymorphicTypeValidator` needs to validate generic type
parameters too
- (2.18.8)
+ (2.18.8)
+ * Contributed fix for #5962: Case-insensitive deserialization may use
+ wrong `@JsonIgnoreProperties`
+ (2.18.9)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/jackson-databind-jackson-databind-2.18.8/release-notes/VERSION-2.x
new/jackson-databind-jackson-databind-2.18.9/release-notes/VERSION-2.x
--- old/jackson-databind-jackson-databind-2.18.8/release-notes/VERSION-2.x
2026-05-29 02:39:50.000000000 +0200
+++ new/jackson-databind-jackson-databind-2.18.9/release-notes/VERSION-2.x
2026-07-08 02:18:37.000000000 +0200
@@ -4,23 +4,38 @@
=== Releases ===
------------------------------------------------------------------------
+2.18.9 (07-Jul-2026)
+
+#5962: Case-insensitive deserialization may use wrong `@JsonIgnoreProperties`
+ [CVE-2026-54515]
+ (fixed by Omkhar A)
+#6054: Honor `@JsonView` for external-type-id (`EXTERNAL_PROPERTY`)
+ properties [GHSA-mhm7-754m-9p8w]
+ (reported by @Sharlong-Wen)
+#6058: Do not allow DNS resolution when deserializing `InetAddress`
+ (reported by @thientd)
+ (fix by @pjfanning)
+#6060: `@JsonView` by-passed for `@JsonUnwrapped` Field/Setter properties
[CVE-2026-59889]
+
2.18.8 (28-May-2026)
-#5950: Improve `UUIDeserializer` error handling
+#5950: Improve `UUIDDeserializer` error handling
(reported by Omkhar A)
-#5951: Improve `InetSocketAddress` deserialization
+#5951: Improve `InetSocketAddress` deserialization [CVE-2026-54514]
(reported by Omkhar A)
#5969: `@JsonView` by-passed for some "setterless" creator properties
+ [CVE-2026-54517]
(fixed by Omkhar A)
-#5971: `@JsonView` by-passed for unwrapped creator parameters
+#5971: `@JsonView` by-passed for unwrapped creator parameters [CVE-2026-54518]
(fixed by Omkhar A)
#5974: `@JsonIgnore` on Record property ignored with `PropertyNamingStrategy`
(reported by Omkhar A)
#5981: `BasicPolymorphicTypeValidator` setting `allowIfSubTypeIsArray()`
- should validate element type
+ should validate element type [CVE-2026-54513]
(reported by Omkhar A)
(fix by @cowtowncoder, w/ Claude code)
#5988: `PolymorphicTypeValidator` needs to validate generic type parameters too
+ [CVE-2026-54512]
(reported by Omkhar A)
(fix by @cowtowncoder, w/ Claude code)
#5993: `UPPER_SNAKE_CASE` / `LOWER_CASE` `NamingStrategyImpls` fold case
@@ -638,7 +653,7 @@
JSON `null` values on reading
#3443: Do not strip generic type from `Class<C>` when resolving `JavaType`
(contributed by Jan J)
-#3447: Deeply nested JsonNode throws StackOverflowError for toString()
+#3447: Deeply nested JsonNode throws StackOverflowError for toString()
[CVE-2026-50193]
(reported by Deniz H)
#3475: Support use of fast double parse
(contributed by @pjfanning)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/jackson-databind-jackson-databind-2.18.8/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java
new/jackson-databind-jackson-databind-2.18.9/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java
---
old/jackson-databind-jackson-databind-2.18.8/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java
2026-05-29 02:39:50.000000000 +0200
+++
new/jackson-databind-jackson-databind-2.18.9/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java
2026-07-08 02:18:37.000000000 +0200
@@ -894,7 +894,10 @@
// 16-May-2016, tatu: How about per-property case-insensitivity?
Boolean B =
format.getFeature(JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);
if (B != null) {
- BeanPropertyMap propsOrig = _beanProperties;
+ // [databind#5962]: must rebuild from the (possibly filtered)
contextual
+ // BeanPropertyMap so that per-property @JsonIgnoreProperties
exclusions
+ // applied by _handleByNameInclusion() above are preserved.
+ BeanPropertyMap propsOrig = contextual._beanProperties;
BeanPropertyMap props =
propsOrig.withCaseInsensitivity(B.booleanValue());
if (props != propsOrig) {
contextual = contextual.withBeanProperties(props);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/jackson-databind-jackson-databind-2.18.8/src/main/java/com/fasterxml/jackson/databind/deser/impl/ExternalTypeHandler.java
new/jackson-databind-jackson-databind-2.18.9/src/main/java/com/fasterxml/jackson/databind/deser/impl/ExternalTypeHandler.java
---
old/jackson-databind-jackson-databind-2.18.8/src/main/java/com/fasterxml/jackson/databind/deser/impl/ExternalTypeHandler.java
2026-05-29 02:39:50.000000000 +0200
+++
new/jackson-databind-jackson-databind-2.18.9/src/main/java/com/fasterxml/jackson/databind/deser/impl/ExternalTypeHandler.java
2026-07-08 02:18:37.000000000 +0200
@@ -201,9 +201,17 @@
public Object complete(JsonParser p, DeserializationContext ctxt, Object
bean)
throws IOException
{
+ final Class<?> activeView = ctxt.getActiveView();
for (int i = 0, len = _properties.length; i < len; ++i) {
- String typeId = _typeIds[i];
final ExtTypedProperty extProp = _properties[i];
+ // 26-Jun-2026, tatu: Must honor `@JsonView` for external-type-id
+ // properties too (see JsonViewExternalTypeIdBypassTest);
otherwise
+ // value for property not visible in active view would still get
bound,
+ // bypassing view-based filtering.
+ if ((activeView != null) &&
!extProp.getProperty().visibleInView(activeView)) {
+ continue;
+ }
+ String typeId = _typeIds[i];
if (typeId == null) {
TokenBuffer tokens = _tokens[i];
// let's allow missing both type and property (may already
have been set, too)
@@ -264,53 +272,71 @@
// first things first: deserialize all data buffered:
final int len = _properties.length;
Object[] values = new Object[len];
+ final Class<?> activeView = ctxt.getActiveView();
for (int i = 0; i < len; ++i) {
- String typeId = _typeIds[i];
final ExtTypedProperty extProp = _properties[i];
- if (typeId == null) {
- // let's allow missing both type and property (may already
have been set, too)
- TokenBuffer tb = _tokens[i];
- if ((tb == null)
- // 19-Feb-2021, tatu: Both missing value and explicit
`null`
- // should be accepted...
- || (tb.firstToken() == JsonToken.VALUE_NULL)
- ) {
- continue;
- }
- // but not just one
- // 26-Oct-2012, tatu: As per [databind#94], must allow use of
'defaultImpl'
- if (!extProp.hasDefaultType()) {
- ctxt.reportPropertyInputMismatch(_beanType,
extProp.getProperty().getName(),
- "Missing external type id property '%s'",
- extProp.getTypePropertyName());
- } else {
- typeId = extProp.getDefaultTypeId();
+ final SettableBeanProperty prop = extProp.getProperty();
+ String typeId = _typeIds[i];
+
+ // 26-Jun-2026, tatu: Must honor `@JsonView` for external-type-id
+ // properties too (see JsonViewExternalTypeIdBypassTest): leave
the
+ // value of a property not visible in the active view unbound
(null),
+ // so it does not bypass view-based filtering. NOTE: the
external type
+ // id itself may be a separate, still-visible creator property,
so that
+ // is still assigned below ([databind#999]).
+ final boolean viewHidden = (activeView != null) &&
!prop.visibleInView(activeView);
+
+ if (!viewHidden) {
+ if (typeId == null) {
+ // let's allow missing both type and property (may already
have been set, too)
+ TokenBuffer tb = _tokens[i];
+ if ((tb == null)
+ // 19-Feb-2021, tatu: Both missing value and
explicit `null`
+ // should be accepted...
+ || (tb.firstToken() == JsonToken.VALUE_NULL)
+ ) {
+ continue;
+ }
+ // but not just one
+ // 26-Oct-2012, tatu: As per [databind#94], must allow use
of 'defaultImpl'
+ if (!extProp.hasDefaultType()) {
+ ctxt.reportPropertyInputMismatch(_beanType,
prop.getName(),
+ "Missing external type id property '%s'",
+ extProp.getTypePropertyName());
+ } else {
+ typeId = extProp.getDefaultTypeId();
+ }
}
- }
- if (_tokens[i] != null) {
- values[i] = _deserialize(p, ctxt, i, typeId);
- } else {
- if
(ctxt.isEnabled(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY))
{
- SettableBeanProperty prop = extProp.getProperty();
- ctxt.reportPropertyInputMismatch(_beanType, prop.getName(),
- "Missing property '%s' for external type id '%s'",
- prop.getName(),
_properties[i].getTypePropertyName());
+ if (_tokens[i] != null) {
+ values[i] = _deserialize(p, ctxt, i, typeId);
+ } else {
+ if
(ctxt.isEnabled(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY))
{
+ ctxt.reportPropertyInputMismatch(_beanType,
prop.getName(),
+ "Missing property '%s' for external type id
'%s'",
+ prop.getName(), extProp.getTypePropertyName());
+ }
+ // 03-Aug-2022, tatu: [databind#3533] to handle absent
value matching
+ // present type id
+ values[i] = _deserializeMissingToken(p, ctxt, i, typeId);
}
- // 03-Aug-2022, tatu: [databind#3533] to handle absent value
matching
- // present type id
- values[i] = _deserializeMissingToken(p, ctxt, i, typeId);
}
- final SettableBeanProperty prop = extProp.getProperty();
// also: if it's creator prop, fill in
if (prop.getCreatorIndex() >= 0) {
- buffer.assignParameter(prop, values[i]);
+ // ...but skip binding the value itself if hidden by the
active view
+ if (!viewHidden) {
+ buffer.assignParameter(prop, values[i]);
+ }
// [databind#999] And maybe there's creator property for type
id too?
+ // Assign it even when the value is view-hidden, provided we
have a
+ // type id and the type-id property is itself visible in the
view.
SettableBeanProperty typeProp = extProp.getTypeProperty();
// for now, should only be needed for creator properties, too
- if ((typeProp != null) && (typeProp.getCreatorIndex() >= 0)) {
+ if ((typeProp != null) && (typeProp.getCreatorIndex() >= 0)
+ && (typeId != null)
+ && ((activeView == null) ||
typeProp.visibleInView(activeView))) {
// 31-May-2018, tatu: [databind#1328] if id is NOT plain
`String`, need to
// apply deserializer... fun fun.
final Object v;
@@ -329,7 +355,12 @@
Object bean = creator.build(ctxt, buffer);
// third: assign non-creator properties
for (int i = 0; i < len; ++i) {
- SettableBeanProperty prop = _properties[i].getProperty();
+ final ExtTypedProperty extProp = _properties[i];
+ // 26-Jun-2026, tatu: also honor `@JsonView` here (see above)
+ final SettableBeanProperty prop = extProp.getProperty();
+ if ((activeView != null) && !prop.visibleInView(activeView)) {
+ continue;
+ }
if (prop.getCreatorIndex() < 0) {
prop.set(bean, values[i]);
}
@@ -379,6 +410,17 @@
protected final void _deserializeAndSet(JsonParser p,
DeserializationContext ctxt,
Object bean, int index, String typeId) throws IOException
{
+ // 26-Jun-2026, tatu: Must honor `@JsonView` here too (see
+ // JsonViewExternalTypeIdBypassTest): for default-constructor beans
the value
+ // may be bound eagerly via this method (when the type id precedes
the value),
+ // so a property not visible in the active view must be skipped here
as well.
+ // The value is read from the buffered token (`_tokens[index]`), and
the main
+ // parser has already advanced past it, so we simply discard it (do
not set).
+ final Class<?> activeView = ctxt.getActiveView();
+ if ((activeView != null) &&
!_properties[index].getProperty().visibleInView(activeView)) {
+ return;
+ }
+
// 11-Nov-2020, tatu: Should never get `null` passed this far,
if (typeId == null) {
ctxt.reportInputMismatch(_beanType, "Internal error in external
Type Id handling: `null` type id passed");
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/jackson-databind-jackson-databind-2.18.8/src/main/java/com/fasterxml/jackson/databind/deser/impl/UnwrappedPropertyHandler.java
new/jackson-databind-jackson-databind-2.18.9/src/main/java/com/fasterxml/jackson/databind/deser/impl/UnwrappedPropertyHandler.java
---
old/jackson-databind-jackson-databind-2.18.8/src/main/java/com/fasterxml/jackson/databind/deser/impl/UnwrappedPropertyHandler.java
2026-05-29 02:39:50.000000000 +0200
+++
new/jackson-databind-jackson-databind-2.18.9/src/main/java/com/fasterxml/jackson/databind/deser/impl/UnwrappedPropertyHandler.java
2026-07-08 02:18:37.000000000 +0200
@@ -56,8 +56,14 @@
Object bean, TokenBuffer buffered)
throws IOException
{
+ // [databind#6060]: honor active @JsonView -- skip Field/Setter
properties not
+ // visible in the active view rather than populating them from
buffered input.
+ final Class<?> activeView = ctxt.getActiveView();
for (int i = 0, len = _properties.size(); i < len; ++i) {
SettableBeanProperty prop = _properties.get(i);
+ if ((activeView != null) && !prop.visibleInView(activeView)) {
+ continue;
+ }
JsonParser p =
buffered.asParser(originalParser.streamReadConstraints());
p.nextToken();
prop.deserializeAndSet(p, ctxt, bean);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/jackson-databind-jackson-databind-2.18.8/src/main/java/com/fasterxml/jackson/databind/deser/std/FromStringDeserializer.java
new/jackson-databind-jackson-databind-2.18.9/src/main/java/com/fasterxml/jackson/databind/deser/std/FromStringDeserializer.java
---
old/jackson-databind-jackson-databind-2.18.8/src/main/java/com/fasterxml/jackson/databind/deser/std/FromStringDeserializer.java
2026-05-29 02:39:50.000000000 +0200
+++
new/jackson-databind-jackson-databind-2.18.9/src/main/java/com/fasterxml/jackson/databind/deser/std/FromStringDeserializer.java
2026-07-08 02:18:37.000000000 +0200
@@ -351,6 +351,11 @@
case STD_TIME_ZONE:
return TimeZone.getTimeZone(value);
case STD_INET_ADDRESS:
+ // 05-Jul-2026, tatu: [databind#6058] Prevent DNS lookup: only
accept valid IP address literals
+ if (!InetAddressValidator.isInetAddress(value)) {
+ return ctxt.handleWeirdStringValue(_valueClass, value,
+ "Not a valid IP address string literal");
+ }
return InetAddress.getByName(value);
case STD_INET_SOCKET_ADDRESS:
if (value.startsWith("[")) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/jackson-databind-jackson-databind-2.18.8/src/main/java/com/fasterxml/jackson/databind/deser/std/InetAddressValidator.java
new/jackson-databind-jackson-databind-2.18.9/src/main/java/com/fasterxml/jackson/databind/deser/std/InetAddressValidator.java
---
old/jackson-databind-jackson-databind-2.18.8/src/main/java/com/fasterxml/jackson/databind/deser/std/InetAddressValidator.java
1970-01-01 01:00:00.000000000 +0100
+++
new/jackson-databind-jackson-databind-2.18.9/src/main/java/com/fasterxml/jackson/databind/deser/std/InetAddressValidator.java
2026-07-08 02:18:37.000000000 +0200
@@ -0,0 +1,218 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Helper that validates whether a string is a valid IP address literal (IPv4
or IPv6)
+ * without performing any DNS lookup.
+ *<p>
+ * Logic is adapted from Google Guava's {@code InetAddresses.isInetAddress()}
under the
+ * Apache License 2.0:
+ *
https://github.com/google/guava/blob/master/guava/src/com/google/common/net/InetAddresses.java
+ *
+ * @since 2.18.9
+ */
+class InetAddressValidator
+{
+ private static final int IPV4_PART_COUNT = 4;
+ private static final int IPV6_PART_COUNT = 8;
+
+ private InetAddressValidator() { }
+
+ /**
+ * Returns {@code true} if the supplied string is a valid IP string literal
+ * (IPv4 or IPv6), {@code false} otherwise.
+ * This method never causes a DNS lookup.
+ */
+ static boolean isInetAddress(String ipString) {
+ return _ipStringToBytes(ipString) != null;
+ }
+
+ // Returns null if unable to parse into a byte array.
+ private static byte[] _ipStringToBytes(String ipString) {
+ boolean hasColon = false;
+ boolean hasDot = false;
+ int percentIndex = -1;
+ for (int i = 0; i < ipString.length(); i++) {
+ char c = ipString.charAt(i);
+ if (c == '.') {
+ hasDot = true;
+ } else if (c == ':') {
+ if (hasDot) {
+ return null; // Colons must not appear after dots.
+ }
+ hasColon = true;
+ } else if (c == '%') {
+ percentIndex = i;
+ break; // Scope ID; stop scanning
+ } else if (Character.digit(c, 16) == -1) {
+ return null; // Everything else must be a decimal or hex digit.
+ }
+ }
+
+ if (hasColon) {
+ if (hasDot) {
+ ipString = _convertDottedQuadToHex(ipString);
+ if (ipString == null) {
+ return null;
+ }
+ }
+ if (percentIndex != -1) {
+ ipString = ipString.substring(0, percentIndex);
+ }
+ return _textToNumericFormatV6(ipString);
+ } else if (hasDot) {
+ if (percentIndex != -1) {
+ return null; // Scope IDs not supported for IPv4
+ }
+ return _textToNumericFormatV4(ipString);
+ }
+ return null;
+ }
+
+ private static byte[] _textToNumericFormatV4(String ipString) {
+ int dotCount = 0;
+ for (int i = 0; i < ipString.length(); i++) {
+ if (ipString.charAt(i) == '.') {
+ dotCount++;
+ }
+ }
+ if (dotCount + 1 != IPV4_PART_COUNT) {
+ return null; // Wrong number of parts
+ }
+
+ byte[] bytes = new byte[IPV4_PART_COUNT];
+ int start = 0;
+ for (int i = 0; i < IPV4_PART_COUNT; i++) {
+ int end = ipString.indexOf('.', start);
+ if (end == -1) {
+ end = ipString.length();
+ }
+ try {
+ bytes[i] = _parseOctet(ipString, start, end);
+ } catch (NumberFormatException ex) {
+ return null;
+ }
+ start = end + 1;
+ }
+ return bytes;
+ }
+
+ private static byte[] _textToNumericFormatV6(String ipString) {
+ int delimiterCount = 0;
+ for (int i = 0; i < ipString.length(); i++) {
+ if (ipString.charAt(i) == ':') {
+ delimiterCount++;
+ }
+ }
+ if (delimiterCount < 2 || delimiterCount > IPV6_PART_COUNT) {
+ return null;
+ }
+ int partsSkipped = IPV6_PART_COUNT - (delimiterCount + 1); //
estimate; may be modified
+ boolean hasSkip = false;
+ for (int i = 0; i < ipString.length() - 1; i++) {
+ if (ipString.charAt(i) == ':' && ipString.charAt(i + 1) == ':') {
+ if (hasSkip) {
+ return null; // Can't have more than one ::
+ }
+ hasSkip = true;
+ partsSkipped++; // :: means we skipped an extra part
+ if (i == 0) {
+ partsSkipped++; // Begins with ::
+ }
+ if (i == ipString.length() - 2) {
+ partsSkipped++; // Ends with ::
+ }
+ }
+ }
+ if (ipString.charAt(0) == ':' && ipString.charAt(1) != ':') {
+ return null; // ^: requires ^::
+ }
+ if (ipString.charAt(ipString.length() - 1) == ':'
+ && ipString.charAt(ipString.length() - 2) != ':') {
+ return null; // :$ requires ::$
+ }
+ if (hasSkip && partsSkipped <= 0) {
+ return null; // :: must expand to at least one '0'
+ }
+ if (!hasSkip && delimiterCount + 1 != IPV6_PART_COUNT) {
+ return null; // Incorrect number of parts
+ }
+
+ ByteBuffer rawBytes = ByteBuffer.allocate(2 * IPV6_PART_COUNT);
+ try {
+ int start = 0;
+ if (ipString.charAt(0) == ':') {
+ start = 1;
+ }
+ while (start < ipString.length()) {
+ int end = ipString.indexOf(':', start);
+ if (end == -1) {
+ end = ipString.length();
+ }
+ if (ipString.charAt(start) == ':') {
+ // expand zeroes
+ for (int i = 0; i < partsSkipped; i++) {
+ rawBytes.putShort((short) 0);
+ }
+ } else {
+ rawBytes.putShort(_parseHextet(ipString, start, end));
+ }
+ start = end + 1;
+ }
+ } catch (NumberFormatException ex) {
+ return null;
+ }
+ return rawBytes.array();
+ }
+
+ private static String _convertDottedQuadToHex(String ipString) {
+ int lastColon = ipString.lastIndexOf(':');
+ String initialPart = ipString.substring(0, lastColon + 1);
+ String dottedQuad = ipString.substring(lastColon + 1);
+ byte[] quad = _textToNumericFormatV4(dottedQuad);
+ if (quad == null) {
+ return null;
+ }
+ String penultimate = Integer.toHexString(((quad[0] & 0xff) << 8) |
(quad[1] & 0xff));
+ String ultimate = Integer.toHexString(((quad[2] & 0xff) << 8) |
(quad[3] & 0xff));
+ return initialPart + penultimate + ":" + ultimate;
+ }
+
+ private static byte _parseOctet(String ipString, int start, int end) {
+ int length = end - start;
+ if (length <= 0 || length > 3) {
+ throw new NumberFormatException();
+ }
+ // Disallow leading zeroes (ambiguous: decimal vs octal)
+ if (length > 1 && ipString.charAt(start) == '0') {
+ throw new NumberFormatException();
+ }
+ int octet = 0;
+ for (int i = start; i < end; i++) {
+ octet *= 10;
+ int digit = Character.digit(ipString.charAt(i), 10);
+ if (digit < 0) {
+ throw new NumberFormatException();
+ }
+ octet += digit;
+ }
+ if (octet > 255) {
+ throw new NumberFormatException();
+ }
+ return (byte) octet;
+ }
+
+ private static short _parseHextet(String ipString, int start, int end) {
+ int length = end - start;
+ if (length <= 0 || length > 4) {
+ throw new NumberFormatException();
+ }
+ int hextet = 0;
+ for (int i = start; i < end; i++) {
+ hextet = hextet << 4;
+ hextet |= Character.digit(ipString.charAt(i), 16);
+ }
+ return (short) hextet;
+ }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/jackson-databind-jackson-databind-2.18.8/src/test/java/com/fasterxml/jackson/databind/deser/filter/IgnorePropertiesCaseInsensitive5962Test.java
new/jackson-databind-jackson-databind-2.18.9/src/test/java/com/fasterxml/jackson/databind/deser/filter/IgnorePropertiesCaseInsensitive5962Test.java
---
old/jackson-databind-jackson-databind-2.18.8/src/test/java/com/fasterxml/jackson/databind/deser/filter/IgnorePropertiesCaseInsensitive5962Test.java
1970-01-01 01:00:00.000000000 +0100
+++
new/jackson-databind-jackson-databind-2.18.9/src/test/java/com/fasterxml/jackson/databind/deser/filter/IgnorePropertiesCaseInsensitive5962Test.java
2026-07-08 02:18:37.000000000 +0200
@@ -0,0 +1,94 @@
+package com.fasterxml.jackson.databind.deser.filter;
+
+import org.junit.jupiter.api.Test;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.testutil.DatabindTestUtil;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * [databind#5962]: Case-insensitive BeanPropertyMap rebuild undoes
per-property
+ * {@code @JsonIgnoreProperties}.
+ *
+ * {@code BeanDeserializerBase.createContextual()} calls {@code
_handleByNameInclusion()}
+ * to filter properties according to per-property {@code
@JsonIgnoreProperties}, producing
+ * a contextual deserializer with the restricted {@code BeanPropertyMap}.
However, the
+ * subsequent case-insensitivity block read {@code _beanProperties} (the
*original*
+ * unfiltered map from {@code this}) rather than {@code
contextual._beanProperties} (the
+ * filtered map). {@code withCaseInsensitivity()} then rebuilt the map from
the unfiltered
+ * source, and {@code contextual.withBeanProperties(props)} overwrote the
filtered map with
+ * the unfiltered one — any properties removed by {@code
_handleByNameInclusion} were
+ * restored.
+ *
+ * Patch: source the case-insensitive rebuild from {@code
contextual._beanProperties}.
+ */
+public class IgnorePropertiesCaseInsensitive5962Test extends DatabindTestUtil
+{
+ static class AdminDto {
+ public String adminKey = "DEFAULT";
+ public String username;
+ }
+
+ // Container that ignores "adminKey" on the AdminDto field AND enables
case-insensitive matching
+ static class Container {
+ @JsonIgnoreProperties("adminKey")
+ @JsonFormat(with =
JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)
+ public AdminDto admin;
+ }
+
+ // Baseline container: only @JsonIgnoreProperties, no case-insensitive
format override
+ static class BaselineContainer {
+ @JsonIgnoreProperties("adminKey")
+ public AdminDto admin;
+ }
+
+ /**
+ * NEGATIVE CONTROL: without the @JsonFormat case-insensitive override,
@JsonIgnoreProperties
+ * correctly suppresses adminKey on the nested AdminDto field.
+ */
+ @Test
+ public void test5962_negativeControl_withoutCaseInsensitivity() throws
Exception {
+ ObjectMapper mapper = jsonMapperBuilder().build();
+ String json =
"{\"admin\":{\"adminKey\":\"HACKED\",\"username\":\"alice\"}}";
+ BaselineContainer result = mapper.readValue(json,
BaselineContainer.class);
+ // Without case-insensitive format, @JsonIgnoreProperties blocks
adminKey
+ assertNotEquals("HACKED", result.admin.adminKey,
+ "@JsonIgnoreProperties alone (no case-insensitive format)
should block adminKey");
+ assertEquals("alice", result.admin.username);
+ }
+
+ /**
+ * EXPLOIT PATH: the case-insensitive BeanPropertyMap rebuild (triggered by
+ * @JsonFormat ACCEPT_CASE_INSENSITIVE_PROPERTIES) restores the unfiltered
original
+ * _beanProperties, undoing the @JsonIgnoreProperties("adminKey")
exclusion.
+ * Case-insensitive matching then routes "adminKey" (or "ADMINKEY") to the
setter.
+ *
+ * Security assertion: adminKey must NOT be settable via JSON when the
enclosing
+ * container declares @JsonIgnoreProperties("adminKey") on the field.
+ */
+ @Test
+ public void test5962_caseInsensitiveRebuildRestoresIgnoredProperty()
throws Exception {
+ ObjectMapper mapper = jsonMapperBuilder()
+ .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
+ .build();
+
+ // Exact case — should be blocked by @JsonIgnoreProperties
+ String json =
"{\"admin\":{\"adminKey\":\"HACKED\",\"username\":\"alice\"}}";
+ Container result = mapper.readValue(json, Container.class);
+ assertNotEquals("HACKED", result.admin.adminKey,
+ "[databind#5962]: case-insensitive BeanPropertyMap rebuild
restored 'adminKey' " +
+ "after it was removed by @JsonIgnoreProperties. The
property was set to 'HACKED'.");
+ assertEquals("alice", result.admin.username);
+
+ // Mixed case — exploits the case-insensitive rebuild more directly
+ String jsonMixed =
"{\"admin\":{\"AdminKey\":\"HACKED2\",\"username\":\"bob\"}}";
+ Container result2 = mapper.readValue(jsonMixed, Container.class);
+ assertNotEquals("HACKED2", result2.admin.adminKey,
+ "[databind#5962]: 'AdminKey' (mixed case) matched 'adminKey'
via case-insensitive " +
+ "lookup that was rebuilt from the unfiltered property
map.");
+ }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/jackson-databind-jackson-databind-2.18.8/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKStringLikeTypeDeserTest.java
new/jackson-databind-jackson-databind-2.18.9/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKStringLikeTypeDeserTest.java
---
old/jackson-databind-jackson-databind-2.18.8/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKStringLikeTypeDeserTest.java
2026-05-29 02:39:50.000000000 +0200
+++
new/jackson-databind-jackson-databind-2.18.9/src/test/java/com/fasterxml/jackson/databind/deser/jdk/JDKStringLikeTypeDeserTest.java
2026-07-08 02:18:37.000000000 +0200
@@ -171,13 +171,39 @@
@Test
public void testInetAddress() throws IOException
{
+ // Valid IPv4 address
InetAddress address = MAPPER.readValue(q("127.0.0.1"),
InetAddress.class);
assertEquals("127.0.0.1", address.getHostAddress());
- // should we try resolving host names? That requires connectivity...
- final String HOST = "google.com";
- address = MAPPER.readValue(q(HOST), InetAddress.class);
- assertEquals(HOST, address.getHostName());
+ // Valid IPv6 address
+ InetAddress ip6 = MAPPER.readValue(
+ q("2001:db8:85a3:8d3:1319:8a2e:370:7348"), InetAddress.class);
+ assertEquals("2001:db8:85a3:8d3:1319:8a2e:370:7348",
ip6.getHostAddress());
+
+ // IPv6 loopback
+ InetAddress loopback6 = MAPPER.readValue(q("::1"), InetAddress.class);
+ assertEquals("0:0:0:0:0:0:0:1", loopback6.getHostAddress());
+ }
+
+ @Test
+ public void testInetAddressNoDNSLookup() throws Exception
+ {
+ // [databind#6058]: should NOT perform DNS lookup — only IP address
literals accepted
+ expectInvalidInetAddress("localhost");
+ expectInvalidInetAddress("google.com");
+ // starts off looking like an IP address (edge case)
+ expectInvalidInetAddress("1.2.3.4.example.com");
+ // punycode
+ expectInvalidInetAddress("xn--bcher-kva.example.com");
+ }
+
+ private void expectInvalidInetAddress(String address) throws Exception {
+ try {
+ MAPPER.readValue(q(address), InetAddress.class);
+ fail("Should not pass");
+ } catch (InvalidFormatException e) {
+ verifyException(e, "Not a valid IP address string literal");
+ }
}
@Test
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/jackson-databind-jackson-databind-2.18.8/src/test/java/com/fasterxml/jackson/databind/ser/jdk/JDKTypeSerializationTest.java
new/jackson-databind-jackson-databind-2.18.9/src/test/java/com/fasterxml/jackson/databind/ser/jdk/JDKTypeSerializationTest.java
---
old/jackson-databind-jackson-databind-2.18.8/src/test/java/com/fasterxml/jackson/databind/ser/jdk/JDKTypeSerializationTest.java
2026-05-29 02:39:50.000000000 +0200
+++
new/jackson-databind-jackson-databind-2.18.9/src/test/java/com/fasterxml/jackson/databind/ser/jdk/JDKTypeSerializationTest.java
2026-07-08 02:18:37.000000000 +0200
@@ -104,20 +104,27 @@
}
@Test
- public void testInetAddress() throws IOException
+ public void testInetAddress() throws Exception
{
- assertEquals(q("127.0.0.1"),
MAPPER.writeValueAsString(InetAddress.getByName("127.0.0.1")));
- InetAddress input = InetAddress.getByName("google.com");
- assertEquals(q("google.com"), MAPPER.writeValueAsString(input));
+ // Default shape: textual form (host part of InetAddress.toString())
+ InetAddress input = InetAddress.getByName("127.0.0.1");
+ assertEquals(q("127.0.0.1"), MAPPER.writeValueAsString(input));
+ // Address carrying a host name (constructed WITHOUT a DNS lookup, via
+ // getByAddress()) still serializes the name in the default shape
+ InetAddress named = InetAddress.getByAddress("myhost.example.com",
+ new byte[] { 1, 2, 3, 4 });
+ assertEquals(q("myhost.example.com"),
MAPPER.writeValueAsString(named));
+
+ // NUMBER shape: numeric host address regardless of host name
ObjectMapper mapper = newJsonMapper();
mapper.configOverride(InetAddress.class)
.setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.NUMBER));
- String json = mapper.writeValueAsString(input);
- assertEquals(q(input.getHostAddress()), json);
+ assertEquals(q(named.getHostAddress()),
mapper.writeValueAsString(named));
- assertEquals(String.format("{\"value\":\"%s\"}",
input.getHostAddress()),
- mapper.writeValueAsString(new InetAddressBean(input)));
+ // ... also as a bean property
+ assertEquals(String.format("{\"value\":\"%s\"}",
named.getHostAddress()),
+ mapper.writeValueAsString(new InetAddressBean(named)));
}
@Test
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/jackson-databind-jackson-databind-2.18.8/src/test/java/com/fasterxml/jackson/databind/views/JsonViewExternalTypeIdBypassTest.java
new/jackson-databind-jackson-databind-2.18.9/src/test/java/com/fasterxml/jackson/databind/views/JsonViewExternalTypeIdBypassTest.java
---
old/jackson-databind-jackson-databind-2.18.8/src/test/java/com/fasterxml/jackson/databind/views/JsonViewExternalTypeIdBypassTest.java
1970-01-01 01:00:00.000000000 +0100
+++
new/jackson-databind-jackson-databind-2.18.9/src/test/java/com/fasterxml/jackson/databind/views/JsonViewExternalTypeIdBypassTest.java
2026-07-08 02:18:37.000000000 +0200
@@ -0,0 +1,341 @@
+package com.fasterxml.jackson.databind.views;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+import com.fasterxml.jackson.databind.testutil.DatabindTestUtil;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+// Tests to verify that {@code @JsonView} is honored for polymorphic properties
+// that use an EXTERNAL_PROPERTY type id. A {@code @JsonView} never changes the
+// type id, so these tests check the two distinct things a view CAN do -- (a)
hide
+// the whole polymorphic property (left null), or (b) hide a field of the
resolved
+// subtype (subtype built, field left null) -- across the different ways such a
+// property gets bound:
+//
+// (1) property-based (@JsonCreator) constructor, property hidden -> null
+// (2) field of the resolved subtype hidden (property-based creator) ->
field null
+// (3) type id is ITSELF a creator param, only the value hidden ->
value null, type id kept
+// (4) default-constructor bean (eager binding), property hidden -> null
+// (5) @JsonPOJOBuilder-based bean, property hidden -> null
+// (6) reverse of (3): type id creator param hidden, value visible ->
value kept, type id null
+public class JsonViewExternalTypeIdBypassTest extends DatabindTestUtil
+{
+ static class PublicView { }
+ static class AdminView extends PublicView { }
+
+ static abstract class Asset {
+ public String name;
+ }
+
+ static class PublicAsset extends Asset { }
+
+ static class AdminAsset extends Asset {
+ public String secret;
+ }
+
+ // Subtype used by case (2): here the admin-only marker is on the field,
not
+ // on the owning property.
+ static class FieldGatedAdminAsset extends Asset {
+ @JsonView(AdminView.class)
+ public String secret;
+ }
+
+ // Case (1): the entire polymorphic property is admin-only.
+ static class PropertyGatedContainer {
+ @JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
+ include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
+ property = "kind")
+ @JsonSubTypes({
+ @JsonSubTypes.Type(value = PublicAsset.class, name = "pub"),
+ @JsonSubTypes.Type(value = AdminAsset.class, name = "admin")
+ })
+ @JsonView(AdminView.class)
+ public Asset asset;
+
+ public String label;
+
+ @JsonCreator
+ public PropertyGatedContainer(
+ @JsonProperty("label") String label,
+ @JsonProperty("asset") @JsonView(AdminView.class) Asset asset)
{
+ this.label = label;
+ this.asset = asset;
+ }
+ }
+
+ // Case (2): the property is always visible, but the resolved subtype has
an
+ // admin-only field.
+ static class FieldGatedContainer {
+ @JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
+ include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
+ property = "kind")
+ @JsonSubTypes({
+ @JsonSubTypes.Type(value = PublicAsset.class, name = "pub"),
+ @JsonSubTypes.Type(value = FieldGatedAdminAsset.class, name =
"admin")
+ })
+ public Asset asset;
+
+ public String label;
+
+ @JsonCreator
+ public FieldGatedContainer(
+ @JsonProperty("label") String label,
+ @JsonProperty("asset") Asset asset) {
+ this.label = label;
+ this.asset = asset;
+ }
+ }
+
+ // Case (3): like case (1), but the external type id "kind" is ITSELF a
creator
+ // parameter ([databind#999]). The value "asset" is admin-only, but "kind"
carries
+ // no view, so hiding the value must not also drop the (visible) type id
property.
+ static class TypeIdCreatorContainer {
+ @JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
+ include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
+ property = "kind",
+ visible = true)
+ @JsonSubTypes({
+ @JsonSubTypes.Type(value = PublicAsset.class, name = "pub"),
+ @JsonSubTypes.Type(value = AdminAsset.class, name = "admin")
+ })
+ @JsonView(AdminView.class)
+ public Asset asset;
+
+ public String label;
+ public String kind;
+
+ @JsonCreator
+ public TypeIdCreatorContainer(
+ @JsonProperty("label") String label,
+ @JsonProperty("kind") String kind,
+ @JsonProperty("asset") @JsonView(AdminView.class) Asset asset)
{
+ this.label = label;
+ this.kind = kind;
+ this.asset = asset;
+ }
+ }
+
+ // Case (4): admin-only external-type property on a default-constructor (no
+ // @JsonCreator) bean -- exercises the other
ExternalTypeHandler.complete() path.
+ static class DefaultCtorContainer {
+ @JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
+ include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
+ property = "kind")
+ @JsonSubTypes({
+ @JsonSubTypes.Type(value = PublicAsset.class, name = "pub"),
+ @JsonSubTypes.Type(value = AdminAsset.class, name = "admin")
+ })
+ @JsonView(AdminView.class)
+ public Asset asset;
+
+ public String label;
+ }
+
+ // Case (5): admin-only external-type property on a @JsonPOJOBuilder-based
bean --
+ // exercises the BuilderBasedDeserializer external-type path.
+ @JsonDeserialize(builder = BuilderContainer.Builder.class)
+ static class BuilderContainer {
+ public final String label;
+ public final Asset asset;
+
+ BuilderContainer(String label, Asset asset) {
+ this.label = label;
+ this.asset = asset;
+ }
+
+ @JsonPOJOBuilder(withPrefix = "with")
+ static class Builder {
+ String label;
+ Asset asset;
+
+ public Builder withLabel(String label) {
+ this.label = label;
+ return this;
+ }
+
+ @JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
+ include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
+ property = "kind")
+ @JsonSubTypes({
+ @JsonSubTypes.Type(value = PublicAsset.class, name =
"pub"),
+ @JsonSubTypes.Type(value = AdminAsset.class, name =
"admin")
+ })
+ @JsonView(AdminView.class)
+ public Builder withAsset(Asset asset) {
+ this.asset = asset;
+ return this;
+ }
+
+ public BuilderContainer build() {
+ return new BuilderContainer(label, asset);
+ }
+ }
+ }
+
+ // Case (6): reverse of (3) -- the type id "kind" is a creator parameter
carrying
+ // the view, while the value "asset" is visible. Under PublicView the
value must
+ // still be bound and resolve correctly (the type id string still drives
type
+ // resolution), but the hidden "kind" creator parameter must be left null.
+ static class TypeIdGatedContainer {
+ @JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
+ include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
+ property = "kind",
+ visible = true)
+ @JsonSubTypes({
+ @JsonSubTypes.Type(value = PublicAsset.class, name = "pub"),
+ @JsonSubTypes.Type(value = AdminAsset.class, name = "admin")
+ })
+ public Asset asset;
+
+ public String label;
+ public String kind;
+
+ @JsonCreator
+ public TypeIdGatedContainer(
+ @JsonProperty("label") String label,
+ @JsonProperty("kind") @JsonView(AdminView.class) String kind,
+ @JsonProperty("asset") Asset asset) {
+ this.label = label;
+ this.kind = kind;
+ this.asset = asset;
+ }
+ }
+
+ private final ObjectMapper MAPPER = newJsonMapper();
+
+ // Case (1): admin-only polymorphic property must NOT be bound when reading
+ // with the less-privileged PublicView. A view never rewrites the type id,
so
+ // the only correct outcome is that "asset" is skipped entirely (left
null).
+ @Test
+ void testViewGatedExternalTypeProperty() throws Exception
+ {
+ String json = a2q("{'label':'hello','kind':'admin',"
+ + "'asset':{'name':'foo','secret':'LEAKED'}}");
+
+ PropertyGatedContainer result = MAPPER.readerWithView(PublicView.class)
+ .forType(PropertyGatedContainer.class)
+ .readValue(json);
+
+ assertEquals("hello", result.label);
+ // Admin-only property hidden from PublicView -> not bound at all
+ assertNull(result.asset, "Admin-only 'asset' must not be bound under
PublicView");
+ }
+
+ // Sanity check: with the AdminView active, the same property IS bound, and
+ // resolves to AdminAsset (the type id is "admin").
+ @Test
+ void testViewGatedExternalTypePropertyVisibleForAdmin() throws Exception
+ {
+ String json = a2q("{'label':'hello','kind':'admin',"
+ + "'asset':{'name':'foo','secret':'shh'}}");
+
+ PropertyGatedContainer result = MAPPER.readerWithView(AdminView.class)
+ .forType(PropertyGatedContainer.class)
+ .readValue(json);
+
+ AdminAsset asset = assertInstanceOf(AdminAsset.class, result.asset);
+ assertEquals("foo", asset.name);
+ assertEquals("shh", asset.secret);
+ }
+
+ // Case (2): the property itself is always visible, so the type id "admin"
+ // correctly produces the admin subtype -- but its admin-only "secret"
field
+ // must be left out when reading with PublicView.
+ @Test
+ void testViewGatedFieldOfExternalTypeSubtype() throws Exception
+ {
+ String json = a2q("{'label':'hello','kind':'admin',"
+ + "'asset':{'name':'foo','secret':'LEAKED'}}");
+
+ FieldGatedContainer result = MAPPER.readerWithView(PublicView.class)
+ .forType(FieldGatedContainer.class)
+ .readValue(json);
+
+ assertEquals("hello", result.label);
+ // View does not change the type id: still the admin subtype...
+ FieldGatedAdminAsset asset =
assertInstanceOf(FieldGatedAdminAsset.class, result.asset);
+ assertEquals("foo", asset.name);
+ // ...but the admin-only field must not leak under PublicView
+ assertNull(asset.secret, "Admin-only 'secret' must not leak under
PublicView");
+ }
+
+ // Case (3): hiding the admin-only value must NOT drop the (non-view) type
id
+ // property when the type id is itself a creator parameter
([databind#999]).
+ @Test
+ void testViewGatedValueKeepsVisibleTypeIdCreatorProp() throws Exception
+ {
+ String json = a2q("{'label':'hello','kind':'admin',"
+ + "'asset':{'name':'foo','secret':'LEAKED'}}");
+
+ TypeIdCreatorContainer result = MAPPER.readerWithView(PublicView.class)
+ .forType(TypeIdCreatorContainer.class)
+ .readValue(json);
+
+ assertEquals("hello", result.label);
+ // Admin-only value is hidden...
+ assertNull(result.asset, "Admin-only 'asset' must not be bound under
PublicView");
+ // ...but the visible type id creator property must still be bound
+ assertEquals("admin", result.kind, "Visible type id 'kind' must still
be bound");
+ }
+
+ // Case (4): admin-only external-type property on a default-constructor
bean must
+ // also be skipped under PublicView (covers the other complete() overload).
+ @Test
+ void testViewGatedExternalTypePropertyDefaultCtor() throws Exception
+ {
+ String json = a2q("{'label':'hello','kind':'admin',"
+ + "'asset':{'name':'foo','secret':'LEAKED'}}");
+
+ DefaultCtorContainer result = MAPPER.readerWithView(PublicView.class)
+ .forType(DefaultCtorContainer.class)
+ .readValue(json);
+
+ assertEquals("hello", result.label);
+ assertNull(result.asset, "Admin-only 'asset' must not be bound under
PublicView");
+ }
+
+ // Case (5): admin-only external-type property on a @JsonPOJOBuilder bean
must be
+ // skipped under PublicView (covers the BuilderBasedDeserializer
external-type path).
+ @Test
+ void testViewGatedExternalTypePropertyBuilder() throws Exception
+ {
+ String json = a2q("{'label':'hello','kind':'admin',"
+ + "'asset':{'name':'foo','secret':'LEAKED'}}");
+
+ BuilderContainer result = MAPPER.readerWithView(PublicView.class)
+ .forType(BuilderContainer.class)
+ .readValue(json);
+
+ assertEquals("hello", result.label);
+ assertNull(result.asset, "Admin-only 'asset' must not be bound under
PublicView");
+ }
+
+ // Case (6): reverse of case (3) -- @JsonView on the type-id creator param
but not
+ // the value. Under PublicView the value is still bound (and resolves to
the admin
+ // subtype via the type id), but the hidden type-id property must be left
null.
+ @Test
+ void testViewGatedTypeIdCreatorPropKeepsVisibleValue() throws Exception
+ {
+ String json = a2q("{'label':'hello','kind':'admin',"
+ + "'asset':{'name':'foo','secret':'shh'}}");
+
+ TypeIdGatedContainer result = MAPPER.readerWithView(PublicView.class)
+ .forType(TypeIdGatedContainer.class)
+ .readValue(json);
+
+ assertEquals("hello", result.label);
+ // Value carries no view -> still bound, and the type id still selects
the subtype
+ AdminAsset asset = assertInstanceOf(AdminAsset.class, result.asset);
+ assertEquals("foo", asset.name);
+ // ...but the admin-only type id creator property must not be bound
+ assertNull(result.kind, "Admin-only type id 'kind' must not be bound
under PublicView");
+ }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/jackson-databind-jackson-databind-2.18.8/src/test/java/com/fasterxml/jackson/databind/views/UnwrappedViewBypass6060Test.java
new/jackson-databind-jackson-databind-2.18.9/src/test/java/com/fasterxml/jackson/databind/views/UnwrappedViewBypass6060Test.java
---
old/jackson-databind-jackson-databind-2.18.8/src/test/java/com/fasterxml/jackson/databind/views/UnwrappedViewBypass6060Test.java
1970-01-01 01:00:00.000000000 +0100
+++
new/jackson-databind-jackson-databind-2.18.9/src/test/java/com/fasterxml/jackson/databind/views/UnwrappedViewBypass6060Test.java
2026-07-08 02:18:37.000000000 +0200
@@ -0,0 +1,117 @@
+package com.fasterxml.jackson.databind.views;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.testutil.DatabindTestUtil;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+// [databind#6060]: `@JsonView` by-passed for `@JsonUnwrapped` Field/Setter
properties
+public class UnwrappedViewBypass6060Test extends DatabindTestUtil {
+ public static class PublicView {}
+ public static class AdminView extends PublicView {}
+
+ public static class AccountFlags { // admin-only block
+ public String role;
+ public boolean approved;
+ public long creditBalance;
+ }
+ public static class Registration { // bound at the public
registration endpoint
+ @JsonView(PublicView.class) public String email;
+ @JsonView(PublicView.class) public String password;
+ @JsonView(AdminView.class) @JsonUnwrapped public AccountFlags flags;
// admin-only, unwrapped
+ }
+ public static class Control { // identical block, NOT unwrapped
+ @JsonView(PublicView.class) public String email;
+ @JsonView(AdminView.class) public AccountFlags flags;
+ }
+
+ public static class RegistrationWithSetters { // same as Registration,
but via setters
+ String email;
+ String password;
+ AccountFlags flags;
+
+ @JsonView(PublicView.class)
+ public void setEmail(String email) { this.email = email; }
+
+ @JsonView(PublicView.class)
+ public void setPassword(String password) { this.password = password; }
+
+ @JsonView(AdminView.class) @JsonUnwrapped
+ public void setFlags(AccountFlags flags) { this.flags = flags; }
+ }
+
+ @Test
+ void testUnwrappedBypass() throws Exception {
+ ObjectMapper mapper = sharedMapper();
+
+ String jsonC =
a2q("{'email':'e','flags':{'role':'ADMIN','approved':true,'creditBalance':1000000}}");
+ Control c = mapper.readerWithView(PublicView.class)
+ .forType(Control.class)
+ .readValue(jsonC);
+ assertEquals("e", c.email);
+ assertNull(c.flags, "expected control flag to be null in PublicView
read");
+
+ String jsonR = a2q("{'email':'[email protected]','password':'pw',"
+ + "'role':'ADMIN','approved':true,'creditBalance':1000000}");
+ Registration r = mapper.readerWithView(PublicView.class)
+ .forType(Registration.class)
+ .readValue(jsonR);
+ assertEquals("[email protected]", r.email);
+ assertEquals("pw", r.password);
+ // JsonUnwrapped flag in Registration class may affect this
+ assertNull(r.flags, "expected registration flag to be null in
PublicView read");
+ }
+
+ // Negative control: AdminView read MUST populate the unwrapped admin-only
block,
+ // so the PublicView assertion above is not passing vacuously.
+ @Test
+ void testUnwrappedVisibleInAdminView() throws Exception {
+ ObjectMapper mapper = sharedMapper();
+
+ String jsonR = a2q("{'email':'[email protected]','password':'pw',"
+ + "'role':'ADMIN','approved':true,'creditBalance':1000000}");
+ Registration r = mapper.readerWithView(AdminView.class)
+ .forType(Registration.class)
+ .readValue(jsonR);
+ assertEquals("[email protected]", r.email);
+ assertNotNull(r.flags, "expected registration flag to be populated in
AdminView read");
+ assertEquals("ADMIN", r.flags.role);
+ assertEquals(1000000, r.flags.creditBalance);
+ }
+
+ @Test
+ void testUnwrappedBypassWithSetters() throws Exception {
+ ObjectMapper mapper = sharedMapper();
+
+ String jsonR = a2q("{'email':'[email protected]','password':'pw',"
+ + "'role':'ADMIN','approved':true,'creditBalance':1000000}");
+ RegistrationWithSetters r = mapper.readerWithView(PublicView.class)
+ .forType(RegistrationWithSetters.class)
+ .readValue(jsonR);
+ assertEquals("[email protected]", r.email);
+ assertEquals("pw", r.password);
+ // JsonUnwrapped flag in RegistrationWithSetters class may affect this
+ assertNull(r.flags, "expected registration flag to be null in
PublicView read");
+ }
+
+ // Negative control for the setter variant: AdminView read MUST populate
flags.
+ @Test
+ void testUnwrappedVisibleInAdminViewWithSetters() throws Exception {
+ ObjectMapper mapper = sharedMapper();
+
+ String jsonR = a2q("{'email':'[email protected]','password':'pw',"
+ + "'role':'ADMIN','approved':true,'creditBalance':1000000}");
+ RegistrationWithSetters r = mapper.readerWithView(AdminView.class)
+ .forType(RegistrationWithSetters.class)
+ .readValue(jsonR);
+ assertEquals("[email protected]", r.email);
+ assertNotNull(r.flags, "expected registration flag to be populated in
AdminView read");
+ assertEquals("ADMIN", r.flags.role);
+ assertEquals(1000000, r.flags.creditBalance);
+ }
+
+}
++++++ jackson-databind-build.xml ++++++
--- /var/tmp/diff_new_pack.HFgb1e/_old 2026-07-15 17:12:22.217109799 +0200
+++ /var/tmp/diff_new_pack.HFgb1e/_new 2026-07-15 17:12:22.221109936 +0200
@@ -11,7 +11,7 @@
<property name="project.groupId" value="com.fasterxml.jackson.core"/>
<property name="project.artifactId" value="jackson-databind"/>
<property name="project.name" value="jackson-databind"/>
- <property name="project.version" value="2.18.8"/>
+ <property name="project.version" value="2.18.9"/>
<property name="project.vendor" value="FasterXML"/>
<property name="project.description" value="General data-binding
functionality for Jackson: works on core streaming API"/>
<property name="bundle.version" value="${project.version}"/>