Security Audit Program

Built for One.
Hardened for Everyone!

Before a single user ever touched the code, AIW underwent 9 independent security passes β€” 8 static analysis and 1 dynamic runtime test with real on-chain proof. Every finding documented. Every finding fixed.

55
VULNERABILITIES FOUND
55
VULNERABILITIES FIXED
9
AUDIT PASSES
52/52
SECURITY CHECKS PASSED
Findings by severity

Every class. Every finding.

Eight passes of static analysis and one dynamic runtime test found vulnerabilities across all severity levels. None are open.

8
Critical
All addressed
9
High
All addressed
22
Medium
All addressed
16
Low
All addressed
Static analysis β€” 8 passes

Every pass found something new

Each audit used the same codebase. Each found vulnerabilities the prior passes missed. That is why you keep going.

1
PASS 01
Initial security review
Auth Β· RPC exposure Β· Agent SDK Β· seed access Β· wallet files
4 critical 5 high
β–Ό
  • CRIT
    LAN-exposed RPC ports β€” anyone on the same WiFi could call sweep_all with no authentication. No credentials. Full wallet drain from any device on the network.
    All RPC ports bound to loopback only
  • CRIT
    relay_tx bypass β€” staged transactions could be broadcast without going through the confirm dialog by calling relay_tx directly through the RPC proxy.
    relay_tx blocked at all four choke points; do_not_relay enforced on all AI-staged spends
  • CRIT
    Agent SDK subprocess received the full process environment including API keys, wallet passwords, Strike PIN, Decree PIN, and database credentials.
    Strict OS-runtime-only allowlist; credential regex strip applied as belt-and-suspenders
  • CRIT
    query_key accessible through the AI chat agent β€” the wallet mnemonic and spend key were extractable through natural language conversation.
    query_key blocked on all four dispatch points: handleChat, handleChatStream, /api/rpc/wallet, /api/rpc/daemon
  • HIGH
    Brute-force lockout used X-Forwarded-For for IP keying. Any attacker could spoof the header to appear as localhost and bypass the lockout entirely.
    Lockout keyed exclusively to req.socket.remoteAddress β€” X-Forwarded-For never trusted
2
PASS 02
Auth & web security
Blank password Β· CORS bypass Β· card XSS Β· secret masking
1 critical 5 medium
β–Ό
  • CRIT
    Blank DASHBOARD_PASSWORD disabled ALL protection. When blank, isAuthenticated() returned true for everyone β€” every fund-moving endpoint was completely unauthenticated by default.
    Startup guard refuses non-loopback bind without password; process.exit(1) on unsafe config
  • MED
    CORS same-origin check used a substring test. A page served from localhost:3000.attacker.com would pass the check because the host string includes "localhost:3000".
    Exact host equality via new URL(origin).host === req.headers.host
  • MED
    Card memo, nickname, merchant name, and CVV were interpolated directly into innerHTML with no escaping. A card nickname containing a script tag would execute in the wallet dashboard with same-origin access to every fund-moving endpoint.
    Single canonical escapeHtml applied to all card field sinks; duplicate definition removed
3
PASS 03
Seed gate, SDK isolation, CLI regression
Seed reveal 2FA Β· Agent SDK cwd isolation Β· startup path regression
3 medium
β–Ό
  • MED
    Seed reveal had no second factor. Any authenticated session could export the full 25-word mnemonic in one POST request with no additional confirmation.
    Dashboard password β†’ wallet password β†’ explicit confirm gate with 3-attempt lockout and audit log
  • MED
    Agent SDK subprocess could load .mcp.json or .claude/settings.json from the AIW working tree, allowing a repo-level file to spawn MCP servers under the user's Claude subscription session.
    Isolated scratch cwd via mkdtemp, settingSources:[], mcpServers:{}, allowedTools:[], maxTurns:1
  • MED
    The secureBindHost() guard was only wired into the Electron app startup path. The npm start CLI path β€” which most developers use β€” still bound to all network interfaces when no password was set.
    Guard applied to both startup paths; both honor DASHBOARD_BIND and enforce loopback on no-password configs
