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.
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.