Validate the optimized-container type marker for count 0 in UBJSON and BJData
The '$' type marker of an optimized container was only checked when an
element was read, so containers with count 0 accepted any marker:
`[$x#i0`, `{$x#i0` and `[$E#i0` parsed as empty containers although a
non-empty container with the same marker is rejected and the UBJSON
specification requires a legal marker after '$'.
get_ubjson_size_type() now checks the marker right after '$' against the
allowed set (UBJSON: Z N T F i U I l L d D H C S [ {; BJData: B U i u I m
l M L h d D C, with the existing exclusion of T F Z N S H [ {) and reports
parse_error.112 "marker ... is not a permitted optimized array type".
Input that was accepted and is now rejected: an optimized container with
count 0 and an invalid '$' type marker. Valid input is unaffected. Two
existing tests that used an invalid marker incidentally ("[$#" and a
negative-count case) were adapted.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
3 files changed
+140
-3
No files matched your search
@@ -3000,6 +3000,14 @@ class binary_reader
|
||||
return false;
|
||||
}
|
||||
|
||||
// validate the type marker up front: otherwise it would only be
|
||||
// checked when an element is read, and a container with count 0
|
||||
// would accept any marker
|
||||
if (JSON_HEDLEY_UNLIKELY(!is_valid_optimized_type(result.second)))
|
||||
{
|
||||
return optimized_type_error();
|
||||
}
|
||||
|
||||
get_ignore_noop();
|
||||
if (JSON_HEDLEY_UNLIKELY(current != '#'))
|
||||
{
|
||||
@@ -4496,6 +4504,44 @@ class binary_reader
|
||||
std::size_t bon8_pushback_size = 0;
|
||||
|
||||
JSON_PRIVATE_UNLESS_TESTED:
|
||||
/*!
|
||||
@brief check whether @a marker is a type marker that may follow '$'
|
||||
UBJSON: Z N T F i U I l L d D H C S [ {; BJData: the numeric markers and C
|
||||
(BJData's excluded markers are rejected separately).
|
||||
*/
|
||||
bool is_valid_optimized_type(const char_int_type marker) const noexcept
|
||||
{
|
||||
switch (marker)
|
||||
{
|
||||
case 'i':
|
||||
case 'U':
|
||||
case 'I':
|
||||
case 'l':
|
||||
case 'L':
|
||||
case 'd':
|
||||
case 'D':
|
||||
case 'C':
|
||||
return true;
|
||||
case 'Z':
|
||||
case 'N':
|
||||
case 'T':
|
||||
case 'F':
|
||||
case 'H':
|
||||
case 'S':
|
||||
case '[':
|
||||
case '{':
|
||||
return input_format != input_format_t::bjdata;
|
||||
case 'B':
|
||||
case 'u':
|
||||
case 'm':
|
||||
case 'M':
|
||||
case 'h':
|
||||
return input_format == input_format_t::bjdata;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief whether @a marker is excluded from BJData's optimized ND-array types
|
||||
@return whether @a marker is one of 'F', 'H', 'N', 'S', 'T', 'Z', '[', '{'
|
||||
|
||||
@@ -3134,7 +3134,7 @@ TEST_CASE("BJData")
|
||||
std::vector<uint8_t> const v1 = {'[', '#', 'i', 0xF1};
|
||||
std::vector<uint8_t> const v2 = {'[', '$', 'I', '#', 'i', 0xF2};
|
||||
std::vector<uint8_t> const v3 = {'[', '$', 'I', '#', '[', 'i', 0xF4, 'i', 0x02, ']'};
|
||||
std::vector<uint8_t> const v4 = {'[', '$', 0xF6, '#', 'i', 0xF7};
|
||||
std::vector<uint8_t> const v4 = {'[', '$', 'l', '#', 'i', 0xF7};
|
||||
std::vector<uint8_t> const v5 = {'[', '$', 'I', '#', '[', 'i', 0xF5, 'i', 0xF1, ']'};
|
||||
std::vector<uint8_t> const v6 = {'[', '#', '[', 'i', 0xF3, 'i', 0x02, ']'};
|
||||
|
||||
@@ -3455,7 +3455,7 @@ TEST_CASE("BJData")
|
||||
CHECK(json::from_bjdata(v0, true, false).is_discarded());
|
||||
|
||||
std::vector<uint8_t> const vi = {'[', '$', '#'};
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vi), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData value: unexpected end of input", json::parse_error&);
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vi), "[json.exception.parse_error.112] parse error at byte 3: syntax error while parsing BJData type: marker 0x23 is not a permitted optimized array type", json::parse_error&);
|
||||
CHECK(json::from_bjdata(vi, true, false).is_discarded());
|
||||
|
||||
std::vector<uint8_t> const vU = {'[', '$', 'U'};
|
||||
@@ -4622,3 +4622,51 @@ TEST_CASE("BJData large strings and binaries (chunked reader)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("BJData optimized container type marker is validated for count 0")
|
||||
{
|
||||
// the '$' type marker must be a legal marker even if no element follows
|
||||
SECTION("invalid marker")
|
||||
{
|
||||
// 'Z', 'T', 'F', 'N', 'S', 'H', '[' and '{' are excluded in BJData
|
||||
for (const char marker : {'x', 'E', 'Z', 'T', 'F', 'N', 'S', 'H', '[', '{'})
|
||||
{
|
||||
for (const char open : {'[', '{'})
|
||||
{
|
||||
for (const std::uint8_t count : {0, 1})
|
||||
{
|
||||
CAPTURE(marker)
|
||||
CAPTURE(open)
|
||||
CAPTURE(count)
|
||||
const std::vector<std::uint8_t> v = {static_cast<std::uint8_t>(open), '$', static_cast<std::uint8_t>(marker), '#', 'i', count};
|
||||
json _;
|
||||
CHECK_THROWS_AS(_ = json::from_bjdata(v), json::parse_error&);
|
||||
CHECK(json::from_bjdata(v, true, false).is_discarded());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("error message")
|
||||
{
|
||||
const std::vector<std::uint8_t> v = {'[', '$', 'x', '#', 'i', 0};
|
||||
json _;
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v), "[json.exception.parse_error.112] parse error at byte 3: syntax error while parsing BJData type: marker 0x78 is not a permitted optimized array type", json::parse_error&);
|
||||
}
|
||||
|
||||
SECTION("valid marker")
|
||||
{
|
||||
for (const char marker : {'i', 'U', 'I', 'u', 'l', 'm', 'L', 'M', 'h', 'd', 'D', 'C'})
|
||||
{
|
||||
CAPTURE(marker)
|
||||
const std::vector<std::uint8_t> va = {'[', '$', static_cast<std::uint8_t>(marker), '#', 'i', 0};
|
||||
const std::vector<std::uint8_t> vo = {'{', '$', static_cast<std::uint8_t>(marker), '#', 'i', 0};
|
||||
CHECK(json::from_bjdata(va) == json::array());
|
||||
CHECK(json::from_bjdata(vo) == json::object());
|
||||
}
|
||||
|
||||
// an optimized array of bytes is read as a binary value
|
||||
const std::vector<std::uint8_t> vb = {'[', '$', 'B', '#', 'i', 0};
|
||||
CHECK(json::from_bjdata(vb) == json::binary({}));
|
||||
}
|
||||
}
|
||||
@@ -2023,7 +2023,7 @@ TEST_CASE("UBJSON")
|
||||
CHECK(json::from_ubjson(v0, true, false).is_discarded());
|
||||
|
||||
std::vector<uint8_t> const vi = {'[', '$', '#'};
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vi), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing UBJSON value: unexpected end of input", json::parse_error&);
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vi), "[json.exception.parse_error.112] parse error at byte 3: syntax error while parsing UBJSON type: marker 0x23 is not a permitted optimized array type", json::parse_error&);
|
||||
CHECK(json::from_ubjson(vi, true, false).is_discarded());
|
||||
|
||||
std::vector<uint8_t> const vT = {'[', '$', 'T'};
|
||||
@@ -3348,3 +3348,46 @@ TEST_CASE("UBJSON large strings (chunked reader)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("UBJSON optimized container type marker is validated for count 0")
|
||||
{
|
||||
// the '$' type marker must be a legal marker even if no element follows
|
||||
SECTION("invalid marker")
|
||||
{
|
||||
for (const char marker : {'x', 'E', 'B', 'u', 'm', 'M', 'h'})
|
||||
{
|
||||
for (const char open : {'[', '{'})
|
||||
{
|
||||
for (const std::uint8_t count : {0, 1})
|
||||
{
|
||||
CAPTURE(marker)
|
||||
CAPTURE(open)
|
||||
CAPTURE(count)
|
||||
const std::vector<std::uint8_t> v = {static_cast<std::uint8_t>(open), '$', static_cast<std::uint8_t>(marker), '#', 'i', count};
|
||||
json _;
|
||||
CHECK_THROWS_AS(_ = json::from_ubjson(v), json::parse_error&);
|
||||
CHECK(json::from_ubjson(v, true, false).is_discarded());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("error message")
|
||||
{
|
||||
const std::vector<std::uint8_t> v = {'[', '$', 'x', '#', 'i', 0};
|
||||
json _;
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(v), "[json.exception.parse_error.112] parse error at byte 3: syntax error while parsing UBJSON type: marker 0x78 is not a permitted optimized array type", json::parse_error&);
|
||||
}
|
||||
|
||||
SECTION("valid marker")
|
||||
{
|
||||
for (const char marker : {'Z', 'N', 'T', 'F', 'i', 'U', 'I', 'l', 'L', 'd', 'D', 'H', 'C', 'S', '[', '{'})
|
||||
{
|
||||
CAPTURE(marker)
|
||||
const std::vector<std::uint8_t> va = {'[', '$', static_cast<std::uint8_t>(marker), '#', 'i', 0};
|
||||
const std::vector<std::uint8_t> vo = {'{', '$', static_cast<std::uint8_t>(marker), '#', 'i', 0};
|
||||
CHECK(json::from_ubjson(va) == json::array());
|
||||
CHECK(json::from_ubjson(vo) == json::object());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user