Skip to content

fix(SuperchainConfig): extend() must check paused() to prevent re-activating expired pauses - #392

Open
Sertug17 wants to merge 1 commit into
base:mainfrom
Sertug17:fix/superchain-config-extend-expired-pause
Open

fix(SuperchainConfig): extend() must check paused() to prevent re-activating expired pauses#392
Sertug17 wants to merge 1 commit into
base:mainfrom
Sertug17:fix/superchain-config-extend-expired-pause

Conversation

@Sertug17

@Sertug17 Sertug17 commented Aug 1, 2026

Copy link
Copy Markdown

Fixes #391

Problem

extend() checked pauseTimestamps[_identifier] == 0 to detect "not paused", but this only catches the never-paused case. An expired pause has pauseTimestamps != 0 while paused() returns false, so it silently passed the check and got its timestamp reset — effectively re-pausing the system without the required unpause() → pause() cycle.

This bypasses the Stage 1 Decentralization requirement explicitly documented in pause():

"this check intentionally prevents re-pausing even after a pause has expired... This is a Stage 1 Decentralization requirement"

Fix

- if (pauseTimestamps[_identifier] == 0) {
+ if (!paused(_identifier)) {

paused() returns false for both never-paused (timestamp == 0) and expired (timestamp != 0 but past PAUSE_EXPIRY), making it the correct guard.

Missing test (to be added)

function test_extend_expiredPause_reverts() external {
    _pauseAsGuardian(address(this));
    vm.warp(block.timestamp + PAUSE_EXPIRY + 1);
    assertFalse(superchainConfig.paused(address(this)));

    vm.prank(superchainConfig.guardian());
    vm.expectRevert(
        abi.encodeWithSelector(
            ISuperchainConfig.SuperchainConfig_NotAlreadyPaused.selector,
            address(this)
        )
    );
    superchainConfig.extend(address(this));
}

…ctivating expired pauses

extend() was checking pauseTimestamps[id] == 0 which only catches the
never-paused case. An expired pause (timestamp != 0, paused() == false)
bypassed this check, allowing the guardian to re-activate it without the
required unpause -> pause cycle (Stage 1 Decentralization requirement).

Fix: use paused() which returns false for both unpaused and expired.

Fixes base#391
@cb-heimdall

Copy link
Copy Markdown
Collaborator

🟡 Heimdall Review Status

Requirement Status More Info
Reviews 🟡 0/1
Denominator calculation
Show calculation
1 if user is bot 0
1 if user is external 0
2 if repo is sensitive 0
From .codeflow.yml 1
Additional review requirements
Show calculation
Max 0
0
From CODEOWNERS 0
Global minimum 0
Max 1
1
1 if commit is unverified 0
Sum 1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(SuperchainConfig): extend() can re-activate an expired pause, bypassing Stage 1 requirement

2 participants