refactor: Limit JSON array size (#63)

This commit is contained in:
Alex Kremer
2026-04-10 18:37:53 +01:00
committed by Ed Hennis
parent 2ddef8c87d
commit 377b155ddc
2 changed files with 48 additions and 8 deletions

View File

@@ -26,6 +26,13 @@
namespace ripple {
/** Maximum JSON object nesting depth permitted during parsing. */
inline constexpr std::size_t maxSTParsedJSONDepth = 64;
/** Maximum number of elements permitted in any JSON array field during parsing.
Requests exceeding this limit are rejected with an invalidParams error. */
inline constexpr std::size_t maxSTParsedJSONArraySize = 512;
/** Holds the serialized result of parsing an input JSON object.
This does validation and checking on the provided JSON.
*/

View File

@@ -160,6 +160,16 @@ array_expected(std::string const& object, std::string const& field)
"Field '" + make_name(object, field) + "' must be a JSON array.");
}
static inline Json::Value
array_too_big(std::string const& object, std::string const& field)
{
return RPC::make_error(
rpcINVALID_PARAMS,
"Field '" + make_name(object, field) +
"' exceeds allowed JSON array size of " +
std::to_string(maxSTParsedJSONArraySize) + " elements per field.");
}
static inline Json::Value
string_expected(std::string const& object, std::string const& field)
{
@@ -743,6 +753,12 @@ parseLeaf(
return ret;
}
if (not value.isNull() and value.size() > maxSTParsedJSONArraySize)
{
error = array_too_big(json_name, fieldName);
return ret;
}
try
{
STVector256 tail(field);
@@ -770,6 +786,12 @@ parseLeaf(
return ret;
}
if (not value.isNull() and value.size() > maxSTParsedJSONArraySize)
{
error = array_too_big(json_name, fieldName);
return ret;
}
try
{
STPathSet tail(field);
@@ -786,6 +808,15 @@ parseLeaf(
return ret;
}
if (not value[i].isNull() and
value[i].size() > maxSTParsedJSONArraySize)
{
std::stringstream ss;
ss << fieldName << "[" << i << "]";
error = array_too_big(json_name, ss.str());
return ret;
}
for (Json::UInt j = 0; value[i].isValidIndex(j); ++j)
{
std::stringstream ss;
@@ -980,8 +1011,6 @@ parseLeaf(
return ret;
}
static int const maxDepth = 64;
// Forward declaration since parseObject() and parseArray() call each other.
static std::optional<detail::STVar>
parseArray(
@@ -1005,7 +1034,7 @@ parseObject(
return std::nullopt;
}
if (depth > maxDepth)
if (depth > maxSTParsedJSONDepth)
{
error = too_deep(json_name);
return std::nullopt;
@@ -1018,7 +1047,6 @@ parseObject(
for (auto const& fieldName : json.getMemberNames())
{
Json::Value const& value = json[fieldName];
auto const& field = SField::getField(fieldName);
if (field == sfInvalid)
@@ -1128,12 +1156,18 @@ parseArray(
return std::nullopt;
}
if (depth > maxDepth)
if (depth > maxSTParsedJSONDepth)
{
error = too_deep(json_name);
return std::nullopt;
}
if (not json.isNull() and json.size() > maxSTParsedJSONArraySize)
{
error = array_too_big(json_name, "");
return std::nullopt;
}
try
{
STArray tail(inName);
@@ -1151,10 +1185,9 @@ parseArray(
}
// TODO: There doesn't seem to be a nice way to get just the
// first/only key in an object without copying all keys into
// a vector
// first/only key in an object without copying all keys into a
// vector
std::string const objectName(json[i].getMemberNames()[0]);
;
auto const& nameField(SField::getField(objectName));
if (nameField == sfInvalid)