sweepUnclaimedBonus() can transfer the pool's accounted bonus to recoveryAddress without setting claimsStarted.
While claimsStarted == false, the moderator is still permitted to correct a previously flagged outcome. If the moderator changes a SURVIVED outcome to good-faith CORRUPTED, flagOutcome() creates a new snapshot from the current totalBonus.
Consequently, when riskWindowStart == 0, any account can sweep the entire bonus before the correction transaction. The subsequent re-flag snapshots the reduced totalBonus, permanently reducing the whitehat's bounty by the swept amount.
The protocol permits the moderator to re-flag an outcome until claimsStarted becomes true:
Every call to flagOutcome() overwrites the resolution snapshots using the current live accounting:
When a pool has been flagged SURVIVED or EXPIRED and riskWindowStart == 0, stakers are not entitled to any bonus. Therefore, sweepUnclaimedBonus() considers the entire bonus sweepable.
The function also decrements totalBonus:
However, despite transferring value out of the pool, it intentionally does not set claimsStarted:
This produces the following sequence:
The registry is CORRUPTED, but no pool interaction observed an active-risk state, leaving riskWindowStart == 0.
The moderator initially flags SURVIVED, for example because the breach was initially classified as outside the pool's scope.
Before any staker claim, the moderator determines that the breach was in scope and prepares to correct the outcome to good-faith CORRUPTED.
Any account calls sweepUnclaimedBonus().
The entire bonus is transferred to the sponsor-controlled recoveryAddress, and totalBonus is reduced to zero.
Because the sweep does not set claimsStarted, the moderator's correction still succeeds.
The corrected outcome re-snapshots totalBonus == 0, permanently excluding the swept bonus from bountyEntitlement.
The problem is not merely that the bonus was transferred. The protocol continues to allow a supposedly valid correction after the transfer but calculates the corrected entitlement from accounting that the transfer has already reduced.
The named whitehat permanently loses up to 100% of the bonus component of their bounty.
For example, with 150 tokens of principal and 50 tokens of bonus:
Intended good-faith bounty: 200 tokens
Bonus swept before correction: 50 tokens
Re-snapshotted bounty: 150 tokens
Permanent whitehat loss: 50 tokens
The transferred bonus is sent to recoveryAddress, which is controlled by the pool sponsor. The sponsor can therefore directly benefit from ordering a bonus sweep before the moderator's correction. A third party can also trigger the same loss because the sweep is permissionless.
This undermines the protocol's good-faith CORRUPTED settlement, under which the whitehat is intended to receive the snapshotted principal and bonus.
The issue requires a specific but supported lifecycle:
the registry is terminally CORRUPTED;
riskWindowStart == 0;
the moderator initially flags SURVIVED;
no claim has started;
the moderator later corrects the classification to good-faith CORRUPTED.
Once these conditions occur, exploitation requires only one permissionless call with no capital or special authorization. The transaction can be submitted before or ordered ahead of the moderator's correction.
Because the financial impact is direct but the required lifecycle is conditional, Medium severity is appropriate.
Add the following test to the test suite and run:
Any sweep that transfers an amount represented by the pool's accounted totalBonus should be treated as a value-moving finalization event.
Set claimsStarted when the sweep removes accounted bonus. Pure direct-transfer donations or unrelated dust can remain sweepable without closing the correction window.
This restores the intended invariant that an outcome cannot be re-flagged after accounted value has left the pool.
Alternatively, the protocol can preserve the moderator's correction ability by preventing accounted bonus from being swept while the registry is CORRUPTED and the outcome remains mutable. That approach should include an explicit finalization mechanism or deadline so that legitimate out-of-scope SURVIVED resolutions do not leave bonus funds permanently locked.
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.