Affected: ConfidencePool.sol:288-301 · ConfidencePool.sol:784-799
A pool that observes the registry directly in a terminal state seals only riskWindowEnd. withdraw() consults only riskWindowStart. After a legitimate BattleChainSafeHarborRegistry.setAttackRegistry migration, the fresh AttackRegistry returns NOT_DEPLOYED for the agreement until re-registration (AttackRegistry.sol:855-857; L882-884 documents NEW_DEPLOYMENT unreachable via the public API). In that transient state, withdraw() passes both halves of its gate and a staker exits full principal after terminal CORRUPTED was already locally observed.
Falsifies DESIGN.md §11: "A benign upstream state rewind cannot re-open withdraw: that is gated on the one-way riskWindowStart != 0 latch."
Likelihood: Requires a pool that saw no UNDER_ATTACK interaction plus a routine DAO registry migration. DESIGN §11 treats migration as expected (per-clone pinning is explicitly rejected because it "would brick every existing pool" on migration).
Impact: Principal that should stay committed to the CORRUPTED resolution exits to the staker. Aggregate loss up to totalEligibleStake; comes at the expense of either recoveryAddress or the good-faith whitehat.
Actor: Withdrawal permissionless. DAO acts via its documented onlyOwner migration entrypoint — in-model, not adversarial.
Make a terminal observation equally binding for the withdraw gate. The no-observed-risk bonus rule (§5) is unaffected — only riskWindowStart == 0 still routes bonus to recovery.
Save as test/TerminalEndRewindWithdraw.t.sol and run forge test --match-contract TerminalEndRewindWithdrawTest -vv.
Impact - Low A staker exits with principal the CORRUPTED resolution was meant to sweep, and the shortfall lands on whoever that outcome was going to pay: the sponsor's recoveryAddress under bad faith, or the named whitehat under good faith. Other stakers are untouched, since each escape only removes its own principal from the snapshot. Nobody gains beyond the one staker sidestepping a loss they had already committed to. Likelihood - Low Two things have to line up. The pool has to reach a terminal state without ever polling during active risk, which is the same no-observed-risk situation §5 already documents as reachable. Then the attack-registry pointer has to be migrated while the pool is still unresolved, in the window before the fresh registry has re-registered the agreement. Neither is exotic alone, though the second is a trusted-party operation that no attacker can trigger.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
View preliminary resultsAppeals are being carefully reviewed by our judges.
The contest is complete and the rewards are being distributed.