Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/mkdocs/docs/features/binary_formats/cbor.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ The library maps CBOR types to JSON value types as follows:
| Byte string | binary | 0x59 |
| Byte string | binary | 0x5A |
| Byte string | binary | 0x5B |
| Byte string | binary | 0x5F |
| UTF-8 string | string | 0x60..0x77 |
| UTF-8 string | string | 0x78 |
| UTF-8 string | string | 0x79 |
Expand All @@ -156,6 +157,9 @@ The library maps CBOR types to JSON value types as follows:
| Single-Precision Float | number_float | 0xFA |
| Double-Precision Float | number_float | 0xFB |

Indefinite-length UTF-8 strings (0x7F) and byte strings (0x5F) are supported. Each chunk must be a definite-length
string of the same major type, as required by [RFC 8949, Section 3.2.3](https://www.rfc-editor.org/rfc/rfc8949.html#section-3.2.3).

!!! warning "Incomplete mapping"

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:
Expand Down
22 changes: 18 additions & 4 deletions include/nlohmann/detail/input/binary_reader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -995,10 +995,11 @@ class binary_reader
Additionally, CBOR's strings with indefinite lengths are supported.

@param[out] result created string
@param[in] allow_indefinite whether an indefinite-length string is allowed

@return whether string creation completed
*/
bool get_cbor_string(string_t& result)
bool get_cbor_string(string_t& result, const bool allow_indefinite = true)
{
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "string")))
{
Expand Down Expand Up @@ -1062,10 +1063,16 @@ class binary_reader

case 0x7F: // UTF-8 string (indefinite length)
{
if (JSON_HEDLEY_UNLIKELY(!allow_indefinite))
{
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
exception_message(input_format_t::cbor, concat("indefinite-length string is not allowed inside indefinite-length string; last byte: 0x", last_token), "string"), nullptr));
}
while (get() != 0xFF)
{
string_t chunk;
if (!get_cbor_string(chunk))
if (!get_cbor_string(chunk, false))
{
return false;
}
Expand All @@ -1091,10 +1098,11 @@ class binary_reader
Additionally, CBOR's byte arrays with indefinite lengths are supported.

@param[out] result created byte array
@param[in] allow_indefinite whether an indefinite-length byte array is allowed

@return whether byte array creation completed
*/
bool get_cbor_binary(binary_t& result)
bool get_cbor_binary(binary_t& result, const bool allow_indefinite = true)
{
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "binary")))
{
Expand Down Expand Up @@ -1162,10 +1170,16 @@ class binary_reader

case 0x5F: // Binary data (indefinite length)
{
if (JSON_HEDLEY_UNLIKELY(!allow_indefinite))
{
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
exception_message(input_format_t::cbor, concat("indefinite-length binary array is not allowed inside indefinite-length binary array; last byte: 0x", last_token), "binary"), nullptr));
}
while (get() != 0xFF)
{
binary_t chunk;
if (!get_cbor_binary(chunk))
if (!get_cbor_binary(chunk, false))
{
return false;
}
Expand Down
22 changes: 18 additions & 4 deletions single_include/nlohmann/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11589,10 +11589,11 @@ class binary_reader
Additionally, CBOR's strings with indefinite lengths are supported.

@param[out] result created string
@param[in] allow_indefinite whether an indefinite-length string is allowed

@return whether string creation completed
*/
bool get_cbor_string(string_t& result)
bool get_cbor_string(string_t& result, const bool allow_indefinite = true)
{
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "string")))
{
Expand Down Expand Up @@ -11656,10 +11657,16 @@ class binary_reader

case 0x7F: // UTF-8 string (indefinite length)
{
if (JSON_HEDLEY_UNLIKELY(!allow_indefinite))
{
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
exception_message(input_format_t::cbor, concat("indefinite-length string is not allowed inside indefinite-length string; last byte: 0x", last_token), "string"), nullptr));
}
while (get() != 0xFF)
{
string_t chunk;
if (!get_cbor_string(chunk))
if (!get_cbor_string(chunk, false))
{
return false;
}
Expand All @@ -11685,10 +11692,11 @@ class binary_reader
Additionally, CBOR's byte arrays with indefinite lengths are supported.

@param[out] result created byte array
@param[in] allow_indefinite whether an indefinite-length byte array is allowed

@return whether byte array creation completed
*/
bool get_cbor_binary(binary_t& result)
bool get_cbor_binary(binary_t& result, const bool allow_indefinite = true)
{
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "binary")))
{
Expand Down Expand Up @@ -11756,10 +11764,16 @@ class binary_reader

case 0x5F: // Binary data (indefinite length)
{
if (JSON_HEDLEY_UNLIKELY(!allow_indefinite))
{
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
exception_message(input_format_t::cbor, concat("indefinite-length binary array is not allowed inside indefinite-length binary array; last byte: 0x", last_token), "binary"), nullptr));
}
while (get() != 0xFF)
{
binary_t chunk;
if (!get_cbor_binary(chunk))
if (!get_cbor_binary(chunk, false))
{
return false;
}
Expand Down
6 changes: 2 additions & 4 deletions tests/src/unit-cbor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2493,10 +2493,8 @@ TEST_CASE("examples from RFC 8949 Appendix A")
{
const auto packed = utils::read_binary_file(TEST_DATA_DIRECTORY "/binary_data/cbor_binary.cbor");
json j;
CHECK_NOTHROW(j = json::from_cbor(packed));

const auto expected = utils::read_binary_file(TEST_DATA_DIRECTORY "/binary_data/cbor_binary.out");
CHECK(j == json::binary(expected));
// The fixture ends with nested indefinite-length byte strings, which RFC 8949 Section 3.2.3 forbids.
CHECK_THROWS_WITH_AS(j = json::from_cbor(packed), "[json.exception.parse_error.113] parse error at byte 513: syntax error while parsing CBOR binary: indefinite-length binary array is not allowed inside indefinite-length binary array; last byte: 0x5F", json::parse_error&);

// 0xd8
CHECK(json::to_cbor(json::binary(std::vector<uint8_t> {}, 0x42)) == std::vector<uint8_t> {0xd8, 0x42, 0x40});
Expand Down
7 changes: 7 additions & 0 deletions tests/src/unit-regression2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1239,6 +1239,13 @@ TEST_CASE("regression tests 2")
CHECK(j == json({2, 4, 6}));
}
#endif

SECTION("issue #5317 - nested indefinite-length CBOR string chunks are rejected")
{
json _;
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<std::uint8_t>({0x7F, 0x7F, 0x61, 0x61, 0xFF, 0xFF})), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR string: indefinite-length string is not allowed inside indefinite-length string; last byte: 0x7F", json::parse_error&);
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<std::uint8_t>({0x5F, 0x5F, 0x41, 0x61, 0xFF, 0xFF})), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR binary: indefinite-length binary array is not allowed inside indefinite-length binary array; last byte: 0x5F", json::parse_error&);
}
}

TEST_CASE_TEMPLATE("issue #4798 - nlohmann::json::to_msgpack() encode float NaN as double", T, double, float) // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization)
Expand Down
Loading