Skip to content

Commit 6e1a8c1

Browse files
MarcoFalkePastaPastaPasta
authored andcommitted
Merge bitcoin#26628: RPC: Reject RPC requests with same named parameter specified multiple times
8c3ff7d test: Suggested cleanups for rpc_namedparams test (Ryan Ofsky) d1ca563 bitcoin-cli: Make it an error to specify the "args" parameter two different ways (Ryan Ofsky) 6bd1d20 rpc: Make it an error server-side to specify same named parameter multiple times (Ryan Ofsky) e2c3b18 test: Add RPC tests for same named parameter specified more than once (Ryan Ofsky) Pull request description: Make the JSON-RPC server reject requests with the same named parameter specified multiple times, instead of silently overwriting earlier parameter values with later ones. Generally JSON keys are supposed to unique, and their order isn't supposed to be significant, so having the server silently discard duplicate keys is error-prone. Most likely if an RPC client is sending a request with duplicate keys it means something is wrong with the request and there should be an error. After this change, named parameters are still allowed to specified multiple times on the `bitcoin-cli` command line, since `bitcoin-cli` automatically replaces earlier values with later values before sending the JSON-RPC request. This makes sense, since it's not unusual for the order of command line options to be significant or for later command line options to override earlier ones. ACKs for top commit: MarcoFalke: review ACK 8c3ff7d 🗂 kristapsk: ACK 8c3ff7d stickies-v: ACK 8c3ff7d Tree-SHA512: 2d1357dcc2c171da287aeefc7b333ba4e67babfb64fc14d7fa0940256e18010a2a65054f3bf7fa1571b144d2de8b82d53076111b5f97ba29320cfe84b6ed986f
1 parent 7c28b01 commit 6e1a8c1

File tree

5 files changed

+25
-4
lines changed

5 files changed

+25
-4
lines changed

doc/release-notes-6050.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
JSON-RPC
2+
---
3+
4+
The JSON-RPC server now rejects requests where a parameter is specified multiple times with the same name, instead of silently overwriting earlier parameter values with later ones. (dash#6050)

src/rpc/client.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,9 @@ UniValue RPCConvertNamedValues(const std::string &strMethod, const std::vector<s
305305
std::string name = s.substr(0, pos);
306306
std::string value = s.substr(pos+1);
307307

308+
// Intentionally overwrite earlier named values with later ones as a
309+
// convenience for scripts and command line users that want to merge
310+
// options.
308311
if (!rpcCvtTable.convert(strMethod, name)) {
309312
// insert string value directly
310313
params.pushKV(name, value);
@@ -315,7 +318,10 @@ UniValue RPCConvertNamedValues(const std::string &strMethod, const std::vector<s
315318
}
316319

317320
if (!positional_args.empty()) {
318-
params.pushKV("args", positional_args);
321+
// Use __pushKV instead of pushKV to avoid overwriting an explicit
322+
// "args" value with an implicit one. Let the RPC server handle the
323+
// request as given.
324+
params.__pushKV("args", positional_args);
319325
}
320326

321327
return params;

src/rpc/server.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,10 @@ static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, c
428428
const std::vector<UniValue>& values = in.params.getValues();
429429
std::unordered_map<std::string, const UniValue*> argsIn;
430430
for (size_t i=0; i<keys.size(); ++i) {
431-
argsIn[keys[i]] = &values[i];
431+
auto [_, inserted] = argsIn.emplace(keys[i], &values[i]);
432+
if (!inserted) {
433+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter " + keys[i] + " specified multiple times");
434+
}
432435
}
433436
// Process expected parameters. If any parameters were left unspecified in
434437
// the request before a parameter that was specified, null values need to be

src/test/rpc_tests.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,15 @@ BOOST_FIXTURE_TEST_SUITE(rpc_tests, RPCTestingSetup)
8585

8686
BOOST_AUTO_TEST_CASE(rpc_namedparams)
8787
{
88-
const std::vector<std::string> arg_names{{"arg1", "arg2", "arg3", "arg4", "arg5"}};
88+
const std::vector<std::string> arg_names{"arg1", "arg2", "arg3", "arg4", "arg5"};
8989

9090
// Make sure named arguments are transformed into positional arguments in correct places separated by nulls
9191
BOOST_CHECK_EQUAL(TransformParams(JSON(R"({"arg2": 2, "arg4": 4})"), arg_names).write(), "[null,2,null,4]");
9292

93+
// Make sure named argument specified multiple times raises an exception
94+
BOOST_CHECK_EXCEPTION(TransformParams(JSON(R"({"arg2": 2, "arg2": 4})"), arg_names), UniValue,
95+
HasJSON(R"({"code":-8,"message":"Parameter arg2 specified multiple times"})"));
96+
9397
// Make sure named and positional arguments can be combined.
9498
BOOST_CHECK_EQUAL(TransformParams(JSON(R"({"arg5": 5, "args": [1, 2], "arg4": 4})"), arg_names).write(), "[1,2,null,4,5]");
9599

@@ -101,7 +105,7 @@ BOOST_AUTO_TEST_CASE(rpc_namedparams)
101105
BOOST_CHECK_EXCEPTION(TransformParams(JSON(R"({"args": [1,2,3], "arg4": 4, "arg2": 2})"), arg_names), UniValue,
102106
HasJSON(R"({"code":-8,"message":"Parameter arg2 specified twice both as positional and named argument"})"));
103107

104-
// Make sure extra positional arguments can be passed through to the method implemenation, as long as they don't overlap with named arguments.
108+
// Make sure extra positional arguments can be passed through to the method implementation, as long as they don't overlap with named arguments.
105109
BOOST_CHECK_EQUAL(TransformParams(JSON(R"({"args": [1,2,3,4,5,6,7,8,9,10]})"), arg_names).write(), "[1,2,3,4,5,6,7,8,9,10]");
106110
BOOST_CHECK_EQUAL(TransformParams(JSON(R"([1,2,3,4,5,6,7,8,9,10])"), arg_names).write(), "[1,2,3,4,5,6,7,8,9,10]");
107111
}

test/functional/interface_bitcoin_cli.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ def run_test(self):
8989
assert_raises_rpc_error(-8, "Parameter arg1 specified twice both as positional and named argument", self.nodes[0].cli.echo, 0, 1, arg1=1)
9090
assert_raises_rpc_error(-8, "Parameter arg1 specified twice both as positional and named argument", self.nodes[0].cli.echo, 0, None, 2, arg1=1)
9191

92+
self.log.info("Test that later cli named arguments values silently overwrite earlier ones")
93+
assert_equal(self.nodes[0].cli("-named", "echo", "arg0=0", "arg1=1", "arg2=2", "arg1=3").send_cli(), ['0', '3', '2'])
94+
assert_raises_rpc_error(-8, "Parameter args specified multiple times", self.nodes[0].cli("-named", "echo", "args=[0,1,2,3]", "4", "5", "6", ).send_cli)
95+
9296
user, password = get_auth_cookie(self.nodes[0].datadir, self.chain)
9397

9498
self.log.info("Test -stdinrpcpass option")

0 commit comments

Comments
 (0)