Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 40 additions & 1 deletion src/qt/rpcconsole.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ const QStringList historyFilter = QStringList()

}

// List of commands that may cause unintended effects if accidentally re-executed from history
const QStringList sensitiveFilter = QStringList()
<< "send"
<< "sendall"
<< "sendmany"
<< "sendtoaddress"
;

/* Object for executing console RPC commands in a separate thread.
*/
class RPCExecutor : public QObject
Expand Down Expand Up @@ -361,6 +369,23 @@ 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();

if (!is_sensitive) {
const QString strcmd = QString::fromStdString(*pstrFilteredOut);
for (const QString& val : sensitiveFilter) {
if (strcmd.contains(val, Qt::CaseInsensitive)) {
is_sensitive = true;
break;
}
}
}

// Prefix "!" to mark sensitive commands as non-executable when recalled from history
if (is_sensitive) {
pstrFilteredOut->insert(0, 1, '!');
}
}
switch(state) // final state
{
Expand Down Expand Up @@ -405,7 +430,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)) {
Expand Down Expand Up @@ -994,6 +1023,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;
Expand Down
28 changes: 22 additions & 6 deletions src/qt/test/rpcnestedtests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,18 +85,34 @@ 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(…))");

// Test filtering for sensitive commands
RPCConsole::RPCParseCommandLine(nullptr, result, "send abc abc", false, &filtered);
QVERIFY(filtered == "!send abc abc");
RPCConsole::RPCParseCommandLine(nullptr, result, "sendall abc abc", false, &filtered);
QVERIFY(filtered == "!sendall abc abc");
RPCConsole::RPCParseCommandLine(nullptr, result, "sendmany abc abc", false, &filtered);
QVERIFY(filtered == "!sendmany abc abc");
RPCConsole::RPCParseCommandLine(nullptr, result, "sendtoaddress abc abc", false, &filtered);
QVERIFY(filtered == "!sendtoaddress abc abc");

RPCConsole::RPCExecuteCommandLine(m_node, result, "rpcNestedTest");
QVERIFY(result == "[]");
Expand Down
Loading