Skip to content

Commit e7675f7

Browse files
Merge pull request #1160 from PowerGridModel/feature/sanitize-json-deserialization-error
fix JSON deserializer message dump when token is extremely long
2 parents 2646bc9 + c816bd0 commit e7675f7

File tree

2 files changed

+51
-9
lines changed

2 files changed

+51
-9
lines changed

power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/serialization/deserializer.hpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ struct JsonMapArrayData {
4848
};
4949

5050
struct JsonSAXVisitor {
51+
static constexpr size_t max_error_message_token_length = 100;
52+
5153
msgpack::packer<msgpack::sbuffer> top_packer() {
5254
if (data_buffers.empty()) {
5355
throw SerializationError{"Json root should be a map!\n"};
@@ -122,12 +124,13 @@ struct JsonSAXVisitor {
122124
return true;
123125
}
124126

125-
[[noreturn]] static bool parse_error(std::size_t position, std::string const& last_token,
126-
json::exception const& ex) {
127-
std::stringstream ss;
128-
ss << "Parse error in JSON. Position: " << position << ", last token: " << last_token
129-
<< ". Exception message: " << ex.what() << '\n';
130-
throw SerializationError{ss.str()};
127+
[[noreturn]] static bool parse_error(std::size_t position, std::string_view last_token, json::exception const& ex) {
128+
throw SerializationError{
129+
std::format("Parse error in JSON. Position: {}, Last token: {}. Exception message: {}", position,
130+
last_token.size() > max_error_message_token_length
131+
? std::format("{}...[truncated]", last_token.substr(0, max_error_message_token_length))
132+
: last_token,
133+
ex.what())};
131134
}
132135

133136
std::stack<JsonMapArrayData> data_buffers;

tests/cpp_unit_tests/test_deserializer.cpp

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ constexpr std::string_view json_batch = R"(
252252
}
253253
)";
254254

255-
void check_error(std::string_view json, char const* err_msg) {
255+
void check_error(std::string_view json, std::string_view err_msg) {
256256
std::vector<NodeInput> node(1);
257257

258258
auto const run = [&]() {
@@ -261,9 +261,8 @@ void check_error(std::string_view json, char const* err_msg) {
261261
deserializer.parse();
262262
};
263263

264-
CHECK_THROWS_WITH_AS(run(), doctest::Contains(err_msg), std::exception);
264+
CHECK_THROWS_WITH_AS(run(), doctest::Contains(err_msg.data()), std::exception);
265265
}
266-
267266
} // namespace
268267

269268
TEST_CASE("Deserializer") {
@@ -633,6 +632,46 @@ true}]}})";
633632
true}]}]})";
634633
check_error(wrong_type_dict, "Position of error: data/0/node/0/id");
635634
}
635+
636+
SUBCASE("Last token") {
637+
CHECK(detail::JsonSAXVisitor::max_error_message_token_length == 100);
638+
639+
SUBCASE("Token is way too long") {
640+
constexpr std::string_view json_invalid =
641+
R"({"version": "1.0", "type": "input", "is_batch": false, "attributes": {}, "data": {"this is an extremely long token that exceeds the maximum length allowed by the deserializer which is set to one hundred characters":})";
642+
643+
std::string const short_token_string{"\"this is an extremely long token that exceeds the maximum length "
644+
"allowed by the deserializer which i"};
645+
CHECK(short_token_string.size() == detail::JsonSAXVisitor::max_error_message_token_length);
646+
647+
check_error(json_invalid,
648+
std::format("Last token: {}...[truncated]. Exception message:", short_token_string));
649+
}
650+
SUBCASE("Token is just too long") {
651+
constexpr std::string_view json_invalid =
652+
R"({"version": "1.0", "type": "input", "is_batch": false, "attributes": {}, "data": {"this is a token that is just too long. It barely exceeds the maximum length allowed in the errors":})";
653+
654+
std::string const full_token_string{"\"this is a token that is just too long. It barely exceeds the "
655+
"maximum length allowed in the errors\":}"};
656+
std::string const truncated_token_string{
657+
full_token_string.substr(0, detail::JsonSAXVisitor::max_error_message_token_length)};
658+
CHECK(full_token_string.size() == detail::JsonSAXVisitor::max_error_message_token_length + 1);
659+
CHECK(truncated_token_string.size() == detail::JsonSAXVisitor::max_error_message_token_length);
660+
661+
check_error(json_invalid,
662+
std::format("Last token: {}...[truncated]. Exception message:", truncated_token_string));
663+
}
664+
SUBCASE("Token is barely short enough") {
665+
constexpr std::string_view json_invalid =
666+
R"({"version": "1.0", "type": "input", "is_batch": false, "attributes": {}, "data": {"this is a token that is just too long. It fits barely in the maximum length allowed in the error":})";
667+
668+
std::string const full_token_string{"\"this is a token that is just too long. It fits barely in the "
669+
"maximum length allowed in the error\":}"};
670+
CHECK(full_token_string.size() == detail::JsonSAXVisitor::max_error_message_token_length);
671+
672+
check_error(json_invalid, std::format("Last token: {}. Exception message:", full_token_string));
673+
}
674+
}
636675
}
637676

638677
} // namespace power_grid_model::meta_data

0 commit comments

Comments
 (0)