fix: security-audit remediation (15 findings), empty-wallet warning, and send/chat/console/shutdown UX
Security audit remediation (15 confirmed findings from the codebase audit): - H-02: scrub+delete the decrypt-flow plaintext key export on ALL exit paths (RAII guard) and purge stale obsidiandecryptexport* files at startup. - M-01/L-03/L-04/L-05/L-07: sodium_memzero the Set-PIN and encrypt-PIN worker passphrase/PIN copies, the RPC Basic-auth string (auth_), the exported/imported key buffers (App::wipeSecrets, called from ~App and before main's _Exit), and the first-run wizard "Skip" buffers. - M-03/M-04/M-05/L-06: return locked COPIES from XmrigManager/EmbeddedDaemon getters (dedicated error_mutex_; DaemonController::lastError now by value), route xmrig last_error_ writes through a locked setter, and wrap shutdown_status_/wizard_stop_status_ in a locking GuardedStatus (wizard_stopping_external_ -> std::atomic). - M-02: persist after a console send/shield/import in the lite backend. - L-01: require the confirm click for z_shieldcoinbase/z_mergetoaddress. - L-02: quote/escape each Windows daemon argv per the MSDN CommandLineToArgvW rules. - L-08: pin json/tomlplusplus/libwebp FetchContent to immutable commit SHAs. - I-01: extract updater archives from the already-verified in-memory buffer (no disk re-read TOCTOU). Feature: warn once (full-node) when the active wallet loads empty while a sibling wallet file in the datadir holds keys. A funded salvage wallet.<ts>.bak routes to the recovery/Restore flow; a funded sibling .dat routes to the wallet manager. Per-wallet-file dismissal; gated on synced + address-list-loaded to avoid false positives on warm reconnect / spent-down wallets. UX fixes: - send: show the TOTAL balance (with a spendable "available" note) in the source dropdown and keep pending-change addresses visible. - chat: insert emoji at the cursor position; restrict new-chat recipients to shielded (z) addresses. - console: optional auto-focus of the command input on tab open (off by default). - shutdown: when "stop external daemon" is on, keep the shutdown screen up until the external node actually exits, showing live status. Adversarially reviewed; verified across full-node, lite, and Windows builds; tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -18,14 +18,57 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace dragonx {
|
||||
namespace util {
|
||||
|
||||
// A reserved bare-filename PREFIX for the datadir "in-place link" wallets: an out-of-datadir wallet the
|
||||
// user opens gets a stable symlink/hardlink under this name in the datadir so the daemon can load it by
|
||||
// bare -wallet=<name>. These are plumbing, not standalone wallet files, so wallet enumeration hides them.
|
||||
// Single source of truth shared with the wallets UI (ui/windows/wallets_dialog.h).
|
||||
constexpr const char* kInPlaceLinkPrefix = "wallet-ip-";
|
||||
inline bool isInPlaceLinkName(const std::string& name) { return name.rfind(kInPlaceLinkPrefix, 0) == 0; }
|
||||
|
||||
// Enumerate the standalone wallet-bearing files in a DragonX datadir (TOP-LEVEL only): bare "wallet*.dat"
|
||||
// files, excluding in-place links and (optionally) one active filename. The "wallet" prefix already excludes
|
||||
// node artifacts (peers.dat / blk*.dat / asmap.dat / …). With includeSalvageBaks, also returns "wallet*.bak"
|
||||
// files — the daemon's salvage backups (wallet.<ts>.bak) that hold the pre-salvage keys. Returns full paths.
|
||||
// Exception-safe (error_code iteration); never descends into subdirectories. This is the lightweight
|
||||
// datadir-only counterpart to the wallets dialog's richer scan (which also walks user-added external folders
|
||||
// and de-dups by canonical path); the default (.dat only, no baks) matches that dialog's semantics.
|
||||
inline std::vector<std::string> enumerateDatadirWalletFiles(const std::string& datadir,
|
||||
const std::string& excludeActiveName = "",
|
||||
bool includeSalvageBaks = false) {
|
||||
namespace fs = std::filesystem;
|
||||
std::vector<std::string> out;
|
||||
std::error_code ec;
|
||||
fs::directory_iterator it(datadir, ec), end;
|
||||
if (ec) return out;
|
||||
for (; it != end; it.increment(ec)) {
|
||||
if (ec) break;
|
||||
const fs::path p = it->path();
|
||||
const std::string name = p.filename().string();
|
||||
if (name.size() <= 4) continue;
|
||||
const std::string ext = name.substr(name.size() - 4);
|
||||
const bool isDat = (ext == ".dat");
|
||||
const bool isBak = includeSalvageBaks && (ext == ".bak");
|
||||
if (!isDat && !isBak) continue; // *.dat (+ *.bak) only
|
||||
if (name.rfind("wallet", 0) != 0) continue; // wallet-prefixed only
|
||||
if (isInPlaceLinkName(name)) continue; // hide in-place links
|
||||
if (!excludeActiveName.empty() && name == excludeActiveName) continue; // skip the active wallet
|
||||
std::error_code fec;
|
||||
if (!fs::is_regular_file(p, fec)) continue;
|
||||
out.push_back(p.string());
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
struct WalletFileProbe {
|
||||
bool isBerkeleyDB = false; ///< file has a valid BDB btree metapage magic (looks like a real wallet.dat)
|
||||
bool encrypted = false; ///< has an "mkey" master-key record → passphrase-encrypted
|
||||
|
||||
Reference in New Issue
Block a user