diff --git a/docs/mkdocs/docs/api/basic_json/operator[].md b/docs/mkdocs/docs/api/basic_json/operator[].md index 3195870e43..8e44810dc4 100644 --- a/docs/mkdocs/docs/api/basic_json/operator[].md +++ b/docs/mkdocs/docs/api/basic_json/operator[].md @@ -79,6 +79,8 @@ Strong exception safety: if an exception occurs, the original value stays intact JSON pointer `ptr` begins with '0'. - Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) if an array index in the passed JSON pointer `ptr` is not a number. + - Throws [`out_of_range.401`](../../home/exceptions.md#jsonexceptionout_of_range401) if an array index in the + passed JSON pointer `ptr` is out of range for the const version. - Throws [`out_of_range.402`](../../home/exceptions.md#jsonexceptionout_of_range402) if the array index '-' is used in the passed JSON pointer `ptr` for the const version. - Throws [`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) if the JSON pointer `ptr` can diff --git a/include/nlohmann/detail/json_pointer.hpp b/include/nlohmann/detail/json_pointer.hpp index 576ba62cca..804cbddbac 100644 --- a/include/nlohmann/detail/json_pointer.hpp +++ b/include/nlohmann/detail/json_pointer.hpp @@ -517,6 +517,7 @@ class json_pointer @throw parse_error.106 if an array index begins with '0' @throw parse_error.109 if an array index was not a number + @throw out_of_range.401 if an array index is out of range @throw out_of_range.402 if the array index '-' is used @throw out_of_range.404 if the JSON pointer can not be resolved */ @@ -542,8 +543,14 @@ class json_pointer JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr)); } - // use unchecked array access - ptr = &ptr->operator[](array_index(reference_token)); + const auto idx = array_index(reference_token); + // the const array operator[] is unchecked, so bounds check here + if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size())) + { + JSON_THROW(detail::out_of_range::create(401, detail::concat( + "array index ", std::to_string(idx), " is out of range"), ptr)); + } + ptr = &ptr->operator[](idx); break; } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 5ead5275a3..1a6f0c2a64 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -15940,6 +15940,7 @@ class json_pointer @throw parse_error.106 if an array index begins with '0' @throw parse_error.109 if an array index was not a number + @throw out_of_range.401 if an array index is out of range @throw out_of_range.402 if the array index '-' is used @throw out_of_range.404 if the JSON pointer can not be resolved */ @@ -15965,8 +15966,14 @@ class json_pointer JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr)); } - // use unchecked array access - ptr = &ptr->operator[](array_index(reference_token)); + const auto idx = array_index(reference_token); + // the const array operator[] is unchecked, so bounds check here + if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size())) + { + JSON_THROW(detail::out_of_range::create(401, detail::concat( + "array index ", std::to_string(idx), " is out of range"), ptr)); + } + ptr = &ptr->operator[](idx); break; } diff --git a/tests/src/unit-json_pointer.cpp b/tests/src/unit-json_pointer.cpp index a8ed4a89e3..7f85e450be 100644 --- a/tests/src/unit-json_pointer.cpp +++ b/tests/src/unit-json_pointer.cpp @@ -378,11 +378,15 @@ TEST_CASE("JSON pointers") CHECK(j["/2"_json_pointer] == j[2]); // assign to nonexisting index + CHECK_THROWS_WITH_AS(j["/3"_json_pointer], + "[json.exception.out_of_range.401] array index 3 is out of range", json::out_of_range&); CHECK_THROWS_WITH_AS(j.at("/3"_json_pointer), "[json.exception.out_of_range.401] array index 3 is out of range", json::out_of_range&); CHECK(!j.contains("/3"_json_pointer)); // assign to nonexisting index (with gap) + CHECK_THROWS_WITH_AS(j["/5"_json_pointer], + "[json.exception.out_of_range.401] array index 5 is out of range", json::out_of_range&); CHECK_THROWS_WITH_AS(j.at("/5"_json_pointer), "[json.exception.out_of_range.401] array index 5 is out of range", json::out_of_range&); CHECK(!j.contains("/5"_json_pointer));