4
PASS 04
DNS rebinding & RPC proxy spend gate
DNS rebinding Β· direct proxy spend Β· body size limit
1 high 2 medium
β–Ό
  • HIGH
    DNS rebinding bypass. A malicious webpage could rebind its domain to 127.0.0.1. The browser would send both Origin and Host headers showing the attacker's domain. The same-origin check compared them against each other β€” both were attacker-controlled β€” and treated the request as same-origin.
    Host header allowlist rejects any non-loopback Host in passwordless mode before auth gate
  • MED
    /api/rpc/wallet and /api/btc/rpc forwarded spending methods (transfer, sweep_all, sendtoaddress) directly to the daemon with no confirmation gate β€” bypassing the chat confirm dialog entirely.
    Spending methods rejected with 403 on all raw RPC proxies; redirect to confirm-gated flow
  • MED
    readBody() buffered the entire request in memory with no cap. A large POST to any endpoint could exhaust the server process memory.
    2MB cap enforced; 413 returned on overflow; connection destroyed
5
PASS 05
BTC realm key-exfiltration gap
BTC key exposure Β· silent transfer fallback Β· direct spend bypass
1 critical 1 high
β–Ό
  • CRIT
    The BTC realm had no key-exfiltration blocklist. dumpprivkey, dumpwallet, and listdescriptors true were all callable through both the AI chat pipeline and the raw RPC proxy β€” yielding the master private key with a single call.
    BTC key-exfil methods added to the fail-closed blocklist on all four enforcement sites
  • HIGH
    /api/transfer/send had a silent fresh-transfer fallback. If no pending preview existed, it built and broadcast a new transaction from the request body directly β€” bypassing the entire confirm dialog the threat model depended on.
    Endpoint requires a prior staged preview; returns 409 with clear error if none exists
6
PASS 06
Swap/exchange confirm gate & memory poisoning
Chat money pipeline Β· RPC allowlist Β· memory injection Β· committed secrets
2 critical 2 high
β–Ό
  • CRIT
    Swap, exchange, and Strike execution had no confirmation gate at all. The do_not_relay staging model covered XMR and BTC transfers β€” but not swaps, exchanges, or Strike withdrawals. A single prompt-injected AI tool call moved real funds with no confirm dialog.
    All chat-driven fund-moving actions staged into _pendingSpendAction; require authenticated human confirm plus PIN for Strike
  • CRIT
    Live ASB seed file, Monero hot wallet, BDK wallet database, and Tor logs were committed to git history under swaps/asb-data/. Publishing the repo would have exposed these permanently.
    Untracked and gitignored; private repo will never be published; open source ships from a clean repo with no history
  • HIGH
    RPC filter was a fail-open denylist. Any method not explicitly blocked passed through by default β€” including any future wallet-rpc method that might expose key material.
    Positive allowlists on all surfaces; anything not on the list is rejected; fail-closed by design
  • HIGH
    The remember tool persisted arbitrary text into user-memory.json, which was injected into every future system prompt. A single malicious transaction memo could poison the AI agent's behavior across all future sessions indefinitely.
    Memory sanitized, capped, injection sequences stripped; labeled as untrusted user-asserted notes in all system prompts
7
PASS 07
Independent review
All 10 attack surfaces re-examined after 6 remediation passes
no critical or high
β–Ό

"The codebase is heavily hardened. Every primary attack class in the brief is already mitigated by explicit, documented controls. No high/critical findings. Findings below are residual hardening items (LOW / INFO)."

