Per line 28, a good-faith CORRUPTED outcome entitles the named attacker to the
entire pool (bountyEntitlement = snapshotTotalStaked + snapshotTotalBonus).
flagOutcome is re-flaggable pre-claim (line 322) so the moderator can correct
a mistaken outcome before distribution — this window is gated on claimsStarted,
not on outcome == UNRESOLVED.
The issue: while riskWindowStart == 0, sweepUnclaimedBonus() reserves only
totalEligibleStake, sweeps the full bonus balance to recoveryAddress, and
deliberately does not set claimsStarted. So if the moderator flags SURVIVED
first (valid even when the live registry reads CORRUPTED), anyone can sweep the
bonus before any claim, then the moderator corrects to good-faith CORRUPTED —
the second flagOutcome snapshots totalBonus after it was already reduced.
claimsStarted protects the distribution, but not the accounting inputs
(totalBonus) feeding the next snapshot — a value-movement event can alter
those inputs inside the window the moderator is meant to still control.
Likelihood:
Occurs whenever a moderator's first flag is SURVIVED on a CORRUPTED registry
(e.g. believing the breach out-of-scope, or flagging before full review) —
a normal, expected moderator workflow, not an edge case.
Permissionless: any address can trigger the sweep once the pool is in a
sweep-eligible outcome with riskWindowStart == 0.
Widens naturally where riskWindowStart was never latched — a scenario the
design doc itself acknowledges as real and accepted (§5).
Impact:
Breaks the documented "entire pool" invariant for good-faith CORRUPTED
(§12) — attacker entitlement is silently reduced by whatever bonus swept.
Swept bonus routes to recoveryAddress permanently, with no recovery path
for the moderator or attacker.
Undermines the reflag window's purpose (line 322): meant to let the
moderator fix the outcome before value moves, but value can move without
tripping the claimsStarted guard meant to be the sole finality signal.
This sequence simulates a pool where the registry reports CORRUPTED but the
moderator's first flag is SURVIVED. Between that flag and the corrective
re-flag, an unprivileged caller sweeps the bonus without setting
claimsStarted. When the moderator then corrects to good-faith CORRUPTED, the
new snapshot reflects the already-reduced totalBonus, so the attacker's
bounty comes up short by exactly the swept amount — demonstrating the
entitlement no longer matches the "entire pool" guarantee.
Alternative: have sweepUnclaimedBonus() set claimsStarted = true whenever
it moves nonzero bonus value out of the pool — treating the sweep as the same
finality-latching event a claim is, consistent with §4's rule that value
movement, not caller identity, closes the correction window.
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.