A pool that resolves CORRUPTED is meant to pay the entire pool (staked principal + contributed bonus) to the moderator-named good-faith whitehat attacker (docs/DESIGN.md §12). The moderator may correct an outcome (e.g. SURVIVED → CORRUPTED) at any time before the first value-moving claim — and sweepUnclaimedBonus deliberately does not latch claimsStarted, so that correction window stays open after a sweep.
When a pool resolves without an observed risk window (riskWindowStart == 0 — DESIGN.md §5's "no-risk-window CORRUPTED race"), sweepUnclaimedBonus reserves only the staked principal and lets the entire contributed bonus be swept to the sponsor-controlled recoveryAddress. A single permissionless sweep, timed between an honest out-of-scope flagOutcome(SURVIVED) and its designed-for correction to flagOutcome(CORRUPTED), permanently removes the bonus from the whitehat's bounty: the re-flag re-reads the now-zeroed live totalBonus into the snapshot.
Likelihood:
Applies whenever the registry reaches a terminal CORRUPTED state without any pool entrypoint observing an active-risk state first (a fast ATTACK_REQUESTED → CORRUPTED transition), leaving riskWindowStart == 0 — a path the protocol's own DESIGN.md §5 documents as reachable.
The moderator makes the documented out-of-scope SURVIVED call and later corrects it to CORRUPTED; the sweepUnclaimedBonus() trigger is permissionless, so any third party — or the sponsor who owns recoveryAddress — front-runs the correction.
Impact:
Permanent, unrecoverable diversion of up to 100% of the pool's contributed bonus to recoveryAddress instead of the named whitehat.
Breaks the DESIGN.md §12 guarantee that a CORRUPTED resolution makes the entire pool the attacker's bounty; the whitehat is shorted to principal-only.
Two Foundry tests (extending the repo's BaseConfidencePoolTest) share identical preconditions; the only difference is the inserted SURVIVED misflag plus one permissionless sweepUnclaimedBonus(). control yields the intended stake + bonus; exploit yields stake only, with the bonus stranded at recoveryAddress.
Concrete outcome: the named whitehat receives 100e18 (stake only) instead of the promised 150e18 (stake + bonus) — 50e18, i.e. 100% of the contributed bonus, is permanently diverted to recoveryAddress.
Result (Foundry 1.7.1, solc 0.8.26, run against the contest commit in an isolated container):
Do not let the bonus leave the pool while the outcome can still be corrected to CORRUPTED (i.e. before claimsStarted):
Alternatively, reserve snapshotTotalBonus - claimedBonus regardless of riskWindowStart whenever !claimsStarted, and on a SURVIVED → CORRUPTED re-flag reconstruct the bounty from the resolution-time totals rather than the post-sweep live totalBonus.
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.