Root cause: sweepUnclaimedBonus() moves accounted value out of the pool but intentionally does NOT set claimsStarted (ConfidencePool.sol#L503-507). DESIGN sec4 states the invariant it violates: "Once value has left the contract, a corrective re-flag cannot be honored without breaking balance accounting," with claimsStarted as the "value-movement finality latch." The sweep lets value leave while claimsStarted stays false, so the moderator's pre-claim re-flag window stays open on top of already-moved funds.
Impact: After a legitimate SURVIVED classification of an out-of-scope breach (riskWindowStart == 0), an untrusted griefer front-runs the moderator's correction and calls sweepUnclaimedBonus(), sending the entire accounted bonus to recoveryAddress. When the moderator then legitimately corrects to good-faith CORRUPTED (naming a whitehat), the re-snapshot no longer contains the bonus, so bountyEntitlement is only the principal. The named whitehat is permanently underpaid by the whole bonus. PoC: whitehat should receive 150 (100 principal + 50 bonus) but receives 100 — 50 tokens lost to recoveryAddress.
sweepUnclaimedBonus is callable by anyone once outcome is SURVIVED/EXPIRED (#L474). When riskWindowStart == 0, the reserve excludes the bonus (#L482-488), so the entire snapshotTotalBonus is swept (#L490-508).
The sweep deliberately skips claimsStarted so a 1-wei donation can't block re-flag — but this also leaves the latch open when it moves the real accounted bonus.
flagOutcome (#L327) allows re-flag while !claimsStarted (the documented typo-correction path, sec4). After the sweep, the correction re-snapshots snapshotTotalBonus = totalBonus, which the sweep already zeroed; bountyEntitlement = snapshotTotalStaked + snapshotTotalBonus excludes the bonus. Value left the contract yet a corrective re-flag was honored — exactly what sec4 says must not happen. No trusted actor misbehaves: the moderator's SURVIVED-then-CORRUPTED correction is the documented flow; the untrusted sweep is the unfenced lever.
Likelihood: Medium — needs (1) a SURVIVED call while riskWindowStart == 0 and registry CORRUPTED (a realistic out-of-scope classification the moderator makes then corrects), and (2) an untrusted griefer front-running the permissionless sweepUnclaimedBonus. Both are ordinary, permissionless actions.
Impact: Medium — the named whitehat is deterministically underpaid the entire bonus, redirected to recoveryAddress. A specific honest party's owed funds are permanently misrouted, with no trusted-actor misbehavior.
test/audit/SweepBeforeReflagQ5.t.sol passes, asserting the bug reproduces:
Logs: bonus swept to recovery = 50, whitehat bountyEntitlement = 100, whitehat received = 100.
Latch claimsStarted whenever sweepUnclaimedBonus transfers accounted bonus (when it decrements totalBonus), keeping the no-latch exception only for provable excess/donations:
Alternatively, block sweepUnclaimedBonus from moving accounted bonus until the re-flag window closes.
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.