The moderator may correct a posted outcome (fix a wrong outcome or attacker) right up until the first claim trips the one-way claimsStarted finality latch — DESIGN §4 states the re-flag window "closes on the first claim (by design)." Every value-moving claim/sweep path sets claimsStarted on execution (claimSurvived, claimCorrupted, claimAttackerBounty, sweepUnclaimedCorrupted, claimExpired), so once value moves, the outcome is frozen. Separately, when a pool resolves SURVIVED with riskWindowStart == 0 (no on-chain-observed risk window), _bonusShare pays zero to every staker, so the whole bonus pot is legitimately swept to recoveryAddress (DESIGN §5).
sweepUnclaimedBonus() is the one value-moving path that deliberately does not set claimsStarted, and its reservation gates the accounted bonus solely on riskWindowStart != 0. So when riskWindowStart == 0, the entire accounted snapshotTotalBonus is treated as unreserved and can be transferred out without tripping the finality latch. Because recoveryAddress is owner-settable at any time (setRecoveryAddress, onlyOwner), the sponsor can point it at itself and, the moment the moderator posts SURVIVED, front-run the moderator's pending correction to good-faith CORRUPTED with a permissionless sweepUnclaimedBonus. The bonus lands at the sponsor's address, claimsStarted stays false (so the correction still "succeeds"), and the corrected bountyEntitlement is recomputed from the now-drained pot — leaving the named whitehat entitled to principal only. DESIGN §4's guarantee is violated: value left the pool before any "claim," yet the correction window it was supposed to protect is hollow.
Occurs whenever a genuinely in-scope breach is settled on a pool whose active-risk window was never observed on-chain (riskWindowStart == 0) — the default for any low-activity pool, since the window is sealed only if some interaction happens to coincide with the active-risk dwell.
Occurs on the exact SURVIVED→good-faith-CORRUPTED correction that DESIGN §4's re-flag window is built for (the moderator's off-chain in/out-of-scope judgement being revised), so it fires during a supported, anticipated operation rather than an unusual error.
The sponsor is economically motivated (the swept bonus lands at its own recoveryAddress) and can reliably win the race — sweepUnclaimedBonus is permissionless and the correction is a public mempool transaction it front-runs.
The named good-faith attacker (whitehat) permanently loses the entire bonus portion of the bounty; the bonus pot is designed to be substantial (it exists to attract stakers), so the loss can be large.
The value is misappropriated between parties — from the whitehat the corrected outcome owes it to, to the sponsor — and is irreversible (it has physically left the pool to a sponsor-controlled address).
A protocol-documented safety mechanism (the moderator's pre-claim outcome correction) is silently defeated by a permissionless action, violating the DESIGN §4 guarantee that correction is possible until the first claim.
Add to a test inheriting the project's BaseConfidencePoolTest and run with forge test. Both pass green.
Do not set claimsStarted inside the sweep — that would let a 1-wei donation trip the finality latch and block the moderator's re-flag window (the reason the latch is intentionally omitted here). Instead, reserve the accounted bonus whenever the correction window is still open (!claimsStarted), so only genuine donations/dust remain sweepable before finality:
Once a legitimate claim sets claimsStarted, the outcome is final and the DESIGN §5 riskWindowStart == 0 behavior resumes (the unowed bonus becomes sweepable to recoveryAddress). This closes the front-running window without reintroducing the dust-griefing concern.
Impact – Medium Up to the entire bonus pool can be routed to the wrong party for good, with no on-chain way to unwind it, and in a stakerless pool the effect is total since bountyEntitlement computes to zero and claimAttackerBounty reverts outright. This impact is definitely not High: the pool stays solvent throughout with no principal ever at risk, and the money ends up at the sponsor's own recoveryAddress, which in most pools means a sponsor recovering a bonus they funded themselves. The whitehat's claim on that bonus also only exists because the moderator changed their mind after the fact. Likelihood – Low Four separate things have to coincide: nobody touches the pool for the whole active-risk window (or there are no stakers to begin with), the moderator flags SURVIVED and later reverses to CORRUPTED, that reversal is good-faith with a named attacker, and a sweep lands between the two flags. The first cuts against staker self-interest, since skipping the poke costs them their entire bonus share, and the second asks the moderator to overturn a scope judgement, which is a bigger deal than the typo fix DESIGN.md #4 offers the window for. The sweep itself I'd treat as near-certain once the rest holds, given it's permissionless and the sponsor has an obvious reason to make the call, but assembling the first three in one pool lifecycle is where this stays rare.
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.