Skip to content

Commit 7392062

Browse files
achow101vijaydasmp
authored andcommitted
Merge bitcoin#26186: rpc: Sanitize label name in various RPCs with tests
65e78bd test: Invalid label name coverage (Aurèle Oulès) 552b51e refactor: Add sanity checks in LabelFromValue (Aurèle Oulès) 67e7ba8 rpc: Sanitize label name in various RPCs (Aurèle Oulès) Pull request description: The following RPCs did not sanitize the optional label name: - importprivkey - importaddress - importpubkey - importmulti - importdescriptors - listsinceblock Thus is was possible to import an address with a label `*` which should not be possible. The wildcard label is used for backwards compatibility in the `listtransactions` rpc. I added test coverage for these RPCs. ACKs for top commit: ajtowns: ACK 65e78bd achow101: ACK 65e78bd furszy: diff ACK 65e78bd stickies-v: re-ACK 65e78bd theStack: re-ACK 65e78bd Tree-SHA512: ad99f2824d4cfae352166b76da4ca0069b7c2eccf81aaa0654be25bbb3c6e5d6b005d93960f3f4154155f80e12be2d0cebd5529922ae3d2a36ee4eed82440b31
1 parent ae46378 commit 7392062

5 files changed

Lines changed: 55 additions & 24 deletions

File tree

src/wallet/rpc/addresses.cpp

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,7 @@ RPCHelpMan getnewaddress()
4242
}
4343

4444
// Parse the label first so we don't generate a key if there's an error
45-
std::string label;
46-
if (!request.params[0].isNull())
47-
label = LabelFromValue(request.params[0]);
45+
const std::string label{LabelFromValue(request.params[0])};
4846

4947
auto op_dest = pwallet->GetNewDestination(label);
5048
if (!op_dest) {
@@ -113,7 +111,7 @@ RPCHelpMan setlabel()
113111
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Dash address");
114112
}
115113

116-
std::string label = LabelFromValue(request.params[1]);
114+
const std::string label{LabelFromValue(request.params[1])};
117115

