mirror of
				https://github.com/XRPLF/clio.git
				synced 2025-11-04 11:55:51 +00:00 
			
		
		
		
	fix: Workaround large number validation and parsing (#2608)
Fixes #2586
This commit is contained in:
		@@ -20,8 +20,13 @@
 | 
			
		||||
#include "util/JsonUtils.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/json/parse.hpp>
 | 
			
		||||
#include <boost/json/value.hpp>
 | 
			
		||||
#include <gtest/gtest.h>
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <limits>
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
 | 
			
		||||
TEST(JsonUtils, RemoveSecrets)
 | 
			
		||||
{
 | 
			
		||||
    auto json = boost::json::parse(R"JSON({
 | 
			
		||||
@@ -60,3 +65,26 @@ TEST(JsonUtils, RemoveSecrets)
 | 
			
		||||
    EXPECT_EQ(json2.at("seed_hex").as_string(), "*");
 | 
			
		||||
    EXPECT_EQ(json2.at("passphrase").as_string(), "*");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST(JsonUtils, integralValueAs)
 | 
			
		||||
{
 | 
			
		||||
    auto const expectedResultUint64 = static_cast<uint64_t>(std::numeric_limits<int32_t>::max()) + 1u;
 | 
			
		||||
    auto const uint64Json = boost::json::value(expectedResultUint64);
 | 
			
		||||
    EXPECT_EQ(util::integralValueAs<int32_t>(uint64Json), std::numeric_limits<int32_t>::min());
 | 
			
		||||
    EXPECT_EQ(util::integralValueAs<uint32_t>(uint64Json), expectedResultUint64);
 | 
			
		||||
    EXPECT_EQ(util::integralValueAs<int64_t>(uint64Json), expectedResultUint64);
 | 
			
		||||
    EXPECT_EQ(util::integralValueAs<uint64_t>(uint64Json), expectedResultUint64);
 | 
			
		||||
 | 
			
		||||
    auto const expectedResultInt64 = static_cast<int64_t>(std::numeric_limits<int32_t>::max()) + 1u;
 | 
			
		||||
    auto const int64Json = boost::json::value(expectedResultInt64);
 | 
			
		||||
    EXPECT_EQ(util::integralValueAs<int32_t>(int64Json), std::numeric_limits<int32_t>::min());
 | 
			
		||||
    EXPECT_EQ(util::integralValueAs<uint32_t>(int64Json), expectedResultInt64);
 | 
			
		||||
    EXPECT_EQ(util::integralValueAs<int64_t>(int64Json), expectedResultInt64);
 | 
			
		||||
    EXPECT_EQ(util::integralValueAs<uint64_t>(int64Json), expectedResultInt64);
 | 
			
		||||
 | 
			
		||||
    auto const doubleJson = boost::json::value(3.14);
 | 
			
		||||
    EXPECT_THROW(util::integralValueAs<int>(doubleJson), std::logic_error);
 | 
			
		||||
 | 
			
		||||
    auto const stringJson = boost::json::value("not a number");
 | 
			
		||||
    EXPECT_THROW(util::integralValueAs<int>(stringJson), std::logic_error);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,7 @@
 | 
			
		||||
#include <xrpl/protocol/ErrorCodes.h>
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <limits>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <string_view>
 | 
			
		||||
 | 
			
		||||
@@ -50,39 +51,97 @@ namespace json = boost::json;
 | 
			
		||||
 | 
			
		||||
class RPCBaseTest : public virtual ::testing::Test {};
 | 
			
		||||
 | 
			
		||||
TEST_F(RPCBaseTest, CheckType)
 | 
			
		||||
TEST_F(RPCBaseTest, CheckTypeString)
 | 
			
		||||
{
 | 
			
		||||
    auto const jstr = json::value("a string");
 | 
			
		||||
    ASSERT_TRUE(checkType<string>(jstr));
 | 
			
		||||
    ASSERT_FALSE(checkType<int>(jstr));
 | 
			
		||||
    auto const jString = json::value("a string");
 | 
			
		||||
    ASSERT_TRUE(checkType<string>(jString));
 | 
			
		||||
    ASSERT_FALSE(checkType<int>(jString));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    auto const juint = json::value(123u);
 | 
			
		||||
    ASSERT_TRUE(checkType<uint32_t>(juint));
 | 
			
		||||
    ASSERT_TRUE(checkType<int32_t>(juint));
 | 
			
		||||
    ASSERT_FALSE(checkType<bool>(juint));
 | 
			
		||||
TEST_F(RPCBaseTest, CheckTypeUint)
 | 
			
		||||
{
 | 
			
		||||
    auto const jUint = json::value(123u);
 | 
			
		||||
    ASSERT_TRUE(checkType<uint32_t>(jUint));
 | 
			
		||||
    ASSERT_TRUE(checkType<int32_t>(jUint));
 | 
			
		||||
    ASSERT_FALSE(checkType<bool>(jUint));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    auto jint = json::value(123);
 | 
			
		||||
    ASSERT_TRUE(checkType<int32_t>(jint));
 | 
			
		||||
    ASSERT_TRUE(checkType<uint32_t>(jint));
 | 
			
		||||
    ASSERT_FALSE(checkType<bool>(jint));
 | 
			
		||||
TEST_F(RPCBaseTest, CheckTypeInt)
 | 
			
		||||
{
 | 
			
		||||
    auto jInt = json::value(123);
 | 
			
		||||
    ASSERT_TRUE(checkType<int32_t>(jInt));
 | 
			
		||||
    ASSERT_TRUE(checkType<uint32_t>(jInt));
 | 
			
		||||
    ASSERT_FALSE(checkType<bool>(jInt));
 | 
			
		||||
 | 
			
		||||
    jint = json::value(-123);
 | 
			
		||||
    ASSERT_TRUE(checkType<int32_t>(jint));
 | 
			
		||||
    ASSERT_FALSE(checkType<uint32_t>(jint));
 | 
			
		||||
    ASSERT_FALSE(checkType<bool>(jint));
 | 
			
		||||
    jInt = json::value(-123);
 | 
			
		||||
    ASSERT_TRUE(checkType<int32_t>(jInt));
 | 
			
		||||
    ASSERT_FALSE(checkType<uint32_t>(jInt));  // Unsigned can't be negative
 | 
			
		||||
    ASSERT_FALSE(checkType<bool>(jInt));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    auto const jbool = json::value(true);
 | 
			
		||||
    ASSERT_TRUE(checkType<bool>(jbool));
 | 
			
		||||
    ASSERT_FALSE(checkType<int>(jbool));
 | 
			
		||||
TEST_F(RPCBaseTest, CheckTypeBool)
 | 
			
		||||
{
 | 
			
		||||
    auto const jBool = json::value(true);
 | 
			
		||||
    ASSERT_TRUE(checkType<bool>(jBool));
 | 
			
		||||
    ASSERT_FALSE(checkType<int>(jBool));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    auto const jdouble = json::value(0.123);
 | 
			
		||||
    ASSERT_TRUE(checkType<double>(jdouble));
 | 
			
		||||
    ASSERT_TRUE(checkType<float>(jdouble));
 | 
			
		||||
    ASSERT_FALSE(checkType<bool>(jdouble));
 | 
			
		||||
TEST_F(RPCBaseTest, CheckTypeDouble)
 | 
			
		||||
{
 | 
			
		||||
    auto const jDouble = json::value(0.123);
 | 
			
		||||
    ASSERT_TRUE(checkType<double>(jDouble));
 | 
			
		||||
    ASSERT_TRUE(checkType<float>(jDouble));
 | 
			
		||||
    ASSERT_FALSE(checkType<bool>(jDouble));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    auto const jarr = json::value({1, 2, 3});
 | 
			
		||||
    ASSERT_TRUE(checkType<json::array>(jarr));
 | 
			
		||||
    ASSERT_FALSE(checkType<int>(jarr));
 | 
			
		||||
TEST_F(RPCBaseTest, CheckTypeArray)
 | 
			
		||||
{
 | 
			
		||||
    auto const jArr = json::value({1, 2, 3});
 | 
			
		||||
    ASSERT_TRUE(checkType<json::array>(jArr));
 | 
			
		||||
    ASSERT_FALSE(checkType<int>(jArr));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(RPCBaseTest, CheckTypeAndClampValueUnchanged)
 | 
			
		||||
{
 | 
			
		||||
    auto jUint = json::value(123u);
 | 
			
		||||
    ASSERT_TRUE(checkTypeAndClamp<uint32_t>(jUint));
 | 
			
		||||
    ASSERT_EQ(jUint.as_uint64(), 123u);
 | 
			
		||||
    ASSERT_TRUE(checkTypeAndClamp<int32_t>(jUint));
 | 
			
		||||
    ASSERT_EQ(jUint.as_uint64(), 123u);
 | 
			
		||||
 | 
			
		||||
    auto jInt = json::value(123);
 | 
			
		||||
    ASSERT_TRUE(checkTypeAndClamp<int32_t>(jInt));
 | 
			
		||||
    ASSERT_EQ(jInt.as_int64(), 123);
 | 
			
		||||
    ASSERT_TRUE(checkTypeAndClamp<uint32_t>(jInt));
 | 
			
		||||
    ASSERT_EQ(jInt.as_int64(), 123);
 | 
			
		||||
 | 
			
		||||
    jInt = json::value(-123);
 | 
			
		||||
    ASSERT_TRUE(checkTypeAndClamp<int32_t>(jInt));
 | 
			
		||||
    ASSERT_EQ(jInt.as_int64(), -123);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(RPCBaseTest, CheckTypeAndClampInvalidValues)
 | 
			
		||||
{
 | 
			
		||||
    auto jInt = json::value(-123);
 | 
			
		||||
    ASSERT_FALSE(checkTypeAndClamp<uint32_t>(jInt));  // Unsigned can't be negative
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(RPCBaseTest, CheckTypeAndClampOverflow)
 | 
			
		||||
{
 | 
			
		||||
    auto jBigUint = json::value(std::numeric_limits<uint64_t>::max());
 | 
			
		||||
    ASSERT_TRUE(checkTypeAndClamp<uint32_t>(jBigUint));
 | 
			
		||||
    ASSERT_EQ(jBigUint.as_uint64(), std::numeric_limits<uint32_t>::max());
 | 
			
		||||
 | 
			
		||||
    auto jBigInt = json::value(std::numeric_limits<int64_t>::max());
 | 
			
		||||
    ASSERT_TRUE(checkTypeAndClamp<int32_t>(jBigInt));
 | 
			
		||||
    ASSERT_EQ(jBigInt.as_int64(), std::numeric_limits<int32_t>::max());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(RPCBaseTest, CheckTypeAndClampUnderflow)
 | 
			
		||||
{
 | 
			
		||||
    auto jLowInt = json::value(std::numeric_limits<int64_t>::min());
 | 
			
		||||
    ASSERT_TRUE(checkTypeAndClamp<int32_t>(jLowInt));
 | 
			
		||||
    ASSERT_EQ(jLowInt.as_int64(), std::numeric_limits<int32_t>::min());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(RPCBaseTest, TypeValidator)
 | 
			
		||||
@@ -203,6 +262,18 @@ TEST_F(RPCBaseTest, MinValidator)
 | 
			
		||||
    ASSERT_FALSE(spec.process(failingInput));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(RPCBaseTest, MinValidatorAfterType)
 | 
			
		||||
{
 | 
			
		||||
    auto spec = RpcSpec{
 | 
			
		||||
        {"amount", Type<std::uint32_t>{}, Min{std::numeric_limits<uint32_t>::max()}},
 | 
			
		||||
        {"amount2", Type<std::int32_t>{}, Min{std::numeric_limits<int32_t>::max()}},
 | 
			
		||||
        {"amount3", Type<std::int32_t>{}, Min{std::numeric_limits<int32_t>::min()}},
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    auto bigInput = json::parse(R"JSON({ "amount": 9999999999, "amount2": 9999999999, "amount3": -9999999999 })JSON");
 | 
			
		||||
    ASSERT_TRUE(spec.process(bigInput));  // type check clamps to type's max/min value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(RPCBaseTest, MaxValidator)
 | 
			
		||||
{
 | 
			
		||||
    auto spec = RpcSpec{
 | 
			
		||||
@@ -219,6 +290,18 @@ TEST_F(RPCBaseTest, MaxValidator)
 | 
			
		||||
    ASSERT_FALSE(spec.process(failingInput));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(RPCBaseTest, MaxValidatorAfterType)
 | 
			
		||||
{
 | 
			
		||||
    auto spec = RpcSpec{
 | 
			
		||||
        {"amount", Type<std::uint32_t>{}, Max{std::numeric_limits<uint32_t>::max()}},
 | 
			
		||||
        {"amount2", Type<std::int32_t>{}, Max{std::numeric_limits<int32_t>::max()}},
 | 
			
		||||
        {"amount3", Type<std::int32_t>{}, Max{std::numeric_limits<int32_t>::min()}},
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    auto bigInput = json::parse(R"JSON({ "amount": 9999999999, "amount2": 9999999999, "amount3": -9999999999 })JSON");
 | 
			
		||||
    ASSERT_TRUE(spec.process(bigInput));  // type check clamps to type's min/max value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(RPCBaseTest, OneOfValidator)
 | 
			
		||||
{
 | 
			
		||||
    auto spec = RpcSpec{
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user