test: Add JSON array size tests (#7592)

This commit is contained in:
Alex Kremer
2026-07-14 13:35:04 +01:00
committed by GitHub
parent e1d4f357dc
commit 73e97b8b84

View File

@@ -19,6 +19,7 @@
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <stdexcept>
@@ -1979,7 +1980,7 @@ class STParsedJSON_test : public beast::unit_test::Suite
json::Value j;
json::Value obj(json::ValueType::Object);
json::Value* current = &obj;
for (int i = 0; i < 63; ++i)
for (std::size_t i = 1; i < kMaxParsedJsonDepth; ++i)
{
json::Value const next(json::ValueType::Object);
(*current)[sfTransactionMetaData] = next;
@@ -1998,7 +1999,7 @@ class STParsedJSON_test : public beast::unit_test::Suite
json::Value j;
json::Value obj(json::ValueType::Object);
json::Value* current = &obj;
for (int i = 0; i < 64; ++i)
for (std::size_t i = 1; i <= kMaxParsedJsonDepth; ++i)
{
json::Value const next(json::ValueType::Object);
(*current)[sfTransactionMetaData] = next;
@@ -2153,6 +2154,191 @@ class STParsedJSON_test : public beast::unit_test::Suite
}
}
void
testArrayBoundsChecking()
{
testcase("Array bounds checking");
auto const limitStr = std::to_string(kMaxParsedJsonArraySize) + " elements per field.";
// parseArray rejects oversized STI_ARRAY (SignerEntries)
{
json::Value jv;
json::Value entries(json::ValueType::Array);
for (std::size_t i = 0; i <= kMaxParsedJsonArraySize; ++i)
{
json::Value entry;
entry["SignerEntry"]["Account"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
entry["SignerEntry"]["SignerWeight"] = 1;
entries.append(entry);
}
jv[sfSignerEntries] = entries;
STParsedJSONObject parsed("test", jv);
BEAST_EXPECT(!parsed.object);
BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
BEAST_EXPECT(
parsed.error[jss::error_message] ==
"Field 'test.SignerEntries' exceeds allowed JSON array size of " + limitStr);
}
// parseObject rejects oversized STI_VECTOR256 (Amendments)
{
json::Value jv;
json::Value amendments(json::ValueType::Array);
std::string const hash(64, '0');
for (std::size_t i = 0; i <= kMaxParsedJsonArraySize; ++i)
amendments.append(hash);
jv[sfAmendments] = amendments;
STParsedJSONObject parsed("test", jv);
BEAST_EXPECT(!parsed.object);
BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
BEAST_EXPECT(
parsed.error[jss::error_message] ==
"Field 'test.Amendments' exceeds allowed JSON array size of " + limitStr);
}
// parseObject accepts exactly kMaxParsedJsonArraySize STI_VECTOR256 (Amendments)
{
json::Value jv;
json::Value amendments(json::ValueType::Array);
std::string const hash(64, '0');
for (std::size_t i = 0; i < kMaxParsedJsonArraySize; ++i)
amendments.append(hash);
jv[sfAmendments] = amendments;
STParsedJSONObject const parsed("test", jv);
BEAST_EXPECT(parsed.object);
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
auto const arrSize = parsed.object->getFieldV256(sfAmendments).size();
BEAST_EXPECT(arrSize == kMaxParsedJsonArraySize);
}
// parseObject rejects oversized STI_PATHSET (outer array)
{
json::Value jv;
json::Value paths(json::ValueType::Array);
for (std::size_t i = 0; i <= kMaxParsedJsonArraySize; ++i)
{
json::Value path(json::ValueType::Array);
json::Value hop;
hop["account"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
path.append(hop);
paths.append(path);
}
jv[sfPaths] = paths;
STParsedJSONObject parsed("test", jv);
BEAST_EXPECT(!parsed.object);
BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
BEAST_EXPECT(
parsed.error[jss::error_message] ==
"Field 'test.Paths' exceeds allowed JSON array size of " + limitStr);
}
// parseObject accepts exactly kMaxParsedJsonArraySize STI_PATHSET (outer array)
{
json::Value jv;
json::Value paths(json::ValueType::Array);
for (std::size_t i = 0; i < kMaxParsedJsonArraySize; ++i)
{
json::Value path(json::ValueType::Array);
json::Value hop;
hop["account"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
path.append(hop);
paths.append(path);
}
jv[sfPaths] = paths;
STParsedJSONObject const parsed("test", jv);
BEAST_EXPECT(parsed.object);
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
auto const arrSize = parsed.object->getFieldPathSet(sfPaths).size();
BEAST_EXPECT(arrSize == kMaxParsedJsonArraySize);
}
// parseObject rejects oversized STI_PATHSET (inner path hop array)
{
json::Value jv;
json::Value paths(json::ValueType::Array);
json::Value path(json::ValueType::Array);
json::Value hop;
hop["account"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
for (std::size_t i = 0; i <= kMaxParsedJsonArraySize; ++i)
path.append(hop);
paths.append(path);
jv[sfPaths] = paths;
STParsedJSONObject parsed("test", jv);
BEAST_EXPECT(!parsed.object);
BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
BEAST_EXPECT(
parsed.error[jss::error_message] ==
"Field 'test.Paths[0]' exceeds allowed JSON array size of " + limitStr);
}
// parseObject accepts exactly kMaxParsedJsonArraySize hops in a single STI_PATHSET path
{
json::Value jv;
json::Value paths(json::ValueType::Array);
json::Value path(json::ValueType::Array);
json::Value hop;
hop["account"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
for (std::size_t i = 0; i < kMaxParsedJsonArraySize; ++i)
path.append(hop);
paths.append(path);
jv[sfPaths] = paths;
STParsedJSONObject const parsed("test", jv);
BEAST_EXPECT(parsed.object);
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
auto const arrSize = parsed.object->getFieldPathSet(sfPaths)[0].size();
BEAST_EXPECT(arrSize == kMaxParsedJsonArraySize);
}
// parseArray accepts exactly kMaxParsedJsonArraySize Memos (boundary)
{
json::Value jv;
json::Value memos(json::ValueType::Array);
for (std::size_t i = 0; i < kMaxParsedJsonArraySize; ++i)
{
json::Value memo;
memo["Memo"] = json::Value(json::ValueType::Object);
memo["Memo"]["MemoData"] = "00";
memos.append(memo);
}
jv[sfMemos] = memos;
STParsedJSONObject const parsed("test", jv);
BEAST_EXPECT(parsed.object);
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
auto const arrSize = parsed.object->getFieldArray(sfMemos).size();
BEAST_EXPECT(arrSize == kMaxParsedJsonArraySize);
}
// parseArray rejects one more than kMaxParsedJsonArraySize Memos
{
json::Value jv;
json::Value memos(json::ValueType::Array);
for (std::size_t i = 0; i <= kMaxParsedJsonArraySize; ++i)
{
json::Value memo;
memo["Memo"] = json::Value(json::ValueType::Object);
memo["Memo"]["MemoData"] = "00";
memos.append(memo);
}
jv[sfMemos] = memos;
STParsedJSONObject parsed("test", jv);
BEAST_EXPECT(!parsed.object);
BEAST_EXPECT(parsed.error[jss::error] == "invalidParams");
BEAST_EXPECT(
parsed.error[jss::error_message] ==
"Field 'test.Memos' exceeds allowed JSON array size of " + limitStr);
}
}
void
testEdgeCases()
{
@@ -2420,6 +2606,7 @@ class STParsedJSON_test : public beast::unit_test::Suite
testNumber();
testObject();
testArray();
testArrayBoundsChecking();
testEdgeCases();
}
};