MEDIUM severity: Gas griefing via duplicate entries in `Allowed` class of enforcers. **Description:** Multiple enforcer contracts (`AllowedMethodsEnforcer` and `AllowedTargetsEnforcer`) don't validate the uniqueness of entries in their terms data, allowing malicious users to intentionally create delegations with excessive duplicates, dramatically increasing gas costs during...

Vulnerable code:
function getTermsInfo(bytes calldata _terms) public pure returns (bytes4[] memory allowedMethods_) {
    uint256 j = 0;
    uint256 termsLength_ = _terms.length;
    require(termsLength_ % 4 == 0, "AllowedMethodsEnforcer:invalid-terms-length");
    allowedMethods_ = new bytes4[](termsLength_ / 4);
    for (uint256 i = 0; i < termsLength_; i += 4) {
        allowedMethods_[j] = bytes4(_terms[i:i + 4]);
        j++;
    }
}

// In beforeHook:
for (uint256 i = 0; i < allowedSignaturesLength_; ++i) {
    if (targetSig_ == allowedSignatures_[i]) { //@audit linear search can be expensive
        return;
    }
}

function test_AllowedMethods_DuplicateMethodsGriefing() public {
        // Create terms with a high number of duplicated methods to increase gas costs
        bytes memory terms = createDuplicateMethodsTerms(INCREMENT_SELECTOR, 100);

        // Create execution to increment counter
        Execution memory execution =
            Execution({ target: address(aliceCounter), value: 0, callData: abi.encodeWithSelector(INCREMENT_SELECTOR) });

        // Create delegation with allowed methods caveat
        Caveat[] memory caveats = new Caveat[](1);
        caveats[0] = Caveat({ enforcer: address(allowedMethodsEnforcer), terms: terms, args: "" });

        // Create and sign the delegation
        Delegation memory delegation = Delegation({
            delegate: address(users.bob.deleGator),
            delegator: address(users.alice.deleGator),
            authority: ROOT_AUTHORITY,
            caveats: caveats,
            salt: 0,
            signature: hex""
        });

        delegation = signDelegation(users.alice, delegation);

        // Measure gas usage with many duplicate methods
        Delegation[] memory delegations = new Delegation[](1);
        delegations[0] = delegation;

        uint256 gasUsed = uint256(
            bytes32(
                gasReporter.measureGas(
                    address(users.bob.deleGator),
                    address(delegationManager),
                    abi.encodeWithSelector(
                        delegationManager.redeemDelegations.selector,
                        createPermissionContexts(delegation),
                        createModes(),
                        createExecutionCallDatas(execution)
                    )
                )
            )
        );

        console.log("Gas used with 100 duplicate methods:", gasUsed);

        // Now compare to normal case with just one method
        terms = abi.encodePacked(INCREMENT_SELECTOR);
        caveats[0].terms = terms;

        delegation.caveats = caveats;
        delegation = signDelegation(users.alice, delegation);

        delegations[0] = delegation;

        uint256 gasUsedNormal = uint256(
            bytes32(
                gasReporter.measureGas(
                    address(users.bob.deleGator),
                    address(delegationManager),
                    abi.encodeWithSelector(
                        delegationManager.redeemDelegations.selector,
                        createPermissionContexts(delegation),
                        createModes(),
                        createExecutionCallDatas(execution)
                    )
                )
            )
        );

        console.log("Gas used with 1 method:", gasUsedNormal);
        console.log("Gas diff:", gasUsed - gasUsedNormal);

        assertGt(gasUsed, gasUsedNormal, "Griefing with duplicate methods should use more gas");
    }

    function createDuplicateMethodsTerms(bytes4 selector, uint256 count) internal pure returns (bytes memory) {
        bytes memory terms = new bytes(count * 4);
        for (uint256 i = 0; i < count; i++) {
            bytes4 methodSig = selector;
            for (uint256 j = 0; j < 4; j++) {
                terms[i * 4 + j] = methodSig[j];
            }
        }
        return terms;
    }

    function createPermissionContexts(Delegation memory del) internal pure returns (bytes[] memory) {
        Delegation[] memory delegations = new Delegation[](1);
        delegations[0] = del;

        bytes[] memory permissionContexts = new bytes[](1);
        permissionContexts[0] = abi.encode(delegations);

        return permissionContexts;
    }

    function createExecutionCallDatas(Execution memory execution) internal pure returns (bytes[] memory) {
        bytes[] memory executionCallDatas = new bytes[](1);
        executionCallDatas[0] = ExecutionLib.encodeSingle(execution.target, execution.value, execution.callData);
        return executionCallDatas;
    }

    function createModes() internal view returns (ModeCode[] memory) {
        ModeCode[] memory modes = new ModeCode[](1);
        modes[0] = mode;
        return modes;
    }

function getTermsInfo(bytes calldata _terms) public pure returns (bytes4[] memory allowedMethods_) {
    uint256 termsLength_ = _terms.length;
    require(termsLength_ % 4 == 0, "AllowedMethodsEnforcer:invalid-terms-length");
    allowedMethods_ = new bytes4[](termsLength_ / 4);

    bytes4 previousSelector = bytes4(0);

    for (uint256 i = 0; i < termsLength_; i += 4) {
        bytes4 currentSelector = bytes4(_terms[i:i + 4]);

        // Ensure selectors are strictly increasing (prevents duplicates)
        require(uint32(currentSelector) > uint32(previousSelector),
                "AllowedMethodsEnforcer:selectors-must-be-strictly-increasing"); //@audit prevents duplicates

        allowedMethods_[i/4] = currentSelector;
        previousSelector = currentSelector;
    }
}