A non-claim bonus sweep during the moderator's re-flag window strands the bonus at recoveryAddress, so a corrected good-faith CORRUPTED pays the named whitehat only the principal instead of the whole pool.
The moderator may re-flag an outcome before the first claim (DESIGN §4), and good-faith CORRUPTED entitles the named whitehat to the WHOLE pool = snapshotTotalStaked + snapshotTotalBonus (DESIGN §12). sweepUnclaimedBonus deliberately does not latch claimsStarted so a dust donation cannot grief the re-flag window.
In the riskWindowStart == 0 branch, sweepUnclaimedBonus permanently transfers the entire bonus to recoveryAddress and zeroes live totalBonus, without latching claimsStarted. If the moderator first flags SURVIVED (out-of-scope judgement) and later corrects to good-faith CORRUPTED, flagOutcome re-snapshots snapshotTotalBonus from the now-zero totalBonus, so bountyEntitlement = snapshotTotalStaked only. The bonus already left irreversibly (contributeBonus is blocked post-outcome), so the whitehat can never receive it — it is stranded at the sponsor's recoveryAddress.
Likelihood:
Triggers when the registry reaches CORRUPTED with riskWindowStart == 0 (a breach with no observed active-risk window), the moderator's first flag is SURVIVED and is later corrected to good-faith CORRUPTED (exactly the correction the re-flag window exists for, DESIGN §4).
Realized when anyone — notably the sponsor, who controls recoveryAddress and profits — calls the permissionless sweepUnclaimedBonus between the SURVIVED flag and the correction.
Impact:
The named whitehat is under-paid by the entire bonus pool; those funds are diverted to the sponsor-controlled recoveryAddress.
Breaks the DESIGN §12 whole-pool-to-attacker guarantee and the DESIGN §4 costless-correction guarantee.
The baseline confirms a direct good-faith CORRUPTED pays the whitehat the whole pool (200), while the exploit shows a permissionless sweepUnclaimedBonus during the SURVIVED→CORRUPTED re-flag window strands the bonus at recoveryAddress, leaving the whitehat only the principal (100).
Do not release the no-risk bonus sweep until the outcome is final. A real claim (not a dust donation) latches claimsStarted, so gating the sweep on it closes the correction race without reintroducing the dust-grief the current design avoids:
(Alternative: reserve snapshotTotalBonus even when riskWindowStart == 0 until claimsStarted.)
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.