# Vault security review status ## Status A separate internal reviewer performed a static review of the vault program and the browser transaction/signature paths. This was an AI-assisted internal review, **not** a formal audit by an independent security firm. No live deployment, deployed binary, fuzzing campaign, or formal proof was examined. No real deposits should be invited on the basis of this review. The source-level replay issue below has been addressed and is covered by program tests. Mainnet deployment immutability remains unverified, and a qualified third-party audit is still required. The site verifies the RPC genesis hash against the configured network and fails closed on a mismatch or unavailable verification. It also fails closed for mainnet vault transactions unless the operator explicitly attests that the external audit is complete, the deployed program is immutable, and mainnet vault actions are enabled. These settings are attestations, not independent proof. ## Scope - `program/src/lib.rs` - `program/tests/vault.rs` - `public/pq.js` - `public/bunker.js` and `public/wallet.js` transaction and signing paths - `program/DEPLOY.md` ## Findings ### High — Replaying a spend after a vault is funded again **Original issue:** Execute verified a valid Winternitz signature but stored no permanent consumed state. A later caller could stage the already-public signature again. Its recipient and refund were fixed by the signed digest, but any funds later sent back to that vault could be moved again, and reusing the one-time key violates the scheme's assumptions. **Source status: fixed.** Execute now creates a permanent `spent` PDA keyed by the vault and rejects further spends. Program deposit instructions also reject spent vaults. The browser discovers spent markers and removes add/send actions for those keys. Direct transfers to an old address remain possible and cannot be recovered through the spent key. **Deployment status: unverified.** The exact updated binary must be deployed and its program ID verified before relying on this protection. ### High — Upgrade authority is part of the custody trust model An upgradeable program can replace the spend rules and redirect later spends if its authority is compromised or malicious. No live program ID or on-chain loader state was inspected in this review. Before mainnet vault actions are enabled, verify the deployed binary and confirm the upgrade authority is absent, or document and explicitly accept a secured governance authority. The site defaults to disabling mainnet actions and requires explicit operator flags for any later enablement. ### High — Configured cluster could differ from the RPC network **Original issue:** The site trusted `CLUSTER` alone. Misconfiguring `CLUSTER=devnet` with a mainnet RPC could make Devnet actions appear enabled and submit them to mainnet. **Source status: fixed.** The server checks the RPC genesis hash against the known mainnet and Devnet values. If the RPC is unavailable, unknown, or mismatched, the UI disables actions and the server rejects `sendTransaction` requests. The program ID must also be configured before transaction submission is enabled. **Deployment status: runtime-dependent.** Verification checks the configured RPC at server startup and is cached for that server process. It does not independently detect a later RPC misroute or a deceptive endpoint. No production RPC configuration was inspected in this review. ### Medium — An older deployed binary can ignore the added marker account Some prior program versions may consume only their original accounts and ignore appended accounts. The source-level tests cannot establish which binary is deployed. Verify that the exact program version enforces the spent marker. If the older program is immutable, deploy a reviewed replacement under a new program ID before enabling site actions. ### Medium — Token-2022 extension coverage is incomplete The program uses basic `TransferChecked` CPIs and the tests cover only a basic Token-2022 mint. Transfer-hook or other extension-dependent mints may fail or become stuck if assets are transferred directly to a vault token account. Token-2022 vault account rent is also not reclaimed after a sweep. Restrict supported extensions/mints or implement and test extension-aware handling before advertising broad Token-2022 compatibility. ### Medium — Recovery discovery stops at index 599 The browser can create higher key indices but its restore scan has a fixed upper bound. A vault at index 600 or later may not be rediscovered if local hints are lost. Refuse creation beyond the recoverable range or implement resumable discovery and test restore at high indices. ### Medium — Spent-marker RPC lookup must not fail open **Original issue:** A failed or malformed marker lookup could be interpreted by the UI as “not spent,” exposing add/send controls for a vault whose state was unknown. **Source status: fixed.** Discovery validates every marker response and disables vault actions unless all marker states are verified. This protects the UI from stale or failed reads; the program remains the enforcement boundary. ### Additional custody limitation — Direct transfers and multiple assets The UI intends one asset per vault, but direct SOL/token transfers bypass its deposit flow. The vault-wide spent marker permanently blocks later program spends for that address, including a different asset sent directly to it. Direct assets sent to spent vault addresses may be unrecoverable. ## Validation limits The separate internal review was static and did not inspect a deployed program. The current `cargo test --release` run passed all 3 unit tests and 4 integration tests, including SOL, SPL Token, and Token-2022 replay attempts after directly refilling the old vault address. `node --test public/pq.test.js` passed both browser signing-vector and transaction-account-order tests. Manual server checks confirmed the mainnet default remains gated, a Devnet label with a mainnet RPC fails closed, and verified Devnet is only enabled with a configured program ID. These server-gate checks are manual; there is no committed automated server-gate regression test. No real transaction was sent. These local checks do not replace a qualified independent audit or live deployment checks.