Normal behavior: A Confidence Pool insures the outcome of a Safe Harbor agreement, and stakers are meant to lose principal only when the in-scope contracts were genuinely breached. (DESIGN.md #1). states the premise the mechanism rests on: "Only the terminal CORRUPTED state is evidence of an actual breach."
The issue: CORRUPTED is not breach evidence — it is an attestation written by the per-agreement attack moderator, which by default is the agreement owner/deployer, i.e. the same party createPool requires to be the pool sponsor (IAgreement(agreement).owner() == msg.sender). Upstream AttackRegistry.markCorrupted() is onlyAttackModerator and requires no proof of any exploit. The pool reads this attestation throughgetAgreementState() and cannot distinguish a fabricatedCORRUPTEDfrom a real one. Because the scope-blind auto-CORRUPTED backstop then moves the entire pool to the sponsor-controlled recoveryAddress, a sponsor can write a CORRUPTED attestation against their own agreement — with no breach — and arm a sweep of all staker principal.
Why this is not already covered by DESIGN.md. The design accepts the outcome but justifies it on reasoning that is wrong for a fabricated attestation:
#1 — asserts CORRUPTED is "evidence of an actual breach." It is a sponsor-settable attestation with no on-chain proof. This is the foundational over-trust; it is true unconditionally, independent of any later precondition.
#6 — accepts the auto-CORRUPTED loss "if the moderator is absent AND the breach was out-of-scope." Its reasoning assumes a real breach that is merely mis-scoped, which the sponsor cannot cause. Here the CORRUPTED trigger is manufactured on demand, so the loss is deliberately armable on every pool rather than a passive coincidence.
#11 — treats a registry "reporting false state" as "the trusted [DAO] entity attacking its own infrastructure — out of the adversarial model," and reasons only about the false-PRODUCTION direction ("worst case is principal returned to stakers"). This path needs no DAO misbehavior — it is an untrusted sponsor writing to an honest registry via a legitimately held role — and it is the false-CORRUPTED → sweep direction #11 never considers.
The sponsor drives the agreement to UNDER_ATTACK (which the pool observes, sealing riskWindowStart and locking stakers out of withdraw), then calls markCorrupted with no breach. During the expiry + MODERATOR_CORRUPTED_GRACE window the pool is frozen for stakers (no withdraw, no claimSurvived, and claimExpired reverts). Once the grace elapses without the pool's DAO moderator flagging SURVIVED, anyone permissionlessly finalizes the pool as bad-faith CORRUPTED and sweeps it to the sponsor.
Likelihood:
A protocol that deploys and owns its own agreement holds the attack-moderator role for it (s_authorizedOwner → attackModerator), so the CORRUPTED trigger is available to the sponsor on their own pools with no external cooperation — and markCorrupted requires no proof, so arming the loss costs the sponsor only a griefed reputation on an agreement they never intended to honor.
The sponsor unilaterally arms the trap; the loss discharges only after the pool's DAO moderator fails to flag SURVIVED for the full expiry + MODERATOR_CORRUPTED_GRACE (180 days). This single liveness dependency is the reason severity is Medium rather than High: under a live, honest DAO the sweep never occurs.
Because arming is free and repeatable, the residual risk is the DAO-lapse rate maximized adversarially — the sponsor fabricates across many pools and times periods of DAO overload — rather than the passive (real-breach × lapse) coincidence DESIGN.md #6 reasoned about.
Impact:
100% of every staker's principal, plus the entire bonus, is swept to the sponsor-controlled recoveryAddress.
Stakers have no self-help during the grace window: withdraw reverts (riskWindowStart != 0), claimSurvived reverts (no SURVIVED flag), and claimExpired reverts (AgreementCorruptedAwaitingModerator). Their funds are frozen and their principal is destroyed unless the DAO intervenes.
The core guarantee of the mechanism — that principal is lost only on a genuine in-scope breach (DESIGN.md #1) — is violated: principal is lost on a fabricated attestation.
Two verified, passing tests. The first isolates the pool-side sweep; the second is a full end-to-end against the real BattleChain registry stack, proving the sponsor legitimately holds the attack-moderator role and fabricates CORRUPTED with no breach.
How to reproduce the test file:
The upstream concretes are solc 0.8.34, the pool is solc 0.8.26, so this needs a multi-version build, which the repo isn't set up for by default:
Unpin solc — remove solc = "0.8.26" from foundry.toml (the pool never imports the concrete upstream, so the repo normally only ever compiles 0.8.26).
Add a remapping — solady/=lib/battlechain-safe-harbor-contracts/lib/solady/src/, and recursively init the solady submodule (git submodule update --init --recursive), which the repo's flat clone omits.
solc 0.8.34 must be installed, and the E2E file is pragma 0.8.34 — it never imports the 0.8.26 pool/token sources (would be an incompatible unit); it drives them via local IPool/IToken interfaces + deployCode("ConfidencePool.sol:ConfidencePool").
The pool cannot verify a breach, so the permissionless, scope-blind backstop must not move principal to a sponsor-controlled address based on an attestation the sponsor can forge. The staker-protective default for the unverifiable path is to return principal (EXPIRED), leaving the punitive CORRUPTED sweep to a live moderator only. This reassigns the "real breach escapes if the moderator is absent" cost onto the sponsor/protocol (who is responsible for moderator liveness) instead of onto stakers (who otherwise lose principal to a fabrication).
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.
The contest is complete and the rewards are being distributed.