FoundrySolidityLayer 2
7.25 ETH
Submission Details
Impact: medium
Likelihood: medium

Auto-resolution can permanently override moderator's intended SURVIVED decision after moderator inactivity

Author Revealed upon completion

Root + Impact

Description

  • The protocol intends the moderator to be the authoritative decision-maker for determining whether a corruption event was in scope (CORRUPTED) or out of scope (SURVIVED).

  • However, claimExpired() contains an automatic resolution path that permanently forces the pool into CORRUPTED whenever:

    • the agreement registry reports CORRUPTED,

    • riskWindowStart != 0, and

    • the moderator grace period has elapsed.

if (state == IAttackRegistry.ContractState.CORRUPTED && riskWindowStart != 0) {
if (block.timestamp < expiry + MODERATOR_CORRUPTED_GRACE) {
revert AgreementCorruptedAwaitingModerator();
}
outcome = PoolStates.Outcome.CORRUPTED;
outcomeFlaggedAt = riskWindowEnd;
corruptedReserve = snapshotTotalStaked + snapshotTotalBonus;
}
  • The design itself acknowledges:

    "Scope-blind by design."

    • This means the automatic resolver does not evaluate whether the actual exploit was inside the committed account scope of the pool.

    • Instead, it relies solely on the registry-level corruption state.

    • Consequently, once the moderator becomes unavailable past the grace period, any user can permanently finalize the pool as CORRUPTED, even in situations where the moderator would have classified the incident as SURVIVED because the exploit occurred outside the pool's committed scope.


Risk

Likelihood:

  • Moderator inactivity beyond MODERATOR_CORRUPTED_GRACE.

  • Agreement-level corruption where the exploit is outside this pool's committed scope.

Impact:

  • Honest stakers lose their expected SURVIVED distribution.

  • Entire pool transitions to CORRUPTED settlement despite no in-scope compromise.

Proof of Concept

// Agreement enters registry state CORRUPTED
// Pool scope did NOT include exploited contract.
// Moderator never submits flagOutcome(SURVIVED).
wait(expiry + MODERATOR_CORRUPTED_GRACE);
claimExpired();
// Pool becomes permanently CORRUPTED.

Recommended Mitigation

- remove this code
+ add this code
// Do not allow automatic CORRUPTED settlement
// unless the protocol has sufficient information
// to determine that the corruption affected this
// pool's committed scope.
// Otherwise continue requiring moderator input
// or introduce an alternative authenticated
// scope verification mechanism.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!