# ZK adversarial findings — remediation and staging handoff

Date: 2026-09-10

All seven findings from the [adversarial review](/audits/groth16/evm-adversarial/) have code fixes and regression coverage. This change does not alter circuits, proving/verifying keys, Solidity contracts, or deployments. It does not establish the absence of other vulnerabilities.

## Remediation

| Finding | Implemented fix | Regression evidence |
| --- | --- | --- |
| ADV-01: untrusted relayer gas spend | The server constructs all gas fields; client fee/type/nonce/access-list/gas-limit suggestions cannot reach the signer. Explicit fee/gas/calldata/per-transaction ceilings, mandatory estimation and final-envelope simulation, and durable atomic lifetime global/per-signing-account reservations apply even with optional preflight disabled. No unbudgeted fee-bump retries or automatic budget resets/refunds. | Policy and actual-handler tests cover malicious gas envelopes, failed/excessive estimates/quotes, final-call failure, ambiguous sends, frozen-envelope signing, expiry, malformed/missing/corrupt ledger configuration, restart persistence, and concurrent processes. |
| ADV-02: invalid Web Locks options | Preserve fail-fast exclusive locking without combining `ifAvailable` and `signal`. Check cancellation before and inside the callback; hold the lease until work settles. | Native Node Web Locks implementation, contention, cancellation timing, lease retention, and fallback tests. |
| ADV-03: proxy rejects minimal V2 envelope | The pool route still requires an access JWT but no redundant public-wallet body field. Every supplied wallet alias must match the JWT; legacy money-route requirements remain. | Actual proxy tests accept minimal requests, reject missing/expired/wrong-type credentials and conflicting aliases, and preserve the outbound body. |
| ADV-04: mismatched HMAC formats | Frontend callers share one signer; Base Node routes require the same V2 method/path+query/timestamp/idempotency/API-key-hash/raw-body-hash transcript. Matched-route classification prevents uppercase Express aliases from selecting legacy authentication. Other route protocols remain unchanged. | Shared fixed signature vector, tampering tests, real HTTP middleware, uppercase legacy rejection, and actual frontend-proxy-to-Node loopback integration. |
| ADV-05: cancellation fills journal | Explicit wallet rejection or a known local pre-send abort becomes an encrypted inactive `wallet_rejected` archive, outside the active-64 limit. Shared handling covers deposits and private settlements. Secrets remain available for copied-proof recovery; ambiguous errors remain active. | More than 64 rejected settlements and deposits, retained swap seeds/output secrets, subsequent copied-proof inclusion, quota failures, late aborts, receipt-time failures, frozen errors, and failing advisory logging. |
| ADV-06: bridge hash-loss recovery | Discover `PrivateBridge` events and authenticate them against the recorded canonical calldata, nullifiers, token/payout, target, and bound order ID. Require a matching successful canonical receipt and receipt event at sufficient finality. Preserve journal/hash/order context and suppress automatic quote retries while pending. | Lost hash with and without change; mismatched event fields/nullifier order; missing/duplicate/removed/foreign receipt events; failed, noncanonical, premature, or mismatched transactions; adapter/hook context tests. |
| ADV-07: SDK reads latest | Use native ethers v6 `transaction.blockTag`; update the provider interface and frontend wrapper. Refresh the tracked SDK build and frontend packaged dependency. | Actual ethers provider call normalization pins all five reads; frontend tests exercise the installed SDK through its wrapper. |

Retained canceled records can span more than 64 historical tree domains. The obsolete global tree-count failure was removed: scans remain sequential and block-range bounded, and archived records are not silently ignored or purged. A regression verifies that 65 archived domains do not block an active settlement.

## Verification

- Full frontend suite: **270 tests passed in 41 suites**.
- Frontend TypeScript check: passed.
- SDK build and full suite: **62 tests passed**.
- Full Node relayer suite: passed, including **17 new spending-policy/handler groups**, V2 validation coverage, and HMAC middleware tests.
- Actual frontend proxy to actual Node server over loopback: passed, with mainnet submission deliberately gated. No funded transaction was broadcast.
- SDK archive: all 94 archived files were checked against the build and installed package. SHA-256: `8a006135e8ff7270ec737c527f7507da795029dc40cc83bf37d436dcc104a2e2`; package-lock integrity refreshed.
- Repository diff checks: passed.
- Frontend production build: passed. Existing large-bundle and stale Browserslist-data warnings remain; dependency metadata was not upgraded as part of these fixes.

The circuit/contract suites from the preceding review were not repeated for this application/SDK/relayer-only change. Live Base-fork execution and full production-service configuration verification remain outside this handoff. Mock-chain tests demonstrate policy and recovery behavior, not acceptance of dummy proofs on-chain.

## Required operational setup

The Node EVM sponsorship path now **fails closed until explicitly configured**. No funded allowance was invented or enabled during this change. Before enabling it, an operator must choose the allowed chains, fee/gas/calldata ceilings, per-transaction and lifetime budgets, and provision the ledger using the explicit initialization command. See EVM sponsorship policy (`stealthos-relayer/docs/EVM_SPONSORSHIP_POLICY.md`).

All participating processes must use the same durable local SQLite ledger on one host. Separate or ephemeral ledgers do not provide a shared allowance. No automatic refund, daily reset, or cap-increase mechanism is present. The signer quota is for the relayer EOA, not a newly linked private-user identity.

Base L1/operator overhead uses current oracle quotes and conservative reserved headroom. Transaction fee fields cannot impose a hard future rollup-overhead ceiling; the documentation explicitly distinguishes admission reservations from eventual total ETH expenditure. Use appropriately limited signer funding and operational supervision.

If the frontend proxy targets this Node service, configure matching Base HMAC secrets and the existing `ALLOW_NO_ORIGIN_CORS=true` setting for server-to-server requests. Authentication remains mandatory. See V2 authentication contract (`stealthos-relayer/docs/base-relay-v2-auth.md`).

Archived cancellations still consume browser storage and reconciliation work. Historical prepared records cannot be safely classified retroactively and are not purged. Browser persistence is not a cross-device backup. Refresh old app tabs after upgrading. See wallet recovery policy (`privacy-frontend/docs/joinsplit-v2-recovery-policy.md`).

## Staging commits

| Repository | Commit pushed to staging |
| --- | --- |
| privacy-frontend | `bfc82d97d6d712b4e0153138e4ee40d59cd8f712` |
| privacy-protocol | `491fe1249e2b9c89566814023003bc62d843979e` |
| stealthos-relayer | `bef98e7cddf993f7151d0fb80dc0885088f09245` |

Each remote `refs/heads/staging` was independently queried after pushing and
matched its local commit exactly. All three working trees are clean. This
handoff records a successful code push, not verification of any automated
deployment or provisioning of the new sponsorship budget.
