Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: low
Invalid

Missing Function to Cancel an Already Scheduled Emergency Action

TimelockController.sol

Overview

  1. Scheduling an Emergency Action
    The contract provides:

    function scheduleEmergencyAction(bytes32 id) external onlyRole(EMERGENCY_ROLE) {
    _emergencyActions[id] = true;
    emit EmergencyActionScheduled(id, block.timestamp);
    }

    This sets _emergencyActions[id] = true;, marking the operation as “ready to be executed in an emergency.”

  2. Executing the Emergency Action
    The user calls:

    function executeEmergencyAction(
    address[] calldata targets,
    uint256[] calldata values,
    bytes[] calldata calldatas,
    bytes32 predecessor,
    bytes32 salt
    ) external payable onlyRole(EMERGENCY_ROLE) nonReentrant {
    // Check `_emergencyActions[id]`.
    // If true => proceed with immediate calls
    delete _emergencyActions[id];
    ...
    }

    No other function can remove that marker if the protocol decides to “un-schedule” or revert the emergency action.

  3. No Cancellation Mechanism

    • If an address with EMERGENCY_ROLE schedules an action by mistake or changes their mind, no function calls delete _emergencyActions[id] except executeEmergencyAction(...).

    • That means the action remains “live” for indefinite time, enabling any holder of EMERGENCY_ROLE to come in later and call executeEmergencyAction (including new signers or compromised keys) to push potentially destructive changes.

Attack Path / Consequences

  • Mistakenly Queued Emergency Action: If an admin schedules an action in error or prior to final approval, they cannot remove it from _emergencyActions.

  • Compromised or Malicious Role: Another holder of EMERGENCY_ROLE can see the scheduled ID is still set to true and at any time call executeEmergencyAction(...), finalizing changes the protocol intended to drop.

  • Indefinite Window: Because there is no forced expiration or “grace period” for _emergencyActions[id], once set, it stays set unless executed. This defies typical timelock patterns where scheduled actions have a limited window or can be canceled prior to execution.

Recommendation

  1. Add cancelEmergencyAction(...)
    Provide a function restricted by EMERGENCY_ROLE or DEFAULT_ADMIN_ROLE to set _emergencyActions[id] = false;, removing the possibility of future execution:

    function cancelEmergencyAction(bytes32 id) external onlyRole(EMERGENCY_ROLE) {
    if (!_emergencyActions[id]) revert EmergencyActionNotScheduled(id);
    _emergencyActions[id] = false;
    emit EmergencyActionCancelled(id);
    }
  2. Add Expiry
    Alternatively (or in addition), store a timestamp for the emergency action and automatically remove it if not executed within some “emergency grace period.” This ensures the action does not linger forever.

  3. Document the Flow
    Clarify that scheduling an emergency action does not queue it in the normal timelock sense, but that it is an immediate‐execution path. If the design calls for indefinite readiness, state that. If not, implement a well-defined cancellation or expiry.

Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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

Give us feedback!