diff --git a/API-CHANGELOG.md b/API-CHANGELOG.md index bc3672588e..c853cfb07c 100644 --- a/API-CHANGELOG.md +++ b/API-CHANGELOG.md @@ -54,6 +54,7 @@ This section contains changes targeting a future version. - `submit`: The `fail_hard` field now returns an error if the value is not a boolean. [#6529](https://github.com/XRPLF/rippled/pull/6529) - `subscribe`: The `taker` field in the `books` array now returns `actMalformed` instead of `badIssuer` if the value is not a valid account. [#6529](https://github.com/XRPLF/rippled/pull/6529) - Fixed a bug in `Forwarded` HTTP header parsing where the extracted IP address could be incorrect when no comma or semicolon delimiter follows the address. This could cause the server to misidentify a client's IP address when operating behind a reverse proxy. [#6529](https://github.com/XRPLF/rippled/pull/6529) +- `gateway_balances`: The `account` and `ident` fields now return an `invalidParams` error if the value is not a string, instead of an `internal` error. [#7655](https://github.com/XRPLF/rippled/pull/7655) - `account_lines`: The `peer` field now returns an error if the value is not a string. [#7728](https://github.com/XRPLF/rippled/pull/7728) ## XRP Ledger server version 3.1.0 diff --git a/src/test/rpc/GatewayBalances_test.cpp b/src/test/rpc/GatewayBalances_test.cpp index 106b9b5f1a..91d9126f61 100644 --- a/src/test/rpc/GatewayBalances_test.cpp +++ b/src/test/rpc/GatewayBalances_test.cpp @@ -176,6 +176,45 @@ public: }); } + void + testGWBInvalidAccount(FeatureBitset features) + { + testcase("Gateway Balances with non-string account/ident"); + using namespace std::chrono_literals; + using namespace jtx; + Env env(*this, features); + + Account const alice{"alice"}; + env.fund(XRP(10000), alice); + env.close(); + + auto wsc = makeWSClient(env.app().config()); + + // A non-string "account" must be rejected cleanly with invalidParams + // rather than throwing a Json::LogicError that surfaces as internal. + json::Value qry; + qry[jss::account] = 42; + qry[jss::hotwallet] = alice.human(); + + forAllApiVersions([&, this](unsigned apiVersion) { + qry[jss::api_version] = apiVersion; + auto jv = wsc->invoke("gateway_balances", qry); + expect(jv[jss::status] == "error"); + BEAST_EXPECT(jv[jss::result][jss::error] == "invalidParams"); + }); + + // The same applies to a non-string "ident". + json::Value qry2; + qry2[jss::ident] = 42; + + forAllApiVersions([&, this](unsigned apiVersion) { + qry2[jss::api_version] = apiVersion; + auto jv = wsc->invoke("gateway_balances", qry2); + expect(jv[jss::status] == "error"); + BEAST_EXPECT(jv[jss::result][jss::error] == "invalidParams"); + }); + } + void testGWBOverflow() { @@ -280,6 +319,7 @@ public: { testGWB(feature); testGWBApiVersions(feature); + testGWBInvalidAccount(feature); } testGWBWithMPT(); testGWBOverflow(); diff --git a/src/xrpld/rpc/handlers/account/GatewayBalances.cpp b/src/xrpld/rpc/handlers/account/GatewayBalances.cpp index ff19d1d1e5..041e878a3f 100644 --- a/src/xrpld/rpc/handlers/account/GatewayBalances.cpp +++ b/src/xrpld/rpc/handlers/account/GatewayBalances.cpp @@ -63,6 +63,12 @@ doGatewayBalances(rpc::JsonContext& context) if (!(params.isMember(jss::account) || params.isMember(jss::ident))) return rpc::missingFieldError(jss::account); + if (params.isMember(jss::account) && !params[jss::account].isString()) + return rpc::invalidFieldError(jss::account); + + if (params.isMember(jss::ident) && !params[jss::ident].isString()) + return rpc::invalidFieldError(jss::ident); + std::string const strIdent( params.isMember(jss::account) ? params[jss::account].asString() : params[jss::ident].asString());