Skip to content

[A01-4] [MEDIUM] Re-initialization protection (impl + clone) untested #323

@thedavidmeister

Description

@thedavidmeister

Gap

Two re-initialization invariants are unverified:

  1. The implementation contract calls _disableInitializers() in its constructor (src/concrete/Flow.sol:120); calling initialize(bytes) on the implementation directly must revert with the OZ InvalidInitialization selector.
  2. A clone, once successfully initialized, must reject a second initialize(bytes) call.

Neither path has a test.

Source

  • src/concrete/Flow.sol:119-121 (constructor)
  • src/concrete/Flow.sol:130 (initializer modifier)

Existing related tests

  • test/src/concrete/Flow.construction.t.sol::testFlowConstructionInitialize — single-init happy path on a clone.

Proposed test

Add to test/src/concrete/Flow.construction.t.sol:

import {Initializable} from "openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol";
import {Flow} from "src/concrete/Flow.sol";

function testFlowImplementationInitializeReverts(
    address expression,
    bytes memory bytecode,
    uint256[] memory constants
) external {
    expressionDeployerDeployExpression2MockCall(expression, bytes(hex"0007"));

    EvaluableConfigV3[] memory flowConfig = new EvaluableConfigV3[](1);
    flowConfig[0] = EvaluableConfigV3(DEPLOYER, bytecode, constants);

    Flow impl = Flow(deployFlowImplementation());
    // OZ v4 selector for "Initializable: contract is already initialized" is a string
    // revert; for OZ v5+ it is `Initializable.InvalidInitialization()`. Use the
    // appropriate matcher for the OZ version pinned in this repo.
    vm.expectRevert(Initializable.InvalidInitialization.selector);
    impl.initialize(abi.encode(flowConfig));
}

function testFlowCloneInitializeOnceOnly(
    address expression,
    bytes memory bytecode,
    uint256[] memory constants
) external {
    expressionDeployerDeployExpression2MockCall(expression, bytes(hex"0007"));

    EvaluableConfigV3[] memory flowConfig = new EvaluableConfigV3[](1);
    flowConfig[0] = EvaluableConfigV3(DEPLOYER, bytecode, constants);

    address clone = I_CLONE_FACTORY.clone(deployFlowImplementation(), abi.encode(flowConfig));

    vm.expectRevert(Initializable.InvalidInitialization.selector);
    Flow(clone).initialize(abi.encode(flowConfig));
}

If Initializable.InvalidInitialization is not the right selector for the OZ version pinned in this repo, replace with the matching error (e.g. vm.expectRevert(bytes("Initializable: contract is already initialized")) for OZ v4).

Metadata

Metadata

Assignees

No one assigned

    Labels

    auditAudit findingmediumSeverity: mediumpass2Audit Pass 2: Test Coverage

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions