Skip to content

Security Upgrades to harden Protocol Security#385

Open
pankajjagtapp wants to merge 27 commits intomasterfrom
pankaj/feat/security-upgrades
Open

Security Upgrades to harden Protocol Security#385
pankajjagtapp wants to merge 27 commits intomasterfrom
pankaj/feat/security-upgrades

Conversation

@pankajjagtapp
Copy link
Copy Markdown
Contributor

@pankajjagtapp pankajjagtapp commented Apr 29, 2026

Note

High Risk
High risk because it changes pause semantics and access control across core protocol contracts, and modifies withdrawal/claim paths and reentrancy protections that directly impact fund movement.

Overview
Introduces a new time-bounded pause mechanism via PausableUntil (namespaced storage, max 1 day + per-pauser cooldown) and adds PAUSE_UNTIL_ROLE/UNPAUSE_UNTIL_ROLE to RoleRegistry, wiring pauseContractUntil() / unpauseContractUntil() into multiple contracts and ensuring whenNotPaused checks also block during a timed pause.

Hardens fund-movement surfaces with a new ReentrancyGuardNamespaced (upgrade-safe storage slot) and applies nonReentrant to LiquidityPool deposit/withdraw/request flows and WithdrawRequestNFT claim paths; WithdrawRequestNFT.invalidateRequest is also restricted to non-finalized requests.

Adjusts operational access control: Liquifier migrates from internal admin/pauser mappings to RoleRegistry roles (adds LIQUIFIER_ADMIN_ROLE/LIQUIFIER_SENDER_ROLE and constructor arg), and EtherFiAdmin.executeTasks becomes permissionless once oracle consensus/freshness checks pass. Withdrawal claims are made more resilient during pauses by allowing permissionless claim paths (WithdrawRequestNFT/PriorityWithdrawalQueue) to proceed even when the LiquidityPool is paused, and PriorityWithdrawalQueue claim functions no longer require whenNotPaused.

Reviewed by Cursor Bugbot for commit 6db9c1a. Bugbot is set up for automated code reviews on this repo. Configure here.

Comment thread src/EtherFiAdmin.sol
}

function executeTasks(IEtherFiOracle.OracleReport calldata _report) external {
if (!roleRegistry.hasRole(ETHERFI_ORACLE_EXECUTOR_TASK_MANAGER_ROLE, msg.sender)) revert IncorrectRole();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed access control enables MEV sandwich attacks on rebases

High Severity

Removing the ETHERFI_ORACLE_EXECUTOR_TASK_MANAGER_ROLE check from executeTasks makes it callable by any address. While report data is still consensus-validated, the caller now controls the exact timing of rebases and protocol fee payments. Since _handleAccruedRewards triggers membershipManager.rebase() which changes the eETH exchange rate via liquidityPool.rebase(), a MEV searcher can sandwich the call: buy eETH, call executeTasks to trigger a positive rebase, then sell eETH at the now-higher rate. Previously the role-gated executor made timing manipulation impractical. This contradicts the PR's stated goal of hardening protocol security.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 1b934ff. Configure here.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 29, 2026

📊 Forge Coverage Report

Coverage report could not be generated. Check workflow logs for details.

Generated by workflow run #697

Comment thread src/LiquidityPool.sol
Comment thread src/PriorityWithdrawalQueue.sol
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 6db9c1a. Configure here.

Comment thread src/Liquifier.sol
_;
function _requireNotPaused() internal override view {
_requireNotPausedUntil();
super._requireNotPaused();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Timed pause blocks full pause escalation in OZ-based contracts

Medium Severity

Overriding _requireNotPaused() to call _requireNotPausedUntil() causes OZ's internal _pause() (which uses whenNotPaused) to revert while a timed pause is active. This prevents escalating from a pauseContractUntil to a permanent pauseContract — the PROTOCOL_PAUSER must first call unpauseContractUntil, creating a brief unpaused window. This is inconsistent with LiquidityPool, WithdrawRequestNFT, PriorityWithdrawalQueue, etc., which add the check in the whenNotPaused modifier instead and CAN escalate freely.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6db9c1a. Configure here.

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

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants