fix(console): redact secret-bearing commands from the console echo and history (W7-1)
The RPC console echoed and stored typed commands verbatim, so `walletpassphrase <secret>`, `z_importkey <key>`, `encryptwallet <pass>` etc. left the secret in the visible log AND the 100-entry recall history (copyable). Adds a pure, unit-testable RedactConsoleCommand()/ConsoleCommandCarriesSecret() (allowlist of 13 secret-bearing first-tokens) in console_tab_helpers; submitConsoleCommand() now echoes and stores `> walletpassphrase ****` while still executing the real command unredacted. Bare secret commands and non-secret commands pass through unchanged. Output-secret commands (dumpprivkey / z_exportkey / z_exportmnemonic) — whose secret is in the RESULT — are a separate redaction concern, tracked as a follow-up. First fix in the wallet-hardening P0-A cluster (see docs/wallet-hardening.md). New testConsoleSecretRedaction (11 assertions); ctest 1/1. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -2523,6 +2523,29 @@ void testAllowsPlaintextRemote()
|
||||
EXPECT_TRUE(!Connection::usesPlaintextRemote(remoteTls)); // TLS → not plaintext, never refused
|
||||
}
|
||||
|
||||
void testConsoleSecretRedaction()
|
||||
{
|
||||
using dragonx::ui::RedactConsoleCommand;
|
||||
using dragonx::ui::ConsoleCommandCarriesSecret;
|
||||
|
||||
// Secret-bearing commands are recognized (case- and whitespace-insensitive on the name).
|
||||
EXPECT_TRUE(ConsoleCommandCarriesSecret("walletpassphrase myPass 60"));
|
||||
EXPECT_TRUE(ConsoleCommandCarriesSecret("z_importkey SK-secret"));
|
||||
EXPECT_TRUE(ConsoleCommandCarriesSecret(" ENCRYPTWALLET topsecret"));
|
||||
EXPECT_TRUE(!ConsoleCommandCarriesSecret("getinfo"));
|
||||
EXPECT_TRUE(!ConsoleCommandCarriesSecret("getwalletinfo")); // not a false-positive substring match
|
||||
|
||||
// Redaction replaces the arguments with **** but preserves the (original-case) command name.
|
||||
EXPECT_EQ(RedactConsoleCommand("walletpassphrase myPass 60"), std::string("walletpassphrase ****"));
|
||||
EXPECT_EQ(RedactConsoleCommand("z_importkey SK-secret-key"), std::string("z_importkey ****"));
|
||||
EXPECT_EQ(RedactConsoleCommand("ENCRYPTWALLET topsecret"), std::string("ENCRYPTWALLET ****"));
|
||||
// A bare secret command with no argument is left unchanged (nothing to hide).
|
||||
EXPECT_EQ(RedactConsoleCommand("walletpassphrase"), std::string("walletpassphrase"));
|
||||
// Non-secret commands pass through untouched.
|
||||
EXPECT_EQ(RedactConsoleCommand("sendtoaddress addr 1.0"), std::string("sendtoaddress addr 1.0"));
|
||||
EXPECT_EQ(RedactConsoleCommand("getwalletinfo"), std::string("getwalletinfo"));
|
||||
}
|
||||
|
||||
void testConnectHasStalled()
|
||||
{
|
||||
using dragonx::util::connectHasStalled;
|
||||
@@ -6861,6 +6884,7 @@ int main()
|
||||
testConnectHasStalled();
|
||||
testIsLocalHost();
|
||||
testAllowsPlaintextRemote();
|
||||
testConsoleSecretRedaction();
|
||||
testDaemonLifecycleExecution();
|
||||
testDaemonLifecycleAdapters();
|
||||
testConsoleTextLayout();
|
||||
|
||||
Reference in New Issue
Block a user