From fc95daa97f324f6a8b3643a3d3c5e8f909d46c35 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Fri, 13 Mar 2015 21:51:27 +0100 Subject: [PATCH 01/11] Qt/RPCConsole: Save current command entry when browsing history Shell-like, but doesn't store changed history commands until executing it. --- src/qt/rpcconsole.cpp | 10 ++++++++++ src/qt/rpcconsole.h | 1 + 2 files changed, 11 insertions(+) diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index cfe197de2..5610e8b6c 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -751,6 +751,8 @@ void RPCConsole::on_lineEdit_returnPressed() if(!cmd.isEmpty()) { + cmdBeforeBrowsing = QString(); + message(CMD_REQUEST, cmd); Q_EMIT cmdRequest(cmd); // Remove command, if already in history @@ -769,6 +771,11 @@ void RPCConsole::on_lineEdit_returnPressed() void RPCConsole::browseHistory(int offset) { + // store current text when start browsing through the history + if (historyPtr == history.size()) { + cmdBeforeBrowsing = ui->lineEdit->text(); + } + historyPtr += offset; if(historyPtr < 0) historyPtr = 0; @@ -777,6 +784,9 @@ void RPCConsole::browseHistory(int offset) QString cmd; if(historyPtr < history.size()) cmd = history.at(historyPtr); + else if (!cmdBeforeBrowsing.isNull()) { + cmd = cmdBeforeBrowsing; + } ui->lineEdit->setText(cmd); } diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index ab8c3dc91..e1698711d 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -140,6 +140,7 @@ private: ClientModel *clientModel; QStringList history; int historyPtr; + QString cmdBeforeBrowsing; QList cachedNodeids; const PlatformStyle *platformStyle; RPCTimerInterface *rpcTimerInterface; From 9044908636f0072d001d9a029053a384127b002b Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Fri, 13 Mar 2015 21:51:27 +0100 Subject: [PATCH 02/11] Qt/RPCConsole: Don't store commands with potentially sensitive information in the history Filters importprivkey, signrawtransaction, walletpassphrase, walletpassphrasechange, and encryptwallet --- src/qt/rpcconsole.cpp | 37 ++++++++++++++++++++++++++++--------- src/qt/rpcconsole.h | 1 + 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 5610e8b6c..562c9509d 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -63,6 +63,14 @@ const struct { {NULL, NULL} }; +// don't add private key handling cmd's to the history +const QStringList RPCConsole::historyFilter = QStringList() + << "importprivkey" + << "signrawtransaction" + << "walletpassphrase" + << "walletpassphrasechange" + << "encryptwallet"; + /* Object for executing console RPC commands in a separate thread. */ class RPCExecutor : public QObject @@ -755,15 +763,26 @@ void RPCConsole::on_lineEdit_returnPressed() message(CMD_REQUEST, cmd); Q_EMIT cmdRequest(cmd); - // Remove command, if already in history - history.removeOne(cmd); - // Append command to history - history.append(cmd); - // Enforce maximum history size - while(history.size() > CONSOLE_HISTORY) - history.removeFirst(); - // Set pointer to end of history - historyPtr = history.size(); + + bool storeHistory = true; + Q_FOREACH(QString unallowedCmd, historyFilter) + { + if (cmd.trimmed().startsWith(unallowedCmd)) + storeHistory = false; break; + } + + if (storeHistory) + { + // Remove command, if already in history + history.removeOne(cmd); + // Append command to history + history.append(cmd); + // Enforce maximum history size + while(history.size() > CONSOLE_HISTORY) + history.removeFirst(); + // Set pointer to end of history + historyPtr = history.size(); + } // Scroll console view to end scrollToEnd(); } diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index e1698711d..4841ea825 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -140,6 +140,7 @@ private: ClientModel *clientModel; QStringList history; int historyPtr; + const static QStringList historyFilter; QString cmdBeforeBrowsing; QList cachedNodeids; const PlatformStyle *platformStyle; From de8980df9d3a1fc0b257139cef10a0e6ba29a8bd Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 4 Oct 2016 04:11:01 +0000 Subject: [PATCH 03/11] Bugfix: Do not add sensitive information to history for real Original code was missing braces, and short-circuited before checking everything after importprivkey --- src/qt/rpcconsole.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 562c9509d..8aa3d10eb 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -768,7 +768,10 @@ void RPCConsole::on_lineEdit_returnPressed() Q_FOREACH(QString unallowedCmd, historyFilter) { if (cmd.trimmed().startsWith(unallowedCmd)) - storeHistory = false; break; + { + storeHistory = false; + break; + } } if (storeHistory) From afde12f265bbc4742b329c6adecc853cb68a7155 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 4 Oct 2016 04:17:27 +0000 Subject: [PATCH 04/11] Qt/RPCConsole: Refactor command_may_contain_sensitive_data function out of RPCConsole::on_lineEdit_returnPressed --- src/qt/rpcconsole.cpp | 26 ++++++++++++++++---------- src/qt/rpcconsole.h | 1 - 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 8aa3d10eb..23a4a2507 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -63,14 +63,28 @@ const struct { {NULL, NULL} }; +namespace { + // don't add private key handling cmd's to the history -const QStringList RPCConsole::historyFilter = QStringList() +const QStringList historyFilter = QStringList() << "importprivkey" << "signrawtransaction" << "walletpassphrase" << "walletpassphrasechange" << "encryptwallet"; +bool command_may_contain_sensitive_data(const QString cmd) +{ + Q_FOREACH(QString unallowedCmd, historyFilter) { + if (cmd.trimmed().startsWith(unallowedCmd)) { + return true; + } + } + return false; +} + +} + /* Object for executing console RPC commands in a separate thread. */ class RPCExecutor : public QObject @@ -764,15 +778,7 @@ void RPCConsole::on_lineEdit_returnPressed() message(CMD_REQUEST, cmd); Q_EMIT cmdRequest(cmd); - bool storeHistory = true; - Q_FOREACH(QString unallowedCmd, historyFilter) - { - if (cmd.trimmed().startsWith(unallowedCmd)) - { - storeHistory = false; - break; - } - } + bool storeHistory = !command_may_contain_sensitive_data(cmd); if (storeHistory) { diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index 4841ea825..e1698711d 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -140,7 +140,6 @@ private: ClientModel *clientModel; QStringList history; int historyPtr; - const static QStringList historyFilter; QString cmdBeforeBrowsing; QList cachedNodeids; const PlatformStyle *platformStyle; From d80a00660f5b26ee16c60b941c2bbbd2d49e711f Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 4 Oct 2016 14:07:50 +0000 Subject: [PATCH 05/11] Qt/RPCConsole: Add signmessagewithprivkey to list of commands filtered from history --- src/qt/rpcconsole.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 23a4a2507..c979fdf87 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -68,6 +68,7 @@ namespace { // don't add private key handling cmd's to the history const QStringList historyFilter = QStringList() << "importprivkey" + << "signmessagewithprivkey" << "signrawtransaction" << "walletpassphrase" << "walletpassphrasechange" From 1755c04576eb42467ba16af9f2658e12b707b7d0 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 4 Oct 2016 15:43:10 +0000 Subject: [PATCH 06/11] Qt/RPCConsole: Truncate filtered commands to just the command name, rather than skip it entirely in history --- src/qt/rpcconsole.cpp | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index c979fdf87..c53ae53c6 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -74,14 +74,14 @@ const QStringList historyFilter = QStringList() << "walletpassphrasechange" << "encryptwallet"; -bool command_may_contain_sensitive_data(const QString cmd) +QString command_filter_sensitive_data(const QString cmd) { Q_FOREACH(QString unallowedCmd, historyFilter) { if (cmd.trimmed().startsWith(unallowedCmd)) { - return true; + return unallowedCmd; } } - return false; + return cmd; } } @@ -779,20 +779,18 @@ void RPCConsole::on_lineEdit_returnPressed() message(CMD_REQUEST, cmd); Q_EMIT cmdRequest(cmd); - bool storeHistory = !command_may_contain_sensitive_data(cmd); + cmd = command_filter_sensitive_data(cmd); + + // Remove command, if already in history + history.removeOne(cmd); + // Append command to history + history.append(cmd); + // Enforce maximum history size + while(history.size() > CONSOLE_HISTORY) + history.removeFirst(); + // Set pointer to end of history + historyPtr = history.size(); - if (storeHistory) - { - // Remove command, if already in history - history.removeOne(cmd); - // Append command to history - history.append(cmd); - // Enforce maximum history size - while(history.size() > CONSOLE_HISTORY) - history.removeFirst(); - // Set pointer to end of history - historyPtr = history.size(); - } // Scroll console view to end scrollToEnd(); } From e2d9213c32e51fe197756709e24c6694f28bf842 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Wed, 16 Nov 2016 10:56:32 +0000 Subject: [PATCH 07/11] Qt/RPCConsole: Make it possible to parse a command without executing it --- src/qt/rpcconsole.cpp | 24 +++++++++++++----------- src/qt/rpcconsole.h | 5 ++++- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index c53ae53c6..f45383341 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -136,10 +136,10 @@ public: #include "rpcconsole.moc" /** - * Split shell command line into a list of arguments and execute the command(s). + * Split shell command line into a list of arguments and optionally execute the command(s). * Aims to emulate \c bash and friends. * - * - Command nesting is possible with brackets [example: validateaddress(getnewaddress())] + * - Command nesting is possible with parenthesis; for example: validateaddress(getnewaddress()) * - Arguments are delimited with whitespace or comma * - Extra whitespace at the beginning and end and between arguments will be ignored * - Text can be "double" or 'single' quoted @@ -150,9 +150,10 @@ public: * * @param[out] result stringified Result from the executed command(chain) * @param[in] strCommand Command line to split + * @param[in] fExecute set true if you want the command to be executed */ -bool RPCConsole::RPCExecuteCommandLine(std::string &strResult, const std::string &strCommand) +bool RPCConsole::RPCParseCommandLine(std::string &strResult, const std::string &strCommand, const bool fExecute) { std::vector< std::vector > stack; stack.push_back(std::vector()); @@ -196,7 +197,7 @@ bool RPCConsole::RPCExecuteCommandLine(std::string &strResult, const std::string curarg += ch; break; } - if (curarg.size()) + if (curarg.size() && fExecute) { // if we have a value query, query arrays with index and objects with a string key UniValue subelement; @@ -271,13 +272,14 @@ bool RPCConsole::RPCExecuteCommandLine(std::string &strResult, const std::string } if ((ch == ')' || ch == '\n') && stack.size() > 0) { - std::string strPrint; - // Convert argument list to JSON objects in method-dependent way, - // and pass it along with the method name to the dispatcher. - JSONRPCRequest req; - req.params = RPCConvertValues(stack.back()[0], std::vector(stack.back().begin() + 1, stack.back().end())); - req.strMethod = stack.back()[0]; - lastResult = tableRPC.execute(req); + if (fExecute) { + // Convert argument list to JSON objects in method-dependent way, + // and pass it along with the method name to the dispatcher. + JSONRPCRequest req; + req.params = RPCConvertValues(stack.back()[0], std::vector(stack.back().begin() + 1, stack.back().end())); + req.strMethod = stack.back()[0]; + lastResult = tableRPC.execute(req); + } state = STATE_COMMAND_EXECUTED; curarg.clear(); diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index e1698711d..9567341ae 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -36,7 +36,10 @@ public: explicit RPCConsole(const PlatformStyle *platformStyle, QWidget *parent); ~RPCConsole(); - static bool RPCExecuteCommandLine(std::string &strResult, const std::string &strCommand); + static bool RPCParseCommandLine(std::string &strResult, const std::string &strCommand, bool fExecute); + static bool RPCExecuteCommandLine(std::string &strResult, const std::string &strCommand) { + return RPCParseCommandLine(strResult, strCommand, true); + } void setClientModel(ClientModel *model); From 629cd423644b1f6d180eb5eeb3fb8a204881db97 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Wed, 16 Nov 2016 11:36:21 +0000 Subject: [PATCH 08/11] Qt/RPCConsole: Teach RPCParseCommandLine how to filter out arguments to sensitive commands --- src/qt/rpcconsole.cpp | 32 ++++++++++++++++++++++++++++++-- src/qt/rpcconsole.h | 6 +++--- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index f45383341..63a23dc49 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -151,9 +151,10 @@ public: * @param[out] result stringified Result from the executed command(chain) * @param[in] strCommand Command line to split * @param[in] fExecute set true if you want the command to be executed + * @param[out] pstrFilteredOut Command line, filtered to remove any sensitive data */ -bool RPCConsole::RPCParseCommandLine(std::string &strResult, const std::string &strCommand, const bool fExecute) +bool RPCConsole::RPCParseCommandLine(std::string &strResult, const std::string &strCommand, const bool fExecute, std::string * const pstrFilteredOut) { std::vector< std::vector > stack; stack.push_back(std::vector()); @@ -173,12 +174,16 @@ bool RPCConsole::RPCParseCommandLine(std::string &strResult, const std::string & } state = STATE_EATING_SPACES; std::string curarg; UniValue lastResult; + unsigned nDepthInsideSensitive = 0; + size_t filter_begin_pos = 0; + std::vector> filter_ranges; std::string strCommandTerminated = strCommand; if (strCommandTerminated.back() != '\n') strCommandTerminated += "\n"; - for(char ch: strCommandTerminated) + for (size_t chpos = 0; chpos < strCommandTerminated.size(); ++chpos) { + char ch = strCommandTerminated[chpos]; switch(state) { case STATE_COMMAND_EXECUTED_INNER: @@ -222,6 +227,13 @@ bool RPCConsole::RPCParseCommandLine(std::string &strResult, const std::string & breakParsing = false; // pop the stack and return the result to the current command arguments + if (nDepthInsideSensitive) { + if (!--nDepthInsideSensitive) { + assert(filter_begin_pos); + filter_ranges.push_back(std::make_pair(filter_begin_pos, chpos)); + filter_begin_pos = 0; + } + } stack.pop_back(); // don't stringify the json in case of a string to avoid doublequotes @@ -260,7 +272,12 @@ bool RPCConsole::RPCParseCommandLine(std::string &strResult, const std::string & if (state == STATE_ARGUMENT) { if (ch == '(' && stack.size() && stack.back().size() > 0) + { + if (nDepthInsideSensitive) { + ++nDepthInsideSensitive; + } stack.push_back(std::vector()); + } // don't allow commands after executed commands on baselevel if (!stack.size()) @@ -291,6 +308,11 @@ bool RPCConsole::RPCParseCommandLine(std::string &strResult, const std::string & else if(state == STATE_ARGUMENT) // Space ends argument { + // This is the only place where the method name should get pushed (as the first stack item) + if ((!nDepthInsideSensitive) && historyFilter.contains(QString::fromStdString(curarg), Qt::CaseInsensitive)) { + nDepthInsideSensitive = 1; + filter_begin_pos = chpos; + } stack.back().push_back(curarg); curarg.clear(); } @@ -328,6 +350,12 @@ bool RPCConsole::RPCParseCommandLine(std::string &strResult, const std::string & break; } } + if (pstrFilteredOut) { + *pstrFilteredOut = strCommand; + for (auto i = filter_ranges.rbegin(); i != filter_ranges.rend(); ++i) { + pstrFilteredOut->replace(i->first, i->second - i->first, "..."); + } + } switch(state) // final state { case STATE_COMMAND_EXECUTED: diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index 9567341ae..dfc1cc26d 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -36,9 +36,9 @@ public: explicit RPCConsole(const PlatformStyle *platformStyle, QWidget *parent); ~RPCConsole(); - static bool RPCParseCommandLine(std::string &strResult, const std::string &strCommand, bool fExecute); - static bool RPCExecuteCommandLine(std::string &strResult, const std::string &strCommand) { - return RPCParseCommandLine(strResult, strCommand, true); + static bool RPCParseCommandLine(std::string &strResult, const std::string &strCommand, bool fExecute, std::string * const pstrFilteredOut = NULL); + static bool RPCExecuteCommandLine(std::string &strResult, const std::string &strCommand, std::string * const pstrFilteredOut = NULL) { + return RPCParseCommandLine(strResult, strCommand, true, pstrFilteredOut); } void setClientModel(ClientModel *model); From a79598ddf4bf35b934ed3513900f2a9fae0bff45 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Wed, 16 Nov 2016 12:31:44 +0000 Subject: [PATCH 09/11] Qt/Test: Make sure filtering sensitive data works correctly in nested commands --- src/qt/test/rpcnestedtests.cpp | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp index fb044489d..fbec5722b 100644 --- a/src/qt/test/rpcnestedtests.cpp +++ b/src/qt/test/rpcnestedtests.cpp @@ -61,8 +61,10 @@ void RPCNestedTests::rpcNestedTests() std::string result; std::string result2; - RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo()[chain]"); //simple result filtering with path + std::string filtered; + RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo()[chain]", &filtered); //simple result filtering with path QVERIFY(result=="main"); + QVERIFY(filtered == "getblockchaininfo()[chain]"); RPCConsole::RPCExecuteCommandLine(result, "getblock(getbestblockhash())"); //simple 2 level nesting RPCConsole::RPCExecuteCommandLine(result, "getblock(getblock(getbestblockhash())[hash], true)"); @@ -87,8 +89,30 @@ void RPCNestedTests::rpcNestedTests() (RPCConsole::RPCExecuteCommandLine(result2, "createrawtransaction( [], {} , 0 )")); //whitespace between parametres is allowed QVERIFY(result == result2); - RPCConsole::RPCExecuteCommandLine(result, "getblock(getbestblockhash())[tx][0]"); + RPCConsole::RPCExecuteCommandLine(result, "getblock(getbestblockhash())[tx][0]", &filtered); QVERIFY(result == "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"); + QVERIFY(filtered == "getblock(getbestblockhash())[tx][0]"); + + RPCConsole::RPCParseCommandLine(result, "importprivkey", false, &filtered); + QVERIFY(filtered == "importprivkey(…)"); + RPCConsole::RPCParseCommandLine(result, "signmessagewithprivkey abc", false, &filtered); + QVERIFY(filtered == "signmessagewithprivkey(…)"); + RPCConsole::RPCParseCommandLine(result, "signmessagewithprivkey abc,def", false, &filtered); + QVERIFY(filtered == "signmessagewithprivkey(…)"); + RPCConsole::RPCParseCommandLine(result, "signrawtransaction(abc)", false, &filtered); + QVERIFY(filtered == "signrawtransaction(…)"); + RPCConsole::RPCParseCommandLine(result, "walletpassphrase(help())", false, &filtered); + QVERIFY(filtered == "walletpassphrase(…)"); + RPCConsole::RPCParseCommandLine(result, "walletpassphrasechange(help(walletpassphrasechange(abc)))", false, &filtered); + QVERIFY(filtered == "walletpassphrasechange(…)"); + RPCConsole::RPCParseCommandLine(result, "help(encryptwallet(abc, def))", false, &filtered); + QVERIFY(filtered == "help(encryptwallet(…))"); + RPCConsole::RPCParseCommandLine(result, "help(importprivkey())", false, &filtered); + QVERIFY(filtered == "help(importprivkey(…))"); + RPCConsole::RPCParseCommandLine(result, "help(importprivkey(help()))", false, &filtered); + QVERIFY(filtered == "help(importprivkey(…))"); + RPCConsole::RPCParseCommandLine(result, "help(importprivkey(abc), walletpassphrase(def))", false, &filtered); + QVERIFY(filtered == "help(importprivkey(…), walletpassphrase(…))"); RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest"); QVERIFY(result == "[]"); From ff77faf480cfaf8098cfa04af6cc17a75c19ba49 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Wed, 16 Nov 2016 12:32:15 +0000 Subject: [PATCH 10/11] Qt/RPCConsole: Use RPCParseCommandLine to perform command filtering --- src/qt/rpcconsole.cpp | 77 ++++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 31 deletions(-) diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 63a23dc49..5cba588d0 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -74,16 +75,6 @@ const QStringList historyFilter = QStringList() << "walletpassphrasechange" << "encryptwallet"; -QString command_filter_sensitive_data(const QString cmd) -{ - Q_FOREACH(QString unallowedCmd, historyFilter) { - if (cmd.trimmed().startsWith(unallowedCmd)) { - return unallowedCmd; - } - } - return cmd; -} - } /* Object for executing console RPC commands in a separate thread. @@ -175,13 +166,32 @@ bool RPCConsole::RPCParseCommandLine(std::string &strResult, const std::string & std::string curarg; UniValue lastResult; unsigned nDepthInsideSensitive = 0; - size_t filter_begin_pos = 0; + size_t filter_begin_pos = 0, chpos; std::vector> filter_ranges; + auto add_to_current_stack = [&](const std::string& curarg) { + if (stack.back().empty() && (!nDepthInsideSensitive) && historyFilter.contains(QString::fromStdString(curarg), Qt::CaseInsensitive)) { + nDepthInsideSensitive = 1; + filter_begin_pos = chpos; + } + stack.back().push_back(curarg); + }; + + auto close_out_params = [&]() { + if (nDepthInsideSensitive) { + if (!--nDepthInsideSensitive) { + assert(filter_begin_pos); + filter_ranges.push_back(std::make_pair(filter_begin_pos, chpos)); + filter_begin_pos = 0; + } + } + stack.pop_back(); + }; + std::string strCommandTerminated = strCommand; if (strCommandTerminated.back() != '\n') strCommandTerminated += "\n"; - for (size_t chpos = 0; chpos < strCommandTerminated.size(); ++chpos) + for (chpos = 0; chpos < strCommandTerminated.size(); ++chpos) { char ch = strCommandTerminated[chpos]; switch(state) @@ -227,14 +237,7 @@ bool RPCConsole::RPCParseCommandLine(std::string &strResult, const std::string & breakParsing = false; // pop the stack and return the result to the current command arguments - if (nDepthInsideSensitive) { - if (!--nDepthInsideSensitive) { - assert(filter_begin_pos); - filter_ranges.push_back(std::make_pair(filter_begin_pos, chpos)); - filter_begin_pos = 0; - } - } - stack.pop_back(); + close_out_params(); // don't stringify the json in case of a string to avoid doublequotes if (lastResult.isStr()) @@ -246,7 +249,7 @@ bool RPCConsole::RPCParseCommandLine(std::string &strResult, const std::string & if (curarg.size()) { if (stack.size()) - stack.back().push_back(curarg); + add_to_current_stack(curarg); else strResult = curarg; } @@ -283,7 +286,7 @@ bool RPCConsole::RPCParseCommandLine(std::string &strResult, const std::string & if (!stack.size()) throw std::runtime_error("Invalid Syntax"); - stack.back().push_back(curarg); + add_to_current_stack(curarg); curarg.clear(); state = STATE_EATING_SPACES_IN_BRACKETS; } @@ -308,12 +311,7 @@ bool RPCConsole::RPCParseCommandLine(std::string &strResult, const std::string & else if(state == STATE_ARGUMENT) // Space ends argument { - // This is the only place where the method name should get pushed (as the first stack item) - if ((!nDepthInsideSensitive) && historyFilter.contains(QString::fromStdString(curarg), Qt::CaseInsensitive)) { - nDepthInsideSensitive = 1; - filter_begin_pos = chpos; - } - stack.back().push_back(curarg); + add_to_current_stack(curarg); curarg.clear(); } if ((state == STATE_EATING_SPACES_IN_BRACKETS || state == STATE_ARGUMENT) && ch == ',') @@ -351,9 +349,13 @@ bool RPCConsole::RPCParseCommandLine(std::string &strResult, const std::string & } } if (pstrFilteredOut) { + if (STATE_COMMAND_EXECUTED == state) { + assert(!stack.empty()); + close_out_params(); + } *pstrFilteredOut = strCommand; for (auto i = filter_ranges.rbegin(); i != filter_ranges.rend(); ++i) { - pstrFilteredOut->replace(i->first, i->second - i->first, "..."); + pstrFilteredOut->replace(i->first, i->second - i->first, "(…)"); } } switch(state) // final state @@ -800,16 +802,29 @@ void RPCConsole::setMempoolSize(long numberOfTxs, size_t dynUsage) void RPCConsole::on_lineEdit_returnPressed() { QString cmd = ui->lineEdit->text(); - ui->lineEdit->clear(); if(!cmd.isEmpty()) { + std::string strFilteredCmd; + try { + std::string dummy; + if (!RPCParseCommandLine(dummy, cmd.toStdString(), false, &strFilteredCmd)) { + // Failed to parse command, so we cannot even filter it for the history + throw std::runtime_error("Invalid command line"); + } + } catch (const std::exception& e) { + QMessageBox::critical(this, "Error", QString("Error: ") + QString::fromStdString(e.what())); + return; + } + + ui->lineEdit->clear(); + cmdBeforeBrowsing = QString(); message(CMD_REQUEST, cmd); Q_EMIT cmdRequest(cmd); - cmd = command_filter_sensitive_data(cmd); + cmd = QString::fromStdString(strFilteredCmd); // Remove command, if already in history history.removeOne(cmd); From 8562792095a78fdef2dacc3c5e32c41206df4c87 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 22 Nov 2016 23:37:23 +0000 Subject: [PATCH 11/11] GUI/RPCConsole: Include importmulti in history sensitive-command filter --- src/qt/rpcconsole.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 5cba588d0..38fe659c3 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -69,6 +69,7 @@ namespace { // don't add private key handling cmd's to the history const QStringList historyFilter = QStringList() << "importprivkey" + << "importmulti" << "signmessagewithprivkey" << "signrawtransaction" << "walletpassphrase"