feat(settings): gate debug options behind a confirmation (+ re-auth if secured)
Revealing the DEBUG OPTIONS dropdown now opens a confirmation modal with a warning instead of expanding directly. If the wallet is secured, it also requires re-authentication before the options appear: - a quick-unlock PIN → verified against the vault (Argon2id derive, off-thread, no wallet side effects); - otherwise an encrypted wallet → the passphrase, verified via walletpassphrase. Unsecured wallets just confirm. Collapsing needs no gate; each expand re-gates. App gains debugGateRequiresAuth() + verifyDebugCredential() (cb on the main thread; secrets wiped). Adds a modal-debug-gate sweep surface. Verified 100% + 150%, dark + light. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
41
src/app.cpp
41
src/app.cpp
@@ -4731,6 +4731,47 @@ bool App::hasPinVault() const {
|
||||
return vault_ && vault_->hasVault() && settings_ && settings_->getPinEnabled();
|
||||
}
|
||||
|
||||
bool App::debugGateRequiresAuth() const {
|
||||
return hasPinVault() || state_.encrypted;
|
||||
}
|
||||
|
||||
void App::verifyDebugCredential(const std::string& secret, std::function<void(bool)> cb) {
|
||||
if (!cb) return;
|
||||
// A quick-unlock PIN vault → verify the PIN by deriving it (pure crypto, no wallet side effects).
|
||||
if (hasPinVault()) {
|
||||
auto* w = worker_.get();
|
||||
if (!w) { cb(false); return; }
|
||||
w->post([this, s = secret, cb]() mutable -> rpc::RPCWorker::MainCb {
|
||||
std::string pass;
|
||||
bool ok = vault_ && vault_->retrieve(s, pass);
|
||||
if (!pass.empty()) util::SecureVault::secureZero(pass.data(), pass.size());
|
||||
if (!s.empty()) util::SecureVault::secureZero(&s[0], s.size());
|
||||
return [cb, ok]() { cb(ok); };
|
||||
});
|
||||
return;
|
||||
}
|
||||
// Encrypted wallet, no PIN → verify the passphrase via walletpassphrase (re-unlocks with the
|
||||
// usual timeout; a wrong passphrase throws).
|
||||
if (state_.encrypted) {
|
||||
auto* w = worker_.get();
|
||||
auto* r = rpc_.get();
|
||||
if (!w || !r) { cb(false); return; }
|
||||
int autoLock = settings_ ? settings_->getAutoLockTimeout() : 300;
|
||||
int timeout = (autoLock > 0) ? std::max(600, autoLock * 2) : 86400;
|
||||
w->post([r, s = secret, timeout, cb]() mutable -> rpc::RPCWorker::MainCb {
|
||||
bool ok = false;
|
||||
try {
|
||||
if (r && r->isConnected()) { r->call("walletpassphrase", {s, timeout}); ok = true; }
|
||||
} catch (...) { ok = false; }
|
||||
if (!s.empty()) util::SecureVault::secureZero(&s[0], s.size());
|
||||
return [cb, ok]() { cb(ok); };
|
||||
});
|
||||
return;
|
||||
}
|
||||
// No credential set — nothing to verify.
|
||||
cb(true);
|
||||
}
|
||||
|
||||
bool App::hasPendingRPCResults() const {
|
||||
return (worker_ && worker_->hasPendingResults())
|
||||
|| (fast_worker_ && fast_worker_->hasPendingResults());
|
||||
|
||||
Reference in New Issue
Block a user