From 69a602da0698f8ad8cc5cf8e0977b28e06c047d8 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Sun, 26 Oct 2025 18:01:28 -0700 Subject: [PATCH 1/2] Fix #599: handle skipping of CBOR string refs --- .../jackson/dataformat/cbor/CBORParser.java | 44 +++++++++++++++++++ .../cbor/{tofix => }/StringRef599Test.java | 6 +-- release-notes/CREDITS-2.x | 4 ++ release-notes/VERSION-2.x | 2 + 4 files changed, 51 insertions(+), 5 deletions(-) rename cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/{tofix => }/StringRef599Test.java (86%) diff --git a/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java b/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java index 6c5cc0587..b515ca585 100644 --- a/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java +++ b/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java @@ -3414,6 +3414,50 @@ protected void _skipIncomplete() throws IOException && type != CBORConstants.MAJOR_TYPE_BYTES) { _throwInternal(); } + + // [dataformats-binary#599]: If we are in a stringref namespace, we need to + // actually read and store the string/bytes value instead of just skipping it, + // so that later string references can find it. + if (!_stringRefs.empty()) { + final int lowBits = _typeByte & 0x1F; + final int len = _decodeExplicitLength(lowBits); + + if (type == CBORConstants.MAJOR_TYPE_TEXT) { + // For text, need to read the string to potentially store as reference + if (len >= 0) { + // Non-chunked text + if (shouldReferenceString(_stringRefs.peek().stringRefs.size(), len)) { + // Need to actually read and store the string + String str = _finishTextToken(_typeByte); + // _finishTextToken already handles adding to stringRefs + return; + } + // No need to store, can skip + } else { + // Chunked text - must read to potentially store + // Note: chunked strings may still need to be stored if large enough + String str = _finishTextToken(_typeByte); + return; + } + } else { + // For bytes, similar logic + if (len >= 0) { + if (shouldReferenceString(_stringRefs.peek().stringRefs.size(), len)) { + // Need to actually read and store the bytes + byte[] b = _finishBytes(len); + // _finishBytes already handles adding to stringRefs + return; + } + // No need to store, can skip + } else { + // Chunked bytes + byte[] b = _finishChunkedBytes(); + return; + } + } + } + + // Standard skip logic when not in stringref namespace final int lowBits = _typeByte & 0x1F; if (lowBits <= 23) { if (lowBits > 0) { diff --git a/cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/tofix/StringRef599Test.java b/cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/StringRef599Test.java similarity index 86% rename from cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/tofix/StringRef599Test.java rename to cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/StringRef599Test.java index 2183e2305..758991608 100644 --- a/cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/tofix/StringRef599Test.java +++ b/cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/StringRef599Test.java @@ -1,13 +1,10 @@ -package com.fasterxml.jackson.dataformat.cbor.tofix; +package com.fasterxml.jackson.dataformat.cbor; import java.util.Arrays; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.cbor.*; -import com.fasterxml.jackson.dataformat.cbor.testutil.failure.JacksonTestFailureExpected; - import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; @@ -27,7 +24,6 @@ public void testDupsNoStringRef() throws Exception } // [dataformats-binary#599] - @JacksonTestFailureExpected @Test public void testDupsWithStringRef() throws Exception { diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x index 182befa7e..16598b7b5 100644 --- a/release-notes/CREDITS-2.x +++ b/release-notes/CREDITS-2.x @@ -422,3 +422,7 @@ Vincent Eigenberger (@beseder1) * Contributed fix for #601: Jackson subtype Avro schema unions are non-deterministic and therefore incompatible with each other (2.20.1) + +Yohei Kishimoto (@morokosi) + * Reported #599: (cbor) Unable to deserialize stringref-enabled CBOR with ignored properties + (2.21.0) diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index a433504f5..9ad755f27 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -16,6 +16,8 @@ Active maintainers: 2.21.0 (not yet released) +#599: (cbor) Unable to deserialize stringref-enabled CBOR with ignored properties + (reported by Yohei K) #623: (ion) Upgrade `ion-java` dep to 1.11.11 (from 1.11.10) (requested by @Shaurya0108) From 25b47168478ae7dae83a09b6ac12bb39f4626a13 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Sun, 26 Oct 2025 19:29:06 -0700 Subject: [PATCH 2/2] Fix the fix --- .../jackson/dataformat/cbor/CBORParser.java | 41 ++++--------------- 1 file changed, 8 insertions(+), 33 deletions(-) diff --git a/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java b/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java index b515ca585..d2d9d8d7e 100644 --- a/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java +++ b/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java @@ -3418,43 +3418,18 @@ protected void _skipIncomplete() throws IOException // [dataformats-binary#599]: If we are in a stringref namespace, we need to // actually read and store the string/bytes value instead of just skipping it, // so that later string references can find it. + // The finish methods will determine if the value should be added to the + // reference table based on shouldReferenceString(). if (!_stringRefs.empty()) { - final int lowBits = _typeByte & 0x1F; - final int len = _decodeExplicitLength(lowBits); - if (type == CBORConstants.MAJOR_TYPE_TEXT) { - // For text, need to read the string to potentially store as reference - if (len >= 0) { - // Non-chunked text - if (shouldReferenceString(_stringRefs.peek().stringRefs.size(), len)) { - // Need to actually read and store the string - String str = _finishTextToken(_typeByte); - // _finishTextToken already handles adding to stringRefs - return; - } - // No need to store, can skip - } else { - // Chunked text - must read to potentially store - // Note: chunked strings may still need to be stored if large enough - String str = _finishTextToken(_typeByte); - return; - } + // Need to actually read the text (which may add to stringRefs) + _finishTextToken(_typeByte); } else { - // For bytes, similar logic - if (len >= 0) { - if (shouldReferenceString(_stringRefs.peek().stringRefs.size(), len)) { - // Need to actually read and store the bytes - byte[] b = _finishBytes(len); - // _finishBytes already handles adding to stringRefs - return; - } - // No need to store, can skip - } else { - // Chunked bytes - byte[] b = _finishChunkedBytes(); - return; - } + // For bytes: decode length then read (which may add to stringRefs) + int len = _decodeExplicitLength(_typeByte & 0x1F); + _finishBytes(len); } + return; } // Standard skip logic when not in stringref namespace