Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: [spearbit-15] IPlugin function prevention in installing execution functions #17

Merged
merged 2 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/account/PluginManagerInternals.sol
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 {
error Erc4337FunctionNotAllowed(bytes4 selector);
error ExecutionFunctionAlreadySet(bytes4 selector);
error ExecutionFunctionNotSet(bytes4 selector);
error IPluginFunctionNotAllowed(bytes4 selector);
error InvalidDependenciesProvided();
error InvalidPluginManifest();
error MissingPluginDependency(address dependency);
Expand All @@ -75,11 +76,17 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 {
revert ExecutionFunctionAlreadySet(selector);
}

// make sure incoming execution function does not collide with any native functions (data are stored on the
// Make sure incoming execution function does not collide with any native functions (data are stored on the
// account implementation contract)
if (KnownSelectors.isNativeFunction(selector)) {
revert NativeFunctionNotAllowed(selector);
}

// Make sure incoming execution function is not any IPlugin functions
fangting-alchemy marked this conversation as resolved.
Show resolved Hide resolved
if (KnownSelectors.isIPluginFunction(selector)) {
revert IPluginFunctionNotAllowed(selector);
}

// Also make sure it doesn't collide with functions defined by ERC-4337
// and called by the entry point. This prevents a malicious plugin from
// sneaking in a function with the same selector as e.g.
Expand Down
12 changes: 12 additions & 0 deletions src/helpers/KnownSelectors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {IAccountLoupe} from "../../src/interfaces/IAccountLoupe.sol";
import {IAccountView} from "../../src/interfaces/IAccountView.sol";
import {IAggregator} from "../../src/interfaces/erc4337/IAggregator.sol";
import {IPaymaster} from "../../src/interfaces/erc4337/IPaymaster.sol";
import {IPlugin} from "../interfaces/IPlugin.sol";
import {IPluginExecutor} from "../interfaces/IPluginExecutor.sol";
import {IPluginManager} from "../../src/interfaces/IPluginManager.sol";
import {IStandardExecutor} from "../interfaces/IStandardExecutor.sol";
Expand Down Expand Up @@ -53,4 +54,15 @@ library KnownSelectors {
|| selector == IAggregator.aggregateSignatures.selector
|| selector == IPaymaster.validatePaymasterUserOp.selector || selector == IPaymaster.postOp.selector;
}

function isIPluginFunction(bytes4 selector) internal pure returns (bool) {
return selector == IPlugin.onInstall.selector || selector == IPlugin.onUninstall.selector
|| selector == IPlugin.preUserOpValidationHook.selector
|| selector == IPlugin.userOpValidationFunction.selector
|| selector == IPlugin.preRuntimeValidationHook.selector
|| selector == IPlugin.runtimeValidationFunction.selector || selector == IPlugin.preExecutionHook.selector
|| selector == IPlugin.postExecutionHook.selector || selector == IPlugin.onHookApply.selector
|| selector == IPlugin.onHookUnapply.selector || selector == IPlugin.pluginManifest.selector
|| selector == IPlugin.pluginMetadata.selector;
}
}
23 changes: 23 additions & 0 deletions test/account/UpgradeableModularAccountPluginManager.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,29 @@ contract UpgradeableModularAccountPluginManagerTest is Test {
});
}

function test_installPlugin_failWithIPluginFunctionSelector() public {
vm.startPrank(owner2);

PluginManifest memory manifestBad;
manifestBad.executionFunctions = new bytes4[](1);
manifestBad.executionFunctions[0] = IPlugin.onInstall.selector;
MockPlugin mockPluginBad = new MockPlugin(manifestBad);
bytes32 manifestHashBad = keccak256(abi.encode(mockPluginBad.pluginManifest()));

vm.expectRevert(
abi.encodeWithSelector(
PluginManagerInternals.IPluginFunctionNotAllowed.selector, IPlugin.onInstall.selector
)
);
IPluginManager(account2).installPlugin({
plugin: address(mockPluginBad),
manifestHash: manifestHashBad,
pluginInitData: bytes(""),
dependencies: new FunctionReference[](0),
injectedHooks: new IPluginManager.InjectedHook[](0)
});
}

function test_installPlugin_failWtihErc4337FunctionSelector() public {
vm.startPrank(owner2);

Expand Down
20 changes: 19 additions & 1 deletion test/helpers/KnownSelectors.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ import {IPaymaster} from "@eth-infinitism/account-abstraction/interfaces/IPaymas
import {KnownSelectors} from "../../src/helpers/KnownSelectors.sol";
import {IAccountLoupe} from "../../src/interfaces/IAccountLoupe.sol";
import {IAccountInitializable} from "../../src/interfaces/IAccountInitializable.sol";
import {IStandardExecutor} from "../../src/interfaces/IStandardExecutor.sol";
import {IPlugin} from "../../src/interfaces/IPlugin.sol";
import {IPluginExecutor} from "../../src/interfaces/IPluginExecutor.sol";
import {IPluginManager} from "../../src/interfaces/IPluginManager.sol";
import {IStandardExecutor} from "../../src/interfaces/IStandardExecutor.sol";

contract KnownSelectorsTest is Test {
function test_isNativeFunction() public {
Expand Down Expand Up @@ -64,4 +65,21 @@ contract KnownSelectorsTest is Test {

assertFalse(KnownSelectors.isErc4337Function(BaseAccount.validateUserOp.selector));
}

function test_isIPluginFunction() public {
assertTrue(KnownSelectors.isIPluginFunction(IPlugin.onInstall.selector));
assertTrue(KnownSelectors.isIPluginFunction(IPlugin.onUninstall.selector));
assertTrue(KnownSelectors.isIPluginFunction(IPlugin.preUserOpValidationHook.selector));
assertTrue(KnownSelectors.isIPluginFunction(IPlugin.userOpValidationFunction.selector));
assertTrue(KnownSelectors.isIPluginFunction(IPlugin.preRuntimeValidationHook.selector));
assertTrue(KnownSelectors.isIPluginFunction(IPlugin.runtimeValidationFunction.selector));
assertTrue(KnownSelectors.isIPluginFunction(IPlugin.preExecutionHook.selector));
assertTrue(KnownSelectors.isIPluginFunction(IPlugin.postExecutionHook.selector));
assertTrue(KnownSelectors.isIPluginFunction(IPlugin.onHookApply.selector));
assertTrue(KnownSelectors.isIPluginFunction(IPlugin.onHookUnapply.selector));
assertTrue(KnownSelectors.isIPluginFunction(IPlugin.pluginManifest.selector));
assertTrue(KnownSelectors.isIPluginFunction(IPlugin.pluginMetadata.selector));

assertFalse(KnownSelectors.isIPluginFunction(IPaymaster.postOp.selector));
}
}