a581f42a331813b2ae28df0fd62a41c62a3b3051
9
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
06afbee4f8 |
fix(mining): remediate mining-tab audit (22 findings) — crash-safety, async control, validation, math
Fixes all 22 confirmed findings from the mining-tab audit (10 Medium, 12 Low; 0 Critical/High), adversarially reviewed (6 follow-ups found + fixed, incl. the review-caught idle-auto-start bypass and a wrong benchmark-restore condition). Crash-safety & lifecycle: - M-04: join a stale/finished monitor thread in XmrigManager::start() and ~XmrigManager so an xmrig crash-then-restart (or quit) no longer std::terminate()s the wallet. - L-03/L-10: surface an unexpected miner exit once and clear the stale running flag. UI never blocks (M-03/L-06/L-08/L-09/L-13): pool start/stop now run on a dedicated serialized FIFO mining-control thread (joined before teardown), so the ~13 call sites don't block the render thread on stop()'s SIGTERM->SIGKILL->join; the spawn result marshals back to the UI. Miner-process / pool trust boundary: - M-01: validate the payout address (util::isValidRecipientAddress) at EVERY start path — the UI gate AND App::startPoolMining() (idle auto-start / thread scaling) — so a stale/wrong-chain address can't silently lose rewards. - M-09: SSRF guard skips the background pool-stats GET for loopback/private/link-local/single-label hosts. - M-02/L-02: cap the pool-stats + xmrig-API HTTP response bodies. - L-01: write the xmrig config 0600 at creation (POSIX open with mode) — no world/group-readable window. - M-10: reject shell-metacharacter binary paths before the version popen (excluding '()' so Program Files (x86) still works). Solo mining: M-06/M-08 clamp thread count to [1, cores] at the setgenerate/xmrig boundary; M-07 notify + don't lie on stop failure. Correctness: L-05 block-time constant 75->150s (chainparams); M-05 discloses pool-mode "Est. Daily" as a rough solo-equivalent; L-04/L-11/L-12 benchmark lifecycle (cancel on nav-away / mode-switch with restore, skip rebalance mid-benchmark); L-07 honor cancel mid-extract in both the xmrig and daemon updaters. Two new i18n keys back-filled across all 8 languages; CJK subset font rebuilt for the new glyphs. Verified across full-node, lite, and Windows builds; tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
6ee81a5abe |
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> |
||
|
|
bcee4bfe72 |
fix(security): apply audit remediations to dev (ported + dev-only)
Dev-branch audit (docs/_archive/security-audit-dev-2026-07-22.md) re-found the
master issues (absent on dev) plus new ones in dev-only code. Applied here;
full-node build + test suite green; the subtle fixes were adversarially re-verified.
Ported from the master remediation (adapted to dev's code):
- bootstrap: reject zip-slip / path-traversal archive members (isSafeArchivePath)
before writing (S2-1). (dev already fail-closes on a missing checksum.)
- xmrig updater: fail closed when a signature is required but no key is pinned (F1-1).
- http_download.httpGetString: 16 MiB hard cap + MAXFILESIZE on the shared
metadata/price fetch (F1-2 / caps the updater + exchange paths at one site).
- rpc_client: explicit SSL_VERIFYPEER/VERIFYHOST (F4-1) and a 256 MiB response
cap in WriteCallback (F4-3).
- lite_connection_service: reject remote http:// lite servers, loopback only (L1-1).
Loopback is matched by a strict dotted-decimal 127.0.0.0/8 check (not a
startsWith("127.") prefix, which would wrongly accept 127.0.0.1.evil.com), with
userinfo/fragment stripping.
- lite controller: propagate encrypt/decrypt save() failure instead of reporting
success (F7-1).
- xmrig_manager: chmod(0600) the pool config before writing secrets (F5-2).
- app: clear the copied secret from the OS clipboard on shutdown (F3b-1).
- export_transactions: neutralize CSV/spreadsheet formula injection (F13-1).
- build pipeline: build-from-source lite backend + remove the self-attested
CMake signature gate (F15-1); pinned+verified appimagetool (F15-3/4);
verified Sapling params in setup.sh (F15-6); build.sh exits 0 on success.
(F14-1 empty-quoted-arg and F8-2 NUL-termination were already fixed on dev.)
Dev-only findings:
- rpc_client.callRaw: scrub the raw buffer + parsed tree (templated scrubJsonSecrets
for ordered_json) so console dumpprivkey/z_exportkey keys don't linger in freed
heap (N1-1).
- seed_wallet_creator: wipe the exported mnemonic on the failure path so a discarded
failed result never carries a live seed (W1-2).
- export_all_keys: write the plaintext key dump 0600 + atomically via
writeFileAtomically (U1-2).
- chat_database: restrict chat_messages.sqlite and its WAL/SHM sidecars to owner-only (C3-1).
Not done (need a decision, documented in the report):
- Chat header metadata (cid/z/p) rides outside the AEAD (C1/C2) — binding it is a
wire-protocol change requiring SilentDragonXLite interop review.
- Bootstrap lacks an offline-rooted signature (S2-2 residual) — needs signing infra.
- Console scrollback retains console-typed key-export output in plaintext (N1-1
residual) — inherent to an echoing console; would need output redaction.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
9c533fa485 |
refactor(audit): batch 5 — share the updater HTTP/download layer
XmrigUpdater and DaemonUpdater carried byte-identical libcurl code —
httpGet, downloadToFile, and the write/progress callbacks — including the
security-critical TLS-verify (SSL_VERIFYPEER=1 / SSL_VERIFYHOST=2) and
max-body-size options, duplicated across two signature-verified
download-and-execute files that had to be hand-kept in sync.
Hoist that machinery into src/util/http_download.{h,cpp}:
- util::httpGetString(url, logTag)
- util::httpDownloadToFile(url, dest, logTag, maxBytes, onProgress)
threading progress + cancellation through a std::function (returning false
aborts the transfer) instead of the class `this` pointer. Each updater's
httpGet/downloadToFile become one-line forwarders that bind their own log
tag and archive-size cap (64 MiB miner / 256 MiB node, both preserved).
Every curl option is preserved byte-identically (verified by diffing the
setopt lines against the pre-change code — the only deltas are the
now-parameterized MAXFILESIZE cap and the XFERINFODATA payload). No behavior
change; the TLS/size-guard options now live in exactly one place.
Deliberately scoped DOWN per the audit's own guidance: the divergent
per-updater logic (release DTOs/parsers, checksum-table parsing, version
compare, and installResolved's per-member-SHA vs transitive-trust verify,
install strategy, and chmod scope) stays in each class — NOT merged behind a
base-class/virtual-hook layer, which would raise review cost on
signature-verified execute-code and risk a subtle security regression. The
shared-DTO/parser consolidation (~77 test refs of churn on stable code) is
left as a separate, lower-value follow-up.
Full-node + Lite build clean; ctest 1/1; source-hygiene clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
4473e7e00a |
feat(updater): in-app dragonxd updater + browse-all-releases
Add a full-node daemon updater (util/DaemonUpdater + daemon_download_dialog) reachable from Settings -> NODE & SECURITY: downloads/verifies (SHA-256 + enforced ed25519 signature) and atomically installs the latest dragonxd from the project Gitea, with a "Restart daemon now" step. Add a shared "Browse all releases..." picker (release_list_view) to both the miner and daemon updaters so users can pin older/pre-release builds. Pure no-I/O cores (daemon_updater_core / xmrig_updater_core) are unit-tested; sign-daemon-release.sh signs release archives offline. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
b9881278af |
feat(mining): enforce xmrig signatures + fix multi-platform checksum/asset bugs
Now that the release publishes a valid .sig per archive (verified against the pinned key for linux/win/macOS), enable enforcement and fix two bugs that the newer multi-platform release (v6.25.3, which added a macOS build) exposed: - kXmrigRequireSignature = true: refuse any install whose release doesn't publish a valid ed25519 signature over the archive. Verified live end-to-end against the signed v6.25.3 (archive SHA-256 + signature -> install). - Drop the redundant inner-binary SHA-256 check. It keyed on the inner filename, but both the linux and macOS archives contain a binary literally named "xmrig", so the two "xmrig (…)" checksum lines collided in the map and the linux install compared against the macOS hash -> spurious "could not verify" failure. The whole archive is already verified (SHA-256 + signature), so every extracted member is authentic by transitivity — the per-member check added nothing but ambiguity. - Fix the macOS platform token: the asset is named "...-macos-x86_64.zip", not "...-macos-x64", so selectXmrigAsset never matched it. currentXmrigPlatformToken() now returns "macos-x86_64" on Intel macs (arm64 has no build -> Unavailable). Added a matcher test for the macOS naming. Both variants build; suite stable (0 failures / multiple runs); live require-mode install verified. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
8765fdf362 |
feat(mining): opt-in ed25519 signature verification for the xmrig updater (#1)
Closes the supply-chain gap the review flagged: today the archive and its SHA-256 share one trust root (the release body), so a compromised/edited release can ship an arbitrary binary that still "verifies". This adds authenticity via a detached ed25519 signature checked against a public key PINNED IN THE BINARY (not fetched), using libsodium's crypto_sign_verify_detached. Opt-in / soft rollout: - kXmrigSignaturePublicKeyBase64 in xmrig_updater.h is EMPTY by default -> signatures are not checked and behavior is unchanged (TLS + SHA-256 only). Paste the base64 public key to enable. - Once a key is pinned, an install verifies a "<archive>.sig" asset (base64/raw 64-byte ed25519 signature over the archive bytes) when present; kXmrigRequireSignature=true additionally refuses installs that publish no signature. - The check runs after the SHA-256 check, over the same already-read archive bytes; refuses on a missing key-but-required, unreachable .sig, or invalid signature. - verifyXmrigSignature + selectXmrigSignatureAsset are pure (libsodium only) and unit-tested: valid base64 + raw-64-byte signatures verify; tampered data, wrong key, and malformed/empty inputs all fail closed. Cross-tool interop verified (Python stdlib base64 == sodium base64). - scripts/sign-xmrig-release.sh: keygen / sign / pubkey helper (PyNaCl = same libsodium ed25519) to produce the .sig assets and the public key to pin. No behavior change until a key is pinned. Both variants build; suite passes; live worker re-verified (signatures off by default). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
98e0cce8ec |
fix(mining): harden xmrig updater per adversarial review
Addresses confirmed findings from the multi-lens review of the updater: - Cancelable + live progress (was: download uncancelable, progress stuck at 0%, closing the dialog mid-download blocked the UI thread on the worker join). Wire a libcurl CURLOPT_XFERINFOFUNCTION that publishes byte counts and returns abort when cancel() is requested; add a Cancel button. The dialog's destructor now aborts the transfer promptly, so closing mid-download no longer freezes the UI. - Graceful "unavailable" instead of a red error on platforms with no published build (macOS / ARM): new terminal State::Unavailable rendered neutrally, not as a failure. - Install-time running guard (TOCTOU): App::isPoolMinerRunning() re-checked in the dialog before each install, so a dialog opened before mining started can't replace a live binary. - Size caps: CURLOPT_MAXFILESIZE on the download and a per-archive-member ceiling before decomphressing into memory, to bound an attacker-controlled archive. - Distinguish a local read failure of the downloaded archive from a checksum mismatch (was reported misleadingly as "possible tampering"). - Reword the dialog's verification note to "checked against the release's published SHA-256 checksum" (integrity, not authenticity — see the signing note below). Not fixed here (needs your input): WinRing0x64.sys has no per-file hash published, but it is covered by the verified archive checksum (it is inside the verified zip); and the release is not cryptographically signed — checksums and binary share one trust root. Adding a pinned-key ed25519/minisign signature is the real supply-chain hardening and needs an offline signing key + a release-process change. Both variants build; suite passes; live worker re-verified end-to-end on linux-x64. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
946958b591 |
feat(mining): xmrig updater service — fetch/verify/install the latest miner from Gitea
Adds util/XmrigUpdater: a background-thread service (mirrors util/Bootstrap) that pulls the latest DRG-XMRig release from the project's Gitea, verifies it, and installs the miner binary into the daemon directory. Service layer only; the mining-tab UI hook comes next. Flow: GET /api/v1/repos/DragonX/drg-xmrig/releases/latest -> pick the asset matching this platform (…-linux-x64.zip / …-win-x64.zip; no macOS build -> graceful "unavailable") -> download (libcurl, TLS verified) -> verify the archive SHA-256 -> extract with miniz, flattening the versioned subdir the archive nests the binary in -> verify the extracted binary's SHA-256 in memory before writing it -> atomic install (+chmod +x on POSIX). On Windows also extracts WinRing0x64.sys; config.json/README.md are skipped. Security (download-and-execute): TLS is verified, and BOTH the archive and the inner binary are checked against the SHA-256 checksums published in the release body (parsed as "<hex> <name>" lines) — install is refused on a missing or mismatched checksum. Split into a pure core (xmrig_updater_core.cpp: release parse, asset/platform match, checksum parse, SHA-256) and the curl/miniz worker (xmrig_updater.cpp). The core is unit-tested against a real captured release fixture (tests/fixtures/xmrig/release_latest.json); an env-gated (DRAGONX_TEST_NETWORK=1) integration test exercises the worker live and was verified end-to-end on linux-x64 (inner binary SHA-256 matches the published value). Both variants build; suite passes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |