From a72ec6b3ef1bd6d838555004650a019b83ee624a Mon Sep 17 00:00:00 2001 From: Ankur Dubey Date: Wed, 11 Oct 2023 10:14:38 +0530 Subject: [PATCH] Implement Security Policy Plugin Management --- .../modules/ISecurityPolicyManagerPlugin.sol | 63 ++++++ .../modules/ISecurityPolicyPlugin.sol | 16 ++ .../modules/SecurityPolicyManagerPlugin.sol | 180 ++++++++++++++++++ 3 files changed, 259 insertions(+) create mode 100644 contracts/smart-account/interfaces/modules/ISecurityPolicyManagerPlugin.sol create mode 100644 contracts/smart-account/interfaces/modules/ISecurityPolicyPlugin.sol create mode 100644 contracts/smart-account/modules/SecurityPolicyManagerPlugin.sol diff --git a/contracts/smart-account/interfaces/modules/ISecurityPolicyManagerPlugin.sol b/contracts/smart-account/interfaces/modules/ISecurityPolicyManagerPlugin.sol new file mode 100644 index 00000000..dbe6d66e --- /dev/null +++ b/contracts/smart-account/interfaces/modules/ISecurityPolicyManagerPlugin.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +import {ISecurityPolicyPlugin} from "./ISecurityPolicyPlugin.sol"; + +address constant SENTINEL_MODULE_ADDRESS = address(0x1); + +/// @title Security Policy Manager Plugin +/// @author @ankurdubey521 +/// @dev Execution Phase Plugin responsible for enforcing security policies during plugin installation on the smart contract wallet +interface ISecurityPolicyManagerPlugin { + event SecurityPolicyEnabled(address indexed scw, address indexed policy); + event SecurityPolicyDisabled(address indexed scw, address indexed policy); + + error SecurityPolicyAlreadyEnabled(address policy); + error SecurityPolicyAlreadyDisabled(address policy); + error InvalidSecurityPolicyAddress(address policy); + error InvalidPointerAddress(address pointer); + error EmptyPolicyList(); + + /// @dev Enables the security policies for the smart contract wallet. Used during the setup process. + /// @param _policy The security policy to be enabled + function enableSecurityPolicy(ISecurityPolicyPlugin _policy) external; + + /// @dev Enables the security policies for the smart contract wallet. Used during the setup process. + /// @param _policies The security policies to be enabled + function enableSecurityPolicies( + ISecurityPolicyPlugin[] calldata _policies + ) external; + + /// @dev Disables the security policy for the smart contract wallet. + /// @param _policy The security policy to be disabled. + /// @param _pointer The address of the security policy preceeding _policy in the list of enabled modules. + function disableSecurityPolicy( + ISecurityPolicyPlugin _policy, + ISecurityPolicyPlugin _pointer + ) external; + + /// @dev Disables the security policies for the smart contract wallet. + /// @param _start The first iterm in the list to be disabled. + /// @param _end The last iterm in the list to be disabled. + /// @param _pointer The address of the security policy preceeding _start in the list of enabled modules. + function disableSecurityPoliciesRange( + ISecurityPolicyPlugin _start, + ISecurityPolicyPlugin _end, + ISecurityPolicyPlugin _pointer + ) external; + + /// @dev Queries the registry and checks if the module is valid and enables the module. + /// @param _setupContract The address of the module contract + /// @param _setupData The data to be passed to the module contract during setup + function checkSetupAndEnableModule( + address _setupContract, + bytes calldata _setupData + ) external returns (address module); + + /// @dev Returns the security policy for the smart contract wallet. + /// @param _scw The address of the smart contract wallet + /// @return SecurityPolicy The security policy of the smart contract wallet + function securityPolicies( + address _scw + ) external view returns (ISecurityPolicyPlugin[] memory); +} diff --git a/contracts/smart-account/interfaces/modules/ISecurityPolicyPlugin.sol b/contracts/smart-account/interfaces/modules/ISecurityPolicyPlugin.sol new file mode 100644 index 00000000..25e9c069 --- /dev/null +++ b/contracts/smart-account/interfaces/modules/ISecurityPolicyPlugin.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +/// @title Security Policy Plugin +/// @author @ankurdubey521 +/// @dev A security policy plugin is a plugin which enforce arbitrary checks and condition during plugin installation on the smart contract wallet +interface ISecurityPolicyPlugin { + /// @dev Validates the security policy for the plugin to be installed, based on the parameters + /// set in the security policy of the smart contract wallet. + /// @param _scw The address of the smart contract wallet + /// @param _plugin The address of the plugin to be installed + function validateSecurityPolicy( + address _scw, + address _plugin + ) external view; +} diff --git a/contracts/smart-account/modules/SecurityPolicyManagerPlugin.sol b/contracts/smart-account/modules/SecurityPolicyManagerPlugin.sol new file mode 100644 index 00000000..ee44261a --- /dev/null +++ b/contracts/smart-account/modules/SecurityPolicyManagerPlugin.sol @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +import {ISecurityPolicyManagerPlugin, ISecurityPolicyPlugin, SENTINEL_MODULE_ADDRESS} from "contracts/smart-account/interfaces/modules/ISecurityPolicyManagerPlugin.sol"; + +contract SecurityPolicyManagerPlugin is ISecurityPolicyManagerPlugin { + mapping(address => mapping(address => address)) + internal enabledSecurityPoliciesLinkedList; + + /// @inheritdoc ISecurityPolicyManagerPlugin + function enableSecurityPolicy( + ISecurityPolicyPlugin _policy + ) external override { + if ( + _policy == ISecurityPolicyPlugin(address(0x0)) || + _policy == ISecurityPolicyPlugin(SENTINEL_MODULE_ADDRESS) + ) { + revert InvalidSecurityPolicyAddress(address(_policy)); + } + + mapping(address => address) + storage enabledSecurityPolicies = enabledSecurityPoliciesLinkedList[ + msg.sender + ]; + + if (enabledSecurityPolicies[address(_policy)] != address(0x0)) { + revert SecurityPolicyAlreadyEnabled(address(_policy)); + } + + enabledSecurityPolicies[address(_policy)] = enabledSecurityPolicies[ + SENTINEL_MODULE_ADDRESS + ]; + enabledSecurityPolicies[SENTINEL_MODULE_ADDRESS] = address(_policy); + + emit SecurityPolicyEnabled(msg.sender, address(_policy)); + } + + /// @inheritdoc ISecurityPolicyManagerPlugin + function enableSecurityPolicies( + ISecurityPolicyPlugin[] calldata _policies + ) external override { + mapping(address => address) + storage enabledSecurityPolicies = enabledSecurityPoliciesLinkedList[ + msg.sender + ]; + + // TODO: Verify if this reduces gas + uint256 length = _policies.length; + + if (length == 0) { + revert EmptyPolicyList(); + } + + address head = enabledSecurityPolicies[SENTINEL_MODULE_ADDRESS]; + + for (uint256 i; i < length; ) { + address policy = address(_policies[i]); + + if (policy == address(0x0) || policy == SENTINEL_MODULE_ADDRESS) { + revert InvalidSecurityPolicyAddress(policy); + } + + if (enabledSecurityPolicies[address(policy)] != address(0x0)) { + revert SecurityPolicyAlreadyEnabled(address(policy)); + } + + enabledSecurityPolicies[policy] = head; + head = policy; + + emit SecurityPolicyEnabled(msg.sender, policy); + + unchecked { + ++i; + } + } + + enabledSecurityPolicies[SENTINEL_MODULE_ADDRESS] = head; + } + + /// @inheritdoc ISecurityPolicyManagerPlugin + function disableSecurityPolicy( + ISecurityPolicyPlugin _policy, + ISecurityPolicyPlugin _pointer + ) external override { + if ( + _policy == ISecurityPolicyPlugin(address(0x0)) || + _policy == ISecurityPolicyPlugin(SENTINEL_MODULE_ADDRESS) + ) { + revert InvalidSecurityPolicyAddress(address(_policy)); + } + + mapping(address => address) + storage enabledSecurityPolicies = enabledSecurityPoliciesLinkedList[ + msg.sender + ]; + + if (enabledSecurityPolicies[address(_policy)] == address(0x0)) { + revert SecurityPolicyAlreadyDisabled(address(_policy)); + } + + if (enabledSecurityPolicies[address(_pointer)] != address(_policy)) { + revert InvalidPointerAddress(address(_pointer)); + } + + enabledSecurityPolicies[address(_pointer)] = enabledSecurityPolicies[ + address(_policy) + ]; + delete enabledSecurityPolicies[address(_policy)]; + + emit SecurityPolicyDisabled(msg.sender, address(_policy)); + } + + /// @inheritdoc ISecurityPolicyManagerPlugin + function disableSecurityPoliciesRange( + ISecurityPolicyPlugin _start, + ISecurityPolicyPlugin _end, + ISecurityPolicyPlugin _pointer + ) external override { + mapping(address => address) + storage enabledSecurityPolicies = enabledSecurityPoliciesLinkedList[ + msg.sender + ]; + + if (enabledSecurityPolicies[address(_pointer)] != address(_start)) { + revert InvalidPointerAddress(address(_pointer)); + } + + if ( + _start == ISecurityPolicyPlugin(address(0x0)) || + _start == ISecurityPolicyPlugin(SENTINEL_MODULE_ADDRESS) + ) { + revert InvalidSecurityPolicyAddress(address(_start)); + } + + if ( + _end == ISecurityPolicyPlugin(address(0x0)) || + _end == ISecurityPolicyPlugin(SENTINEL_MODULE_ADDRESS) + ) { + revert InvalidSecurityPolicyAddress(address(_end)); + } + + enabledSecurityPolicies[address(_pointer)] = enabledSecurityPolicies[ + address(_end) + ]; + + bool endFound = false; + address current = address(_start); + while (current != address(_end) || current != SENTINEL_MODULE_ADDRESS) { + if (current == address(_end)) { + endFound = true; + } + + address next = enabledSecurityPolicies[current]; + delete enabledSecurityPolicies[current]; + + emit SecurityPolicyDisabled(msg.sender, current); + + current = next; + } + + if (!endFound) { + revert InvalidSecurityPolicyAddress(address(_end)); + } + } + + /// @inheritdoc ISecurityPolicyManagerPlugin + function checkSetupAndEnableModule( + address, + bytes calldata + ) external override returns (address) { + revert("Not implemented"); + } + + /// @inheritdoc ISecurityPolicyManagerPlugin + function securityPolicies( + address + ) external view override returns (ISecurityPolicyPlugin[] memory) { + revert("Not implemented"); + } +}