# AIW Security Audit Program

AIW is an AI-orchestrated, self-custodial Monero/Bitcoin wallet. Because it was
built by orchestrating AI agents — not hand-written line by line — every
security claim is treated as unproven until a *different* agent verifies it.
This folder publishes the full audit trail, redacted only for live credentials
and one real mainnet address (see [REDACTION-LOG.md](./REDACTION-LOG.md) —
every change is itemized).

## How to Read These Documents

- **Static passes (1–8):** source review and data-flow tracing. They find
  structural weaknesses by reading the code and reasoning about it.
- **Dynamic pass (9):** the wallet is actually *running*. Real HTTP requests,
  real RPC calls, real stagenet transactions. Nothing is assumed — attacks are
  executed and the result is recorded with evidence.
- **"PASS"** means the protection was attacked and held, with evidence (a
  status code, a response body, or an on-chain transaction hash) — not that it
  "looked fine on review."
- **Findings are shown, not hidden.** Eight static passes plus one dynamic pass
  surfaced vulnerabilities across every severity class. Publishing them — with
  the fix for each — is the point. A security program with zero findings either
  did not look hard enough or is not being honest.
- **What the RETEST proves:** the first dynamic run found real HIGH defects on
  a stale build. They were fixed. Then an *independent* re-run attacked the
  live, fixed system again — and all nine areas passed, with fresh on-chain
  proof. The RETEST is the document that matters most: it shows the fixes are
  real on the system that actually runs, not just in a claim.

## The Audit Arc

| Document | Passes | Method | Key Outcome |
|---|---|---|---|
| [SECURITY-REVIEW.md](./SECURITY-REVIEW.md) | 1 | Static analysis | Threat model established; 4 Critical / 5 High / structural findings — all Critical & High fixed |
| [SECURITY-AUDIT2.md](./SECURITY-AUDIT2.md) – [AUDIT8.md](./SECURITY-AUDIT8.md) | 2–8 | Static + behavioral | Iterative hardening (blank-password lockout, CORS/DNS-rebind, BTC key-exfil parity, swap confirm gate, memory-poisoning, fail-closed RPC allowlist…). By Pass 7–8 independent reviewers found no Critical/High — only Low/Info defence-in-depth, all fixed |
| [SECURITY-AUDIT9.md](./SECURITY-AUDIT9.md) | 9 (first live run) | Dynamic | On a **stale installed build**: 2 HIGH (cross-session broadcast, unauthenticated wallet-rpc) + a Low message defect — fixed immediately |
| [SECURITY-AUDIT9-RETEST.md](./SECURITY-AUDIT9-RETEST.md) ⭐ | 9 RETEST | Dynamic only, live attacks | **9/9 PASS** on the live fixed build; the 2 HIGH not reproducible; 2 Med + 1 Low residuals fixed and re-verified, all with on-chain proof |

## On-Chain Verification

The dynamic tests moved **real Monero stagenet** transactions. Stagenet is
Monero's public test network — its coins have **zero monetary value**, so these
transactions carry no fund risk, but they are still real, mined, and
**independently verifiable by anyone**. That is the entire point: the proof
does not ask you to trust the auditor, it asks you to check the chain.

All test transactions are self-sends to the stagenet address
`56C2ZE1pLbG2zxqaKMFcUpHksR6ktnskuCdEvgo2KxxuA8haWLrsfqYZSxQRK7ubyBeadYo6nSC3mUkiKJnc38YQCwV4Bf2`.
You can verify each hash on a Monero **stagenet** block explorer (for example
`https://stagenet.xmrchain.net/` — paste a hash into its search). A "mined"
transaction will resolve to its block; a "never broadcast" transaction will
**not** be found — and that absence is itself the proof that a gate held.

Key transactions from `SECURITY-AUDIT9-RETEST.md`:

| Tx hash | Status | What it proves |
|---|---|---|
| `bb1d294f7e4155d15ad480f6b12c1b49e9f676a7ae787b0910e96d5aa8e98091` | mined — block 2122567 | Concurrent-session fix: Session A confirmed only its own staged transfer after Session B staged later (old build → 403 clobber) |
| `b1acac478ecf1cd13dd06caba63651b5a63ada12d4559bd504141c5ccf184283` | mined — block 2122569 | Per-session preview store: Session B has an independent slot, no cross-contamination |
| `c193865d56eed3bd81e27d2703dac1136acbbcd52f53443283704d1f83d9bd3e` | mined — block 2122548 | Session isolation: a session can confirm/broadcast only the transfer it staged |
| `66a14ef0a58d0b1c98ce576a43211f4f31e60aa6f52f892cda5b1f5abcaa4de8` | `missed_tx` — never broadcast | Cross-session confirm attack rejected (403) — nothing reached the chain |
| `61138d00298e222d80994c966bbe935cf66abfb5b0769b648128e361899e097c` | `missed_tx` — never broadcast | `/api/transfer/cancel` actually cancelled — the transfer was never sent |
| `ce29a017ded4c754eea440d4a7fa329bd335cbbfdc3bb79e38d6d917a54f883d` | `missed_tx` — never broadcast | Expired (>5 min TTL) preview rejected on confirm — never broadcast |

(The original `SECURITY-AUDIT9.md` also records `3de7b774…`, mined at block
2122507 — the *failure* proof on the stale build, before the fix. It is kept on
purpose: the program documents what broke, not only what passed.)

## Accepted Risks

These are deliberate, documented trade-offs for AIW's threat model
(**single user, local machine, loopback-bound, optionally password-gated**) —
not oversights:

- **Loopback lockout bypass (AUD-001-01).** Brute-force lockout is intentionally
  *not* enforced for requests originating from the same machine (loopback).
  Rationale: an attacker with loopback access already has local-machine access,
  which implies a higher-privilege compromise than the lockout defends against.
  Remote (non-loopback) IPs *are* rate-limited, and `X-Forwarded-For` is never
  trusted for this decision (verified in Pass 9 / RETEST, T-005).
- **ASAR integrity disabled.** The Electron build ships unpacked
  (`asar: false`). There is therefore no ASAR integrity check — but also no
  ASAR-bypass attack surface. Local-filesystem tampering would require code
  execution on the user's machine, outside the stated threat model. Accepted
  for a single-user local app; documented rather than hidden.

## Responsible Disclosure

If you find a security issue in AIW, **report it privately first** — use the
report form on the security page: <https://followtherabbit.app/security#report>.
Submissions are delivered privately to the maintainer; they are not posted
publicly. Please give a reasonable window for a fix to ship before any public
disclosure, and do **not** open public issues for anything that could affect
user funds.

Prefer an encrypted channel? DM [@PokiiDaddy](https://x.com/PokiiDaddy) to
exchange one.

*(Operator note: the form's delivery endpoint is finalized before public
launch — see the security page for current status.)*

---

*Redaction policy: only live credential values, process IDs, and one real
mainnet Bitcoin address were replaced with descriptive placeholders. Every
change is enumerated in [REDACTION-LOG.md](./REDACTION-LOG.md). Stagenet
hashes, attack payloads, endpoint names, and source line references are
published in full — intentionally.*
