diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index d6d2be7b393..1161fd10110 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -361,6 +361,13 @@ bool RPCConsole::RPCParseCommandLine(interfaces::Node* node, std::string &strRes for (auto i = filter_ranges.rbegin(); i != filter_ranges.rend(); ++i) { pstrFilteredOut->replace(i->first, i->second - i->first, "(…)"); } + + bool is_sensitive = !filter_ranges.empty(); + + // Prefix "!" to mark sensitive commands as non-executable when recalled from history + if (is_sensitive) { + pstrFilteredOut->insert(0, 1, '!'); + } } switch(state) // final state { @@ -405,7 +412,11 @@ void RPCExecutor::request(const QString &command, const QString& wallet_name) " example: getblock(getblockhash(0) 1)[tx]\n\n" "Results without keys can be queried with an integer in brackets using the parenthesized syntax.\n" - " example: getblock(getblockhash(0),1)[tx][0]\n\n"))); + " example: getblock(getblockhash(0),1)[tx][0]\n\n" + + "Commands starting with a leading '!' are blocked from execution.\n" + "These entries are shown for reference only. Remove the '!' or retype to run them.\n" + " example: !walletpassphrase(...)\n\n"))); return; } if (!RPCConsole::RPCExecuteCommandLine(m_node, result, executableCommand, nullptr, wallet_name)) { @@ -994,6 +1005,16 @@ void RPCConsole::on_lineEdit_returnPressed() return; } + // Prevent parsing and execution of commands prefixed with '!' + if (cmd.startsWith('!')) { + QMessageBox::information(this, tr("Command not executed"), tr( + "Commands prefixed with '!' are blocked.\n" + "Remove the '!' or retype to run again." + + )); + return; + } + std::string strFilteredCmd; try { std::string dummy; diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp index 0857a4ebd5a..435dcd4ec2f 100644 --- a/src/qt/test/rpcnestedtests.cpp +++ b/src/qt/test/rpcnestedtests.cpp @@ -85,18 +85,24 @@ void RPCNestedTests::rpcNestedTests() QVERIFY(result == "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"); QVERIFY(filtered == "getblock(getbestblockhash())[tx][0]"); + RPCConsole::RPCParseCommandLine(nullptr, result, "createwallet test true", false, &filtered); + QVERIFY(filtered == "!createwallet(…)"); + RPCConsole::RPCParseCommandLine(nullptr, result, "createwalletdescriptor abc", false, &filtered); + QVERIFY(filtered == "!createwalletdescriptor(…)"); + RPCConsole::RPCParseCommandLine(nullptr, result, "migratewallet abc abc", false, &filtered); + QVERIFY(filtered == "!migratewallet(…)"); RPCConsole::RPCParseCommandLine(nullptr, result, "signmessagewithprivkey abc", false, &filtered); - QVERIFY(filtered == "signmessagewithprivkey(…)"); + QVERIFY(filtered == "!signmessagewithprivkey(…)"); RPCConsole::RPCParseCommandLine(nullptr, result, "signmessagewithprivkey abc,def", false, &filtered); - QVERIFY(filtered == "signmessagewithprivkey(…)"); + QVERIFY(filtered == "!signmessagewithprivkey(…)"); RPCConsole::RPCParseCommandLine(nullptr, result, "signrawtransactionwithkey(abc)", false, &filtered); - QVERIFY(filtered == "signrawtransactionwithkey(…)"); + QVERIFY(filtered == "!signrawtransactionwithkey(…)"); RPCConsole::RPCParseCommandLine(nullptr, result, "walletpassphrase(help())", false, &filtered); - QVERIFY(filtered == "walletpassphrase(…)"); + QVERIFY(filtered == "!walletpassphrase(…)"); RPCConsole::RPCParseCommandLine(nullptr, result, "walletpassphrasechange(help(walletpassphrasechange(abc)))", false, &filtered); - QVERIFY(filtered == "walletpassphrasechange(…)"); + QVERIFY(filtered == "!walletpassphrasechange(…)"); RPCConsole::RPCParseCommandLine(nullptr, result, "help(encryptwallet(abc, def))", false, &filtered); - QVERIFY(filtered == "help(encryptwallet(…))"); + QVERIFY(filtered == "!help(encryptwallet(…))"); RPCConsole::RPCExecuteCommandLine(m_node, result, "rpcNestedTest"); QVERIFY(result == "[]");