Fix CBOR tag handlers not recognizing tags 0-5 and 21-23 (#5331)

* Fix CBOR tag handlers not recognizing tags 0-5 and 21-23

The tagged-item switch in binary_reader::parse_cbor_internal() only handled
head bytes 0xC6-0xD4 and 0xD8-0xDB. Bytes 0xC0-0xC5 (tags 0-5: date/time,
epoch, bignum, decimal, bigfloat) and 0xD5-0xD7 (tags 21-23: base64url,
base64, base16 conversion hints) fell through to the default case and were
reported as invalid bytes, even under cbor_tag_handler_t::ignore and ::store,
despite being valid CBOR major-type-6 tags per RFC 8949.

Add the missing case labels so the full 0xC0-0xDB range is handled
uniformly. Extend the "Tagged values" test in unit-cbor.cpp to cover
0xC0-0xD7, and update the CBOR docs to state the corrected tag range.

Fixes #5315

Signed-off-by: sahilkamate03 <45514385+sahilkamate03@users.noreply.github.com>

* Fix stale CBOR tag docs and add store-mode binary-payload test

The "Incomplete mapping" warning still listed tags 0-5 (date/time,
bignum, decimal fraction, bigfloat) and 21-23 (expected conversions)
as unsupported, even though they now parse correctly under
cbor_tag_handler_t::ignore/store, same as 0xC6..0xD4/0xD8..0xDB.
Remove those five bullets and cross-reference the "Tagged items"
warning below, matching the equivalent docs fix landed independently
in PR #5367.

Also add a cbor_tag_handler_t::store test that wraps a binary
payload (not just a string) for every byte in 0xC0..0xD7, confirming
these tags are unwrapped the same way as 0xC6..0xD4 rather than
mistaken for the 0xD8..0xDB binary-subtype marker syntax, per review
feedback on #5331.

Signed-off-by: sahilkamate03 <45514385+sahilkamate03@users.noreply.github.com>

---------

Signed-off-by: sahilkamate03 <45514385+sahilkamate03@users.noreply.github.com>
This commit is contained in:
Sahil_Kamate
2026-08-19 20:19:47 +02:00
committed by GitHub
parent ce87157d4e
commit b5378e8deb
4 changed files with 36 additions and 10 deletions
@@ -160,14 +160,11 @@ The library maps CBOR types to JSON value types as follows:
The mapping is **incomplete** in the sense that not all CBOR types can be converted to a JSON value. The following CBOR types are not supported and will yield parse errors: The mapping is **incomplete** in the sense that not all CBOR types can be converted to a JSON value. The following CBOR types are not supported and will yield parse errors:
- date/time (0xC0..0xC1)
- bignum (0xC2..0xC3)
- decimal fraction (0xC4)
- bigfloat (0xC5)
- expected conversions (0xD5..0xD7)
- simple values (0xE0..0xF3, 0xF8) - simple values (0xE0..0xF3, 0xF8)
- undefined (0xF7) - undefined (0xF7)
Tagged items (0xC0..0xDB) are not interpreted either; see the note on tagged items below.
!!! warning "Negative integer overflow" !!! warning "Negative integer overflow"
CBOR negative integers (major type 1) are decoded as `-1 - n`. If the encoded magnitude `n` is too large for the CBOR negative integers (major type 1) are decoded as `-1 - n`. If the encoded magnitude `n` is too large for the
@@ -181,7 +178,7 @@ The library maps CBOR types to JSON value types as follows:
!!! warning "Tagged items" !!! warning "Tagged items"
Tagged items will throw a parse error by default. They can be ignored by passing `cbor_tag_handler_t::ignore` to function `from_cbor`. They can be stored by passing `cbor_tag_handler_t::store` to function `from_cbor`. Tagged items (0xC0..0xDB) will throw a parse error by default. They can be ignored by passing `cbor_tag_handler_t::ignore` to function `from_cbor`, in which case the tag is skipped and the enclosed data item is parsed on its own. They can be stored by passing `cbor_tag_handler_t::store` to function `from_cbor`. Note that no tag is ever interpreted: for instance, a text string tagged with tag 0 (date/time) stays a string.
??? example ??? example
@@ -773,7 +773,13 @@ class binary_reader
case 0xBF: // map (indefinite length) case 0xBF: // map (indefinite length)
return get_cbor_object(detail::unknown_size(), tag_handler); return get_cbor_object(detail::unknown_size(), tag_handler);
case 0xC6: // tagged item case 0xC0: // tagged item
case 0xC1:
case 0xC2:
case 0xC3:
case 0xC4:
case 0xC5:
case 0xC6:
case 0xC7: case 0xC7:
case 0xC8: case 0xC8:
case 0xC9: case 0xC9:
@@ -788,6 +794,9 @@ class binary_reader
case 0xD2: case 0xD2:
case 0xD3: case 0xD3:
case 0xD4: case 0xD4:
case 0xD5:
case 0xD6:
case 0xD7:
case 0xD8: // tagged item (1 byte follows) case 0xD8: // tagged item (1 byte follows)
case 0xD9: // tagged item (2 bytes follow) case 0xD9: // tagged item (2 bytes follow)
case 0xDA: // tagged item (4 bytes follow) case 0xDA: // tagged item (4 bytes follow)
+10 -1
View File
@@ -11395,7 +11395,13 @@ class binary_reader
case 0xBF: // map (indefinite length) case 0xBF: // map (indefinite length)
return get_cbor_object(detail::unknown_size(), tag_handler); return get_cbor_object(detail::unknown_size(), tag_handler);
case 0xC6: // tagged item case 0xC0: // tagged item
case 0xC1:
case 0xC2:
case 0xC3:
case 0xC4:
case 0xC5:
case 0xC6:
case 0xC7: case 0xC7:
case 0xC8: case 0xC8:
case 0xC9: case 0xC9:
@@ -11410,6 +11416,9 @@ class binary_reader
case 0xD2: case 0xD2:
case 0xD3: case 0xD3:
case 0xD4: case 0xD4:
case 0xD5:
case 0xD6:
case 0xD7:
case 0xD8: // tagged item (1 byte follows) case 0xD8: // tagged item (1 byte follows)
case 0xD9: // tagged item (2 bytes follow) case 0xD9: // tagged item (2 bytes follow)
case 0xDA: // tagged item (4 bytes follow) case 0xDA: // tagged item (4 bytes follow)
+13 -2
View File
@@ -2565,11 +2565,16 @@ TEST_CASE("Tagged values")
const json j = "s"; const json j = "s";
auto v = json::to_cbor(j); auto v = json::to_cbor(j);
SECTION("0xC6..0xD4") const json j_bin_payload = json::binary(std::vector<std::uint8_t> {0x01, 0x02, 0x03});
auto v_bin_payload = json::to_cbor(j_bin_payload);
SECTION("0xC0..0xD7")
{ {
for (const auto b : std::vector<std::uint8_t> for (const auto b : std::vector<std::uint8_t>
{ {
0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5,
0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4,
0xD5, 0xD6, 0xD7
}) })
{ {
CAPTURE(b); CAPTURE(b);
@@ -2589,6 +2594,12 @@ TEST_CASE("Tagged values")
auto j_tagged_stored = json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::store); auto j_tagged_stored = json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::store);
CHECK(j_tagged_stored == j); CHECK(j_tagged_stored == j);
auto v_binary_tagged = v_bin_payload;
v_binary_tagged.insert(v_binary_tagged.begin(), b);
auto j_binary_tagged_stored = json::from_cbor(v_binary_tagged, true, true, json::cbor_tag_handler_t::store);
CHECK(j_binary_tagged_stored == j_bin_payload);
CHECK(!j_binary_tagged_stored.get_binary().has_subtype());
} }
} }