β€” SECURITY-AUDIT7.md, verdict

  • LOW
    R1 β€” Strike PIN comparison used a non-constant-time String(pin) !== String(STRIKE_PIN), leaving a timing side-channel the login/Decree/seed gates had already closed.
    Routed through safeStrEqual(), matching the other PIN/password gates
  • LOW
    R2 β€” Zero-config seed reveal accepted {confirm:true} from loopback when neither dashboard nor wallet password was set.
    Returns hasSecret:false and refuses with a setup instruction when no secret is configured
  • INFO
    R3 β€” Static-file prefix check via startsWith(PUBLIC_DIR) could match a sibling directory like <PUBLIC_DIR>-x. R4/R5 (XMR raw-proxy explicit auth re-check, Origin-less CSRF surface) raised as optional defence-in-depth β€” already neutralised by the spend-403 + fail-closed allowlist and SameSite=Strict.
    R3 tightened to exact-dir + separator check; R4/R5 accepted, no fund-movement risk β€” "Ship."
8
PASS 08
Final static hardening
XMR proxy auth parity Β· confirm dialog source of truth Β· staged spend details
3 low improvements
β–Ό
  • LOW
    /api/rpc/wallet and /api/rpc/daemon relied only on the global auth gate, while /api/btc/rpc, seed reveal, decree, and spend-confirm all had explicit per-route isAuthenticated checks as defense-in-depth.
    Explicit 401 guard added to both XMR proxy routes for full parity
  • LOW
    The staged-transaction confirm card sourced the amount and destination address from model-controlled markdown. A prompt-injected agent could render misleading text around a real transfer.
    Server-derived details[] array rendered from actual staged params in a tamper-evident confirm block; agent prose cannot alter it (AUD-#19)
  • LOW
    Credentialed CORS reflection: a request from https://localhost carrying a valid Bearer token was accepted because the default MOBILE_ORIGINS allowlist is broad for the Android client.
    Operator guidance in .env.example + code comment to narrow MOBILE_ORIGINS to capacitor://localhost when no Android client is used (AUD-#18); plus daemon/provider names escaped (INFO)
Pass 9 β€” Dynamic runtime testing

Attacks against the live wallet

Not static analysis. Not code review. The wallet was running. The tests were real. Real stagenet transactions served as proof.

βš—οΈ
Nine tests. One live system.
System under test: AIW.exe on localhost:3000, stagenet wallet test-wallet-02, daemon synced 100%. Every claim backed by HTTP status codes, response bodies, or on-chain transaction hashes. No mainnet spend was performed.
T-001
Prototype pollution via JSON endpoints
Pass β€” allowlist not bypassable
T-002
Cross-session preview race condition
Fail found β†’ Fixed & verified
T-003
wallet-rpc unauthenticated direct access
Fail found β†’ Fixed & verified
T-004
20-payload prompt injection battery
Pass β€” 20/20 contained
T-005
Lockout persistence & spoofing resistance
Pass by design
T-006
Electron CVE-2025-55305 & ASAR
Pass β€” v42.1.0 not vulnerable
T-007
Full spend pipeline E2E on stagenet
Pass β€” 4/4 gates real & on-chain
T-008
npm supply chain behavioral scan
Pass β€” 0 vulnerabilities
T-009
Exchange deposit address validation
Pass + server-side hardened
On-chain evidence β€” stagenet transactions (click any hash to copy the full ID β€” verify it on a Monero stagenet explorer you trust; "missed" txs were never broadcast, their absence is the proof)
3de7b774aefc6700658e356ea21beb331b8d1f… T-002 failure proof MINED BLK 2122507
bb1d294f7e4155d15ad480f6b12c1b49e9f676… T-002 fix verified MINED BLK 2122567
ca9b74700f220616471d65739483687b2172f4… T-007 spend gate MINED BLK 2122551
66a14ef0a58d0b1c98ce576a43211f4f31e60a… T-002 cross-session blocked MISSED β€” NEVER BROADCAST
ce29a017ded4c754eea440d4a7fa329bd335cb… T-007 TTL expiry gate MISSED β€” EXPIRED PREVIEW
b1acac478ecf1cd13dd06caba63651b5a63ada… T-002 fix β€” independent slot MINED BLK 2122569
61138d00298e222d80994c966bbe935cf66abf… /api/transfer/cancel works MISSED β€” CANCELLED, NEVER SENT
βœ“
Independent re-verification β€” 9/9 on the live build
A third independent agent attacked the live deployed system after all fixes were applied. The two original High findings (T-002 cross-session broadcast, T-003 unauthenticated wallet-rpc) are not reproducible, and the two Medium and one Low residual items were fixed and re-verified with fresh on-chain evidence from stagenet blocks 2122567 and 2122569. Backed by automated suites: 52/52 security-verification checks (security-verify-results.csv), 8/8 dynamic regression checks (audit9-regression.mjs), and 86 Postman API assertions across 51 requests β€” all passing. The working tree matches what is actually running.
⭐ FINAL VERIFICATION β€” full RETEST report β†’ Pass 9 β€” first dynamic run (original findings) β†’
Why this matters

Rule #0: Never trust AI

RULE #0

AIW was built by orchestrating AI agents β€” not by writing code manually. That means every security claim had to be verified by a different agent than the one that made it. The agent that fixes a bug will tell you the fix works. The agent that finds the bug will tell you if it doesn't.

The first Pass 9 auditor wrote "All findings resolved. Fixes verified." without running the tests. The second auditor ran the actual tests and found two High vulnerabilities still present β€” proven with real stagenet transactions. The third auditor independently re-verified the fixes on the deployed system.

That is the process. Not one pass. Not one agent. Trust the evidence, not the claim.

Audit documents & test artifacts

Everything ships with the repo

All ten audit documents are published in full β€” only live credential values, process IDs and one real mainnet address were replaced with descriptive placeholders. The redaction log itemizes every change. Click any document to read it.

SECURITY-REVIEW.md β†’
Pass 1 β€” LAN exposure, relay_tx bypass, Agent SDK leak
SECURITY-AUDIT2.md β†’
Pass 2 β€” blank password, CORS bypass, card XSS
SECURITY-AUDIT3.md β†’
Pass 3 β€” seed 2FA, SDK isolation, CLI regression
SECURITY-AUDIT4.md β†’
Pass 4 β€” DNS rebinding, RPC proxy spend gate
SECURITY-AUDIT5.md β†’
Pass 5 β€” BTC key-exfil, silent transfer fallback
SECURITY-AUDIT6.md β†’
Pass 6 β€” swap confirm gate, memory poisoning
SECURITY-AUDIT7.md β†’
Pass 7 β€” independent clean review, no critical or high
SECURITY-AUDIT8.md β†’
Pass 8 β€” final static hardening, 3 low improvements
SECURITY-AUDIT9.md β†’
Pass 9 β€” dynamic testing, original findings and fixes
SECURITY-AUDIT9-RETEST.md β†’
Independent re-verification, 9/9 pass on live deployed system + remediation proof
⭐ FINAL VERIFICATION
index.md β†’
Narrative overview β€” how to read the passes, on-chain verification guide
REDACTION-LOG.md β†’
Every public-release change, itemized and justified
aiw-security-audit9.xml
The Pass 9 dynamic test spec β€” ships in the repo (open source)
tests/security/audit9-regression.mjs
Executable regression suite β€” 8/8 checks; ships in the repo
πŸ”’ All audit documents are published in full, with only minor credential redactions. View the redaction log to see exactly what was changed and why, or read the audit program overview.
Responsible disclosure

Found something we missed?

These documents invite scrutiny β€” that is the point. If you find a vulnerability, tell us privately first so it can be fixed before it is public. No bug bounty yet, but credit is given to anyone who wants it.

Report privately. Do not open a public issue or post details for anything that could move user funds β€” send it here and give us a reasonable window to ship a fix before disclosure.
Submissions are delivered privately to the maintainer β€” they are not posted publicly. Prefer encrypted contact or have no email? DM @PokiiDaddy to exchange a secure channel.