mirror of
https://github.com/nlohmann/json.git
synced 2026-07-31 08:03:03 +04:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cf14cbdd02 | |||
| 3565f40229 | |||
| 1c5a953de5 | |||
| a03e65420c | |||
| d6ede37088 |
@@ -38,14 +38,14 @@ jobs:
|
|||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
uses: github/codeql-action/init@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
|
||||||
with:
|
with:
|
||||||
languages: c-cpp
|
languages: c-cpp
|
||||||
|
|
||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
uses: github/codeql-action/autobuild@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
uses: github/codeql-action/analyze@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
|
||||||
|
|||||||
@@ -43,6 +43,6 @@ jobs:
|
|||||||
output: 'flawfinder_results.sarif'
|
output: 'flawfinder_results.sarif'
|
||||||
|
|
||||||
- name: Upload analysis results to GitHub Security tab
|
- name: Upload analysis results to GitHub Security tab
|
||||||
uses: github/codeql-action/upload-sarif@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
uses: github/codeql-action/upload-sarif@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
|
||||||
with:
|
with:
|
||||||
sarif_file: ${{github.workspace}}/flawfinder_results.sarif
|
sarif_file: ${{github.workspace}}/flawfinder_results.sarif
|
||||||
|
|||||||
@@ -76,6 +76,6 @@ jobs:
|
|||||||
|
|
||||||
# Upload the results to GitHub's code scanning dashboard.
|
# Upload the results to GitHub's code scanning dashboard.
|
||||||
- name: "Upload to code-scanning"
|
- name: "Upload to code-scanning"
|
||||||
uses: github/codeql-action/upload-sarif@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
uses: github/codeql-action/upload-sarif@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
|
||||||
with:
|
with:
|
||||||
sarif_file: results.sarif
|
sarif_file: results.sarif
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ jobs:
|
|||||||
|
|
||||||
# Upload SARIF file generated in previous step
|
# Upload SARIF file generated in previous step
|
||||||
- name: Upload SARIF file
|
- name: Upload SARIF file
|
||||||
uses: github/codeql-action/upload-sarif@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
uses: github/codeql-action/upload-sarif@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
|
||||||
with:
|
with:
|
||||||
sarif_file: semgrep.sarif
|
sarif_file: semgrep.sarif
|
||||||
if: always()
|
if: always()
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
- uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0
|
- uses: actions/stale@1e223db275d687790206a7acac4d1a11bd6fe629 # v10.4.0
|
||||||
with:
|
with:
|
||||||
stale-issue-label: 'state: stale'
|
stale-issue-label: 'state: stale'
|
||||||
stale-pr-label: 'state: stale'
|
stale-pr-label: 'state: stale'
|
||||||
|
|||||||
@@ -1846,6 +1846,47 @@ class binary_reader
|
|||||||
return get_ubjson_value(get_char ? get_ignore_noop() : current);
|
return get_ubjson_value(get_char ? get_ignore_noop() : current);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief reject a negative UBJSON/BJData string length
|
||||||
|
|
||||||
|
String and key lengths are written with signed integer markers (i, I, l,
|
||||||
|
L). A negative value is malformed; without this check get_string() would
|
||||||
|
silently treat it as an empty string and leave the following bytes to be
|
||||||
|
misread as the next value. This mirrors the non-negative check the
|
||||||
|
optimized-container count path already performs in get_ubjson_size_value.
|
||||||
|
|
||||||
|
@param[in] len the string length read from the input
|
||||||
|
@return whether the length is valid (non-negative)
|
||||||
|
*/
|
||||||
|
template<typename NumberType>
|
||||||
|
bool check_ubjson_string_length(const NumberType len)
|
||||||
|
{
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(len < 0))
|
||||||
|
{
|
||||||
|
return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
|
||||||
|
exception_message(input_format, "string length must not be negative", "string"), nullptr));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief create the error message for a missing/invalid length-type marker
|
||||||
|
|
||||||
|
Used both when reading a UBJSON/BJData string and when reading an optimized
|
||||||
|
container size. The accepted markers depend on the input format, and the
|
||||||
|
size variant appends " after '#'" via @a infix.
|
||||||
|
|
||||||
|
@param[in] last_token the offending byte as returned by get_token_string()
|
||||||
|
@param[in] infix extra context inserted after the marker list
|
||||||
|
@return the formatted error message
|
||||||
|
*/
|
||||||
|
std::string unexpected_length_type_message(const std::string& last_token, const char* infix) const
|
||||||
|
{
|
||||||
|
return concat("expected length type specification (",
|
||||||
|
input_format == input_format_t::bjdata ? "U, i, u, I, m, l, M, L" : "U, i, I, l, L",
|
||||||
|
")", infix, "; last byte: 0x", last_token);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief reads a UBJSON string
|
@brief reads a UBJSON string
|
||||||
|
|
||||||
@@ -1883,25 +1924,25 @@ class binary_reader
|
|||||||
case 'i':
|
case 'i':
|
||||||
{
|
{
|
||||||
std::int8_t len{};
|
std::int8_t len{};
|
||||||
return get_number(input_format, len) && get_string(input_format, len, result);
|
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'I':
|
case 'I':
|
||||||
{
|
{
|
||||||
std::int16_t len{};
|
std::int16_t len{};
|
||||||
return get_number(input_format, len) && get_string(input_format, len, result);
|
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'l':
|
case 'l':
|
||||||
{
|
{
|
||||||
std::int32_t len{};
|
std::int32_t len{};
|
||||||
return get_number(input_format, len) && get_string(input_format, len, result);
|
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'L':
|
case 'L':
|
||||||
{
|
{
|
||||||
std::int64_t len{};
|
std::int64_t len{};
|
||||||
return get_number(input_format, len) && get_string(input_format, len, result);
|
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'u':
|
case 'u':
|
||||||
@@ -1938,17 +1979,8 @@ class binary_reader
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
auto last_token = get_token_string();
|
auto last_token = get_token_string();
|
||||||
std::string message;
|
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
||||||
|
exception_message(input_format, unexpected_length_type_message(last_token, ""), "string"), nullptr));
|
||||||
if (input_format != input_format_t::bjdata)
|
|
||||||
{
|
|
||||||
message = "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
message = "expected length type specification (U, i, u, I, m, l, M, L); last byte: 0x" + last_token;
|
|
||||||
}
|
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, "string"), nullptr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -2227,17 +2259,8 @@ class binary_reader
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
auto last_token = get_token_string();
|
auto last_token = get_token_string();
|
||||||
std::string message;
|
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
||||||
|
exception_message(input_format, unexpected_length_type_message(last_token, " after '#'"), "size"), nullptr));
|
||||||
if (input_format != input_format_t::bjdata)
|
|
||||||
{
|
|
||||||
message = "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
message = "expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0x" + last_token;
|
|
||||||
}
|
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, "size"), nullptr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|||||||
@@ -395,17 +395,30 @@ struct wide_string_input_helper<BaseInputAdapter, 2>
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!input.empty()))
|
// A supplementary code point is a high surrogate (0xD800..0xDBFF)
|
||||||
|
// followed by a low surrogate (0xDC00..0xDFFF). A lone low
|
||||||
|
// surrogate, a high surrogate at the end of the input, or a high
|
||||||
|
// surrogate followed by any other unit is malformed UTF-16. In
|
||||||
|
// that case the offending unit is passed through unchanged so the
|
||||||
|
// UTF-8 decoder rejects it, matching how \uXXXX surrogate escapes
|
||||||
|
// are handled in the lexer.
|
||||||
|
bool valid_pair = false;
|
||||||
|
if (wc <= 0xDBFF && JSON_HEDLEY_UNLIKELY(!input.empty()))
|
||||||
{
|
{
|
||||||
const auto wc2 = static_cast<unsigned int>(input.get_character());
|
const auto wc2 = static_cast<unsigned int>(input.get_character());
|
||||||
const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu));
|
if (0xDC00 <= wc2 && wc2 <= 0xDFFF)
|
||||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u));
|
{
|
||||||
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));
|
const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu));
|
||||||
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu));
|
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u));
|
||||||
utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (charcode & 0x3Fu));
|
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));
|
||||||
utf8_bytes_filled = 4;
|
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu));
|
||||||
|
utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (charcode & 0x3Fu));
|
||||||
|
utf8_bytes_filled = 4;
|
||||||
|
valid_pair = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (!valid_pair)
|
||||||
{
|
{
|
||||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
|
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
|
||||||
utf8_bytes_filled = 1;
|
utf8_bytes_filled = 1;
|
||||||
|
|||||||
@@ -7382,17 +7382,30 @@ struct wide_string_input_helper<BaseInputAdapter, 2>
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!input.empty()))
|
// A supplementary code point is a high surrogate (0xD800..0xDBFF)
|
||||||
|
// followed by a low surrogate (0xDC00..0xDFFF). A lone low
|
||||||
|
// surrogate, a high surrogate at the end of the input, or a high
|
||||||
|
// surrogate followed by any other unit is malformed UTF-16. In
|
||||||
|
// that case the offending unit is passed through unchanged so the
|
||||||
|
// UTF-8 decoder rejects it, matching how \uXXXX surrogate escapes
|
||||||
|
// are handled in the lexer.
|
||||||
|
bool valid_pair = false;
|
||||||
|
if (wc <= 0xDBFF && JSON_HEDLEY_UNLIKELY(!input.empty()))
|
||||||
{
|
{
|
||||||
const auto wc2 = static_cast<unsigned int>(input.get_character());
|
const auto wc2 = static_cast<unsigned int>(input.get_character());
|
||||||
const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu));
|
if (0xDC00 <= wc2 && wc2 <= 0xDFFF)
|
||||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u));
|
{
|
||||||
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));
|
const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu));
|
||||||
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu));
|
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u));
|
||||||
utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (charcode & 0x3Fu));
|
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));
|
||||||
utf8_bytes_filled = 4;
|
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu));
|
||||||
|
utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (charcode & 0x3Fu));
|
||||||
|
utf8_bytes_filled = 4;
|
||||||
|
valid_pair = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (!valid_pair)
|
||||||
{
|
{
|
||||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
|
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
|
||||||
utf8_bytes_filled = 1;
|
utf8_bytes_filled = 1;
|
||||||
@@ -12382,6 +12395,47 @@ class binary_reader
|
|||||||
return get_ubjson_value(get_char ? get_ignore_noop() : current);
|
return get_ubjson_value(get_char ? get_ignore_noop() : current);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief reject a negative UBJSON/BJData string length
|
||||||
|
|
||||||
|
String and key lengths are written with signed integer markers (i, I, l,
|
||||||
|
L). A negative value is malformed; without this check get_string() would
|
||||||
|
silently treat it as an empty string and leave the following bytes to be
|
||||||
|
misread as the next value. This mirrors the non-negative check the
|
||||||
|
optimized-container count path already performs in get_ubjson_size_value.
|
||||||
|
|
||||||
|
@param[in] len the string length read from the input
|
||||||
|
@return whether the length is valid (non-negative)
|
||||||
|
*/
|
||||||
|
template<typename NumberType>
|
||||||
|
bool check_ubjson_string_length(const NumberType len)
|
||||||
|
{
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(len < 0))
|
||||||
|
{
|
||||||
|
return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
|
||||||
|
exception_message(input_format, "string length must not be negative", "string"), nullptr));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief create the error message for a missing/invalid length-type marker
|
||||||
|
|
||||||
|
Used both when reading a UBJSON/BJData string and when reading an optimized
|
||||||
|
container size. The accepted markers depend on the input format, and the
|
||||||
|
size variant appends " after '#'" via @a infix.
|
||||||
|
|
||||||
|
@param[in] last_token the offending byte as returned by get_token_string()
|
||||||
|
@param[in] infix extra context inserted after the marker list
|
||||||
|
@return the formatted error message
|
||||||
|
*/
|
||||||
|
std::string unexpected_length_type_message(const std::string& last_token, const char* infix) const
|
||||||
|
{
|
||||||
|
return concat("expected length type specification (",
|
||||||
|
input_format == input_format_t::bjdata ? "U, i, u, I, m, l, M, L" : "U, i, I, l, L",
|
||||||
|
")", infix, "; last byte: 0x", last_token);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief reads a UBJSON string
|
@brief reads a UBJSON string
|
||||||
|
|
||||||
@@ -12419,25 +12473,25 @@ class binary_reader
|
|||||||
case 'i':
|
case 'i':
|
||||||
{
|
{
|
||||||
std::int8_t len{};
|
std::int8_t len{};
|
||||||
return get_number(input_format, len) && get_string(input_format, len, result);
|
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'I':
|
case 'I':
|
||||||
{
|
{
|
||||||
std::int16_t len{};
|
std::int16_t len{};
|
||||||
return get_number(input_format, len) && get_string(input_format, len, result);
|
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'l':
|
case 'l':
|
||||||
{
|
{
|
||||||
std::int32_t len{};
|
std::int32_t len{};
|
||||||
return get_number(input_format, len) && get_string(input_format, len, result);
|
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'L':
|
case 'L':
|
||||||
{
|
{
|
||||||
std::int64_t len{};
|
std::int64_t len{};
|
||||||
return get_number(input_format, len) && get_string(input_format, len, result);
|
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'u':
|
case 'u':
|
||||||
@@ -12474,17 +12528,8 @@ class binary_reader
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
auto last_token = get_token_string();
|
auto last_token = get_token_string();
|
||||||
std::string message;
|
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
||||||
|
exception_message(input_format, unexpected_length_type_message(last_token, ""), "string"), nullptr));
|
||||||
if (input_format != input_format_t::bjdata)
|
|
||||||
{
|
|
||||||
message = "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
message = "expected length type specification (U, i, u, I, m, l, M, L); last byte: 0x" + last_token;
|
|
||||||
}
|
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, "string"), nullptr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -12763,17 +12808,8 @@ class binary_reader
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
auto last_token = get_token_string();
|
auto last_token = get_token_string();
|
||||||
std::string message;
|
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
||||||
|
exception_message(input_format, unexpected_length_type_message(last_token, " after '#'"), "size"), nullptr));
|
||||||
if (input_format != input_format_t::bjdata)
|
|
||||||
{
|
|
||||||
message = "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
message = "expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0x" + last_token;
|
|
||||||
}
|
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, "size"), nullptr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|||||||
@@ -2721,6 +2721,19 @@ TEST_CASE("BJData")
|
|||||||
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing BJData string: expected length type specification (U, i, u, I, m, l, M, L); last byte: 0x31", json::parse_error&);
|
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing BJData string: expected length type specification (U, i, u, I, m, l, M, L); last byte: 0x31", json::parse_error&);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("negative length")
|
||||||
|
{
|
||||||
|
json _;
|
||||||
|
|
||||||
|
std::vector<uint8_t> const vi = {'S', 'i', 0xFF};
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vi), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing BJData string: string length must not be negative", json::parse_error&);
|
||||||
|
CHECK(json::from_bjdata(vi, true, false).is_discarded());
|
||||||
|
|
||||||
|
std::vector<uint8_t> const vl = {'S', 'l', 0xFF, 0xFF, 0xFF, 0xFF};
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vl), "[json.exception.parse_error.113] parse error at byte 6: syntax error while parsing BJData string: string length must not be negative", json::parse_error&);
|
||||||
|
CHECK(json::from_bjdata(vl, true, false).is_discarded());
|
||||||
|
}
|
||||||
|
|
||||||
SECTION("parse bjdata markers in ubjson")
|
SECTION("parse bjdata markers in ubjson")
|
||||||
{
|
{
|
||||||
// create a single-character string for all number types
|
// create a single-character string for all number types
|
||||||
|
|||||||
@@ -1862,6 +1862,31 @@ TEST_CASE("UBJSON")
|
|||||||
json _;
|
json _;
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(v), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing UBJSON string: expected length type specification (U, i, I, l, L); last byte: 0x31", json::parse_error&);
|
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(v), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing UBJSON string: expected length type specification (U, i, I, l, L); last byte: 0x31", json::parse_error&);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("negative length")
|
||||||
|
{
|
||||||
|
json _;
|
||||||
|
|
||||||
|
std::vector<uint8_t> const vi = {'S', 'i', 0xFF};
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vi), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing UBJSON string: string length must not be negative", json::parse_error&);
|
||||||
|
CHECK(json::from_ubjson(vi, true, false).is_discarded());
|
||||||
|
|
||||||
|
std::vector<uint8_t> const vI = {'S', 'I', 0xFF, 0xFF};
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vI), "[json.exception.parse_error.113] parse error at byte 4: syntax error while parsing UBJSON string: string length must not be negative", json::parse_error&);
|
||||||
|
CHECK(json::from_ubjson(vI, true, false).is_discarded());
|
||||||
|
|
||||||
|
std::vector<uint8_t> const vl = {'S', 'l', 0xFF, 0xFF, 0xFF, 0xFF};
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vl), "[json.exception.parse_error.113] parse error at byte 6: syntax error while parsing UBJSON string: string length must not be negative", json::parse_error&);
|
||||||
|
CHECK(json::from_ubjson(vl, true, false).is_discarded());
|
||||||
|
|
||||||
|
std::vector<uint8_t> const vL = {'S', 'L', 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vL), "[json.exception.parse_error.113] parse error at byte 10: syntax error while parsing UBJSON string: string length must not be negative", json::parse_error&);
|
||||||
|
CHECK(json::from_ubjson(vL, true, false).is_discarded());
|
||||||
|
|
||||||
|
// a length of zero remains valid and yields an empty string
|
||||||
|
std::vector<uint8_t> const v0 = {'S', 'i', 0};
|
||||||
|
CHECK(json::from_ubjson(v0) == json(""));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("array")
|
SECTION("array")
|
||||||
|
|||||||
@@ -53,6 +53,27 @@ TEST_CASE("wide strings")
|
|||||||
std::wstring const w = L"\"\xDBFF";
|
std::wstring const w = L"\"\xDBFF";
|
||||||
json _;
|
json _;
|
||||||
CHECK_THROWS_AS(_ = json::parse(w), json::parse_error&);
|
CHECK_THROWS_AS(_ = json::parse(w), json::parse_error&);
|
||||||
|
|
||||||
|
// the exact message depends on the width of wchar_t: a 16-bit
|
||||||
|
// wchar_t passes the lone surrogate to the UTF-8 decoder unchanged
|
||||||
|
// (rejected as a single ill-formed byte at column 2), while a
|
||||||
|
// 32-bit wchar_t first encodes it as an ill-formed three-byte
|
||||||
|
// sequence (rejected one byte later, at column 3)
|
||||||
|
const char* const error_low_surrogate = sizeof(wchar_t) == 2
|
||||||
|
? "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"<U+0000>'"
|
||||||
|
: "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"\xED\xB0'";
|
||||||
|
const char* const error_high_surrogate = sizeof(wchar_t) == 2
|
||||||
|
? "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"<U+0000>'"
|
||||||
|
: "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"\xED\xA0'";
|
||||||
|
|
||||||
|
// a lone low surrogate cannot start a pair
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::parse(std::wstring{L'"', static_cast<wchar_t>(0xDC00), L'"'}), error_low_surrogate, json::parse_error&);
|
||||||
|
// a high surrogate followed by a non-low-surrogate unit is invalid
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::parse(std::wstring{L'"', static_cast<wchar_t>(0xD800), L'a', L'"'}), error_high_surrogate, json::parse_error&);
|
||||||
|
// a lone low surrogate must not swallow the following unit: pairing
|
||||||
|
// it with any second unit would produce valid UTF-8, so the error
|
||||||
|
// has to report an ill-formed byte at the surrogate's own position
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::parse(std::wstring{L'"', static_cast<wchar_t>(0xDC00), L'a', L'"'}), error_low_surrogate, json::parse_error&);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,11 +89,22 @@ TEST_CASE("wide strings")
|
|||||||
|
|
||||||
SECTION("invalid std::u16string")
|
SECTION("invalid std::u16string")
|
||||||
{
|
{
|
||||||
if (wstring_is_utf16())
|
if (u16string_is_utf16())
|
||||||
{
|
{
|
||||||
std::u16string const w = u"\"\xDBFF";
|
std::u16string const w = u"\"\xDBFF";
|
||||||
json _;
|
json _;
|
||||||
CHECK_THROWS_AS(_ = json::parse(w), json::parse_error&);
|
CHECK_THROWS_AS(_ = json::parse(w), json::parse_error&);
|
||||||
|
|
||||||
|
// a lone low surrogate cannot start a pair
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::parse(std::u16string{u'"', 0xDC00, u'"'}), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"<U+0000>'", json::parse_error&);
|
||||||
|
// a high surrogate followed by a non-low-surrogate unit is invalid
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::parse(std::u16string{u'"', 0xD800, u'a', u'"'}), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"<U+0000>'", json::parse_error&);
|
||||||
|
// a lone low surrogate must not swallow the following unit: pairing
|
||||||
|
// it with any second unit would produce valid UTF-8, so the error
|
||||||
|
// has to report an ill-formed byte at the surrogate's own position
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::parse(std::u16string{u'"', 0xDC00, u'a', u'"'}), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"<U+0000>'", json::parse_error&);
|
||||||
|
// a valid surrogate pair is still decoded (U+1F600)
|
||||||
|
CHECK(json::parse(std::u16string{u'"', 0xD83D, 0xDE00, u'"'}).get<std::string>() == "\xF0\x9F\x98\x80");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user