security(console): mask key-dump command RESULTS in the scrollback
The console redacted secret command ARGS but showed the OUTPUT of key-revealing commands (dumpprivkey / z_exportkey / z_exportviewingkey / z_exportmnemonic / dumpwallet / z_exportwallet, and the lite seed/export verbs) in full — visible in the scrollback, Copy All, Ctrl+C and the filter. Now, when the submitted command is key-revealing, its result is held ONLY in a member buffer (never in the scrollback model, so Copy-All etc. can't leak it) and a masked placeholder is shown. An explicit "Copy secret" button routes through the auto-clearing clipboard; the buffer auto-wipes after 60s, on Clear, and on wallet lock (via wipeDisplayedSecrets). The command string passed to the executor is byte-identical. New pure helper ConsoleCommandRevealsSecret is unit-testable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,8 @@
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <functional>
|
||||
#include <chrono>
|
||||
|
||||
namespace dragonx {
|
||||
namespace ui {
|
||||
@@ -66,6 +68,13 @@ public:
|
||||
*/
|
||||
void clear();
|
||||
|
||||
// Masked secret-result support: the plaintext of a key-revealing command's output is held ONLY here
|
||||
// (never in the scrollback model), so Copy All / Ctrl+C / the filter can't leak it. An explicit
|
||||
// "Copy secret" action routes through this handler (the app wires it to the auto-clearing clipboard).
|
||||
void setCopySecretHandler(std::function<void(const std::string&)> f) { copy_secret_cb_ = std::move(f); }
|
||||
bool hasSecretResult() const { return !secret_result_.empty(); }
|
||||
void wipeSecretResult(); // zero + drop the buffer, disarm, reset the deadline (call on lock/clear)
|
||||
|
||||
// Ask the console to place the keyboard focus in the command input on the next render (consumed once).
|
||||
// Called when the user switches to the Console tab, gated by the console_auto_focus setting.
|
||||
void requestInputFocus() { focus_input_pending_ = true; }
|
||||
@@ -162,6 +171,10 @@ private:
|
||||
char input_buffer_[4096] = {0};
|
||||
bool stop_confirm_pending_ = false; // 'stop' typed once, awaiting a confirming second 'stop'
|
||||
std::string destructive_confirm_cmd_; // a destructive command typed once, awaiting an identical re-entry
|
||||
bool secret_result_armed_ = false; // the just-submitted command reveals key material in its result
|
||||
std::string secret_result_; // plaintext of the masked result (member-only; never in model_)
|
||||
std::chrono::steady_clock::time_point secret_result_deadline_{}; // auto-wipe the buffer at this time
|
||||
std::function<void(const std::string&)> copy_secret_cb_; // routes "Copy secret" to the app
|
||||
bool focus_input_pending_ = false; // one-shot: focus the command input next render (tab-open auto-focus)
|
||||
// (log-ingestion cursors + result queue moved to the ConsoleCommandExecutor)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user