118116
if (pwallet->IsMine(dest)) {
119117
pwallet->SetAddressBook(dest, label, "receive");
@@ -226,9 +224,7 @@ RPCHelpMan addmultisigaddress()
226224

227225
LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
228226

229-
std::string label;
230-
if (!request.params[2].isNull())
231-
label = LabelFromValue(request.params[2]);
227+
const std::string label{LabelFromValue(request.params[2])};
232228

233229
int required = request.params[0].getInt<int>();
234230

@@ -589,7 +585,7 @@ RPCHelpMan getaddressesbylabel()
589585

590586
LOCK(pwallet->cs_wallet);
591587

592-
std::string label = LabelFromValue(request.params[0]);
588+
const std::string label{LabelFromValue(request.params[0])};
593589

594590
// Find all addresses that have the given label
595591
UniValue ret(UniValue::VOBJ);

src/wallet/rpc/backup.cpp

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,7 @@ RPCHelpMan importprivkey()
132132
EnsureWalletIsUnlocked(*pwallet);
133133

134134
std::string strSecret = request.params[0].get_str();
135-
std::string strLabel;
136-
if (!request.params[1].isNull())
137-
strLabel = request.params[1].get_str();
135+
const std::string strLabel{LabelFromValue(request.params[1])};
138136

139137
// Whether to perform rescan after import
140138
if (!request.params[2].isNull())
@@ -239,9 +237,7 @@ RPCHelpMan importaddress()
239237

240238
EnsureLegacyScriptPubKeyMan(*pwallet, true);
241239

242-
std::string strLabel;
243-
if (!request.params[1].isNull())
244-
strLabel = request.params[1].get_str();
240+
const std::string strLabel{LabelFromValue(request.params[1])};
245241

246242
// Whether to perform rescan after import
247243
bool fRescan = true;
@@ -432,9 +428,7 @@ RPCHelpMan importpubkey()
432428

433429
EnsureLegacyScriptPubKeyMan(*pwallet, true);
434430

435-
std::string strLabel;
436-
if (!request.params[1].isNull())
437-
strLabel = request.params[1].get_str();
431+
const std::string strLabel{LabelFromValue(request.params[1])};
438432

439433
// Whether to perform rescan after import
440434
bool fRescan = true;
@@ -1396,7 +1390,7 @@ static UniValue ProcessImport(CWallet& wallet, const UniValue& data, const int64
13961390
if (internal && data.exists("label")) {
13971391
throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal addresses should not have a label");
13981392
}
1399-
const std::string& label = data.exists("label") ? data["label"].get_str() : "";
1393+
const std::string label{LabelFromValue(data["label"])};
14001394
const bool add_keypool = data.exists("keypool") ? data["keypool"].get_bool() : false;
14011395

14021396
// Add to keypool only works with privkeys disabled
@@ -1693,7 +1687,7 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c
16931687
const std::string& descriptor = data["desc"].get_str();
16941688
const bool active = data.exists("active") ? data["active"].get_bool() : false;
16951689
const bool internal = data.exists("internal") ? data["internal"].get_bool() : false;
1696-
const std::string& label = data.exists("label") ? data["label"].get_str() : "";
1690+
const std::string label{LabelFromValue(data["label"])};
16971691

16981692
// Parse descriptor string
16991693
FlatSigningProvider keys;

src/wallet/rpc/transactions.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,7 @@ RPCHelpMan listtransactions()
499499

500500
std::optional<std::string> filter_label;
501501
if (!request.params[0].isNull() && request.params[0].get_str() != "*") {
502-
filter_label = request.params[0].get_str();
502+
filter_label.emplace(LabelFromValue(request.params[0]));
503503
if (filter_label.value().empty()) {
504504
throw JSONRPCError(RPC_INVALID_PARAMETER, "Label argument must be a valid label name or \"*\".");
505505
}
@@ -646,10 +646,9 @@ RPCHelpMan listsinceblock()
646646

647647
bool include_removed = (request.params[3].isNull() || request.params[3].get_bool());
648648

649+
// Only set it if 'label' was provided.
649650
std::optional<std::string> filter_label;
650-
if (!request.params[5].isNull()) {
651-
filter_label = request.params[5].get_str();
652-
}
651+
if (!request.params[5].isNull()) filter_label.emplace(LabelFromValue(request.params[5]));
653652

654653
int depth = height ? wallet.GetLastBlockHeight() + 1 - *height : -1;
655654

src/wallet/rpc/util.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,10 @@ const LegacyScriptPubKeyMan& EnsureConstLegacyScriptPubKeyMan(const CWallet& wal
132132

133133
std::string LabelFromValue(const UniValue& value)
134134
{
135-
std::string label = value.get_str();
135+
static const std::string empty_string;
136+
if (value.isNull()) return empty_string;
137+
138+
const std::string& label{value.get_str()};
136139
if (label == "*")
137140
throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, "Invalid label name");
138141
return label;

test/functional/wallet_labels.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,44 @@ def set_test_params(self):
2525
def skip_test_if_missing_module(self):
2626
self.skip_if_no_wallet()
2727

28+
def invalid_label_name_test(self):
29+
node = self.nodes[0]
30+
address = node.getnewaddress()
31+
pubkey = node.getaddressinfo(address)['pubkey']
32+
rpc_calls = [
33+
[node.getnewaddress],
34+
[node.setlabel, address],
35+
[node.getaddressesbylabel],
36+
[node.importpubkey, pubkey],
37+
[node.addmultisigaddress, 1, [pubkey]],
38+
[node.getreceivedbylabel],
39+
[node.listsinceblock, node.getblockhash(0), 1, False, True, False],
40+
]
41+
if self.options.descriptors:
42+
response = node.importdescriptors([{
43+
'desc': f'pkh({pubkey})',
44+
'label': '*',
45+
'timestamp': 'now',
46+
}])
47+
else:
48+
rpc_calls.extend([
49+
[node.importprivkey, node.dumpprivkey(address)],
50+
[node.importaddress, address],
51+
])
52+
53+
response = node.importmulti([{
54+
'scriptPubKey': {'address': address},
55+
'label': '*',
56+
'timestamp': 'now',
57+
}])
58+
59+
assert_equal(response[0]['success'], False)
60+
assert_equal(response[0]['error']['code'], -11)
61+
assert_equal(response[0]['error']['message'], "Invalid label name")
62+
63+
for rpc_call in rpc_calls:
64+
assert_raises_rpc_error(-11, "Invalid label name", *rpc_call, "*")
65+
2866
def run_test(self):
2967
# Check that there's no UTXO on the node
3068
node = self.nodes[0]
@@ -170,6 +208,7 @@ def run_test(self):
170208
)
171209

172210

211+
173212
class Label:
174213
def __init__(self, name):
175214
# Label name

0 commit comments

Comments
 (0)