The same signature can be used in different `distribution` implementation causing that the caller who owns the signature, can distribute on unauthorized implementations
The same signature can be used in different `distribute` implementations causing that the caller who owns the signature, to `distribute` on unauthorized implementations.\\nThe ProxyFactory::setContest() function helps to configure a `closeTime` to specific `organizer`, `contestId` and `implementation`.\\n```\\nFile: ProxyFactory.sol\\n    function setContest(address organizer, bytes32 contestId, uint256 closeTime, address implementation)\\n        public\\n        onlyOwner\\n// rest of code\\n// rest of code\\n        bytes32 salt = _calculateSalt(organizer, contestId, implementation);\\n        if (saltToCloseTime[salt] != 0) revert ProxyFactory__ContestIsAlreadyRegistered();\\n        saltToCloseTime[salt] = closeTime;\\n```\\n\\nThe caller who owns the signature, can distributes to winners using the deployProxyAndDistributeBySignature() function. The problem is that the hash in the code line (#159) does not consider the `implementation` parameter.\\n```\\nFile: ProxyFactory.sol\\n    function deployProxyAndDistributeBySignature(\\n        address organizer,\\n        bytes32 contestId,\\n        address implementation,\\n        bytes calldata signature,\\n        bytes calldata data\\n    ) public returns (address) {\\n        bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(contestId, data)));\\n        if (ECDSA.recover(digest, signature) != organizer) revert ProxyFactory__InvalidSignature();\\n        bytes32 salt = _calculateSalt(organizer, contestId, implementation);\\n        if (saltToCloseTime[salt] == 0) revert ProxyFactory__ContestIsNotRegistered();\\n        if (saltToCloseTime[salt] > block.timestamp) revert ProxyFactory__ContestIsNotClosed();\\n        address proxy = _deployProxy(organizer, contestId, implementation);\\n        _distribute(proxy, data);\\n        return proxy;\\n    }\\n```\\n\\nFor some reason, there could be a different `distribution` implementation to the same `contestId`. Then the caller who owns the signature can distribute even if the organizer does not authorize a signature to the new implementation.\\nI created a test where the caller who owns a signature can distribute to new `distribute implementation` using the same signature. Test steps:\\nOwner setContest using the implementation `address(distributor)`\\nOrganizer creates a signature.\\nCaller distributes prizes using the signature.\\nFor some reason there is a new distributor implementation. The Owner set the new distributor for the same `contestId`.\\nThe caller can distribute prizes using the same signature created in the step 2 in different distributor implementation.\\n```\\n// test/integration/ProxyFactoryTest.t.sol:ProxyFactoryTest\\n// $ forge test --match-test "testSignatureCanBeUsedToNewImplementation" -vvv\\n//\\n    function testSignatureCanBeUsedToNewImplementation() public {\\n        address organizer = TEST_SIGNER;\\n        bytes32 contestId = keccak256(abi.encode("Jason", "001"));\\n        //\\n        // 1. Owner setContest using address(distributor)\\n        vm.startPrank(factoryAdmin);\\n        proxyFactory.setContest(organizer, contestId, block.timestamp + 8 days, address(distributor));\\n        vm.stopPrank();\\n        bytes32 salt = keccak256(abi.encode(organizer, contestId, address(distributor)));\\n        address proxyAddress = proxyFactory.getProxyAddress(salt, address(distributor));\\n        vm.startPrank(sponsor);\\n        MockERC20(jpycv2Address).transfer(proxyAddress, 10000 ether);\\n        vm.stopPrank();\\n        assertEq(MockERC20(jpycv2Address).balanceOf(proxyAddress), 10000 ether);\\n        // before\\n        assertEq(MockERC20(jpycv2Address).balanceOf(user1), 0 ether);\\n        assertEq(MockERC20(jpycv2Address).balanceOf(stadiumAddress), 0 ether);\\n        //\\n        // 2. Organizer creates a signature\\n        (bytes32 digest, bytes memory sendingData, bytes memory signature) = createSignatureByASigner(TEST_SIGNER_KEY);\\n        assertEq(ECDSA.recover(digest, signature), TEST_SIGNER);\\n        vm.warp(8.01 days);\\n        //\\n        // 3. Caller distributes prizes using the signature\\n        proxyFactory.deployProxyAndDistributeBySignature(\\n            TEST_SIGNER, contestId, address(distributor), signature, sendingData\\n        );\\n        // after\\n        assertEq(MockERC20(jpycv2Address).balanceOf(user1), 9500 ether);\\n        assertEq(MockERC20(jpycv2Address).balanceOf(stadiumAddress), 500 ether);\\n        //\\n        // 4. For some reason there is a new distributor implementation.\\n        // The Owner set the new distributor for the same contestId\\n        Distributor new_distributor = new Distributor(address(proxyFactory), stadiumAddress);\\n        vm.startPrank(factoryAdmin);\\n        proxyFactory.setContest(organizer, contestId, block.timestamp + 8 days, address(new_distributor));\\n        vm.stopPrank();\\n        bytes32 newDistributorSalt = keccak256(abi.encode(organizer, contestId, address(new_distributor)));\\n        address proxyNewDistributorAddress = proxyFactory.getProxyAddress(newDistributorSalt, address(new_distributor));\\n        vm.startPrank(sponsor);\\n        MockERC20(jpycv2Address).transfer(proxyNewDistributorAddress, 10000 ether);\\n        vm.stopPrank();\\n        //\\n        // 5. The caller can distribute prizes using the same signature in different distributor implementation\\n        vm.warp(20 days);\\n        proxyFactory.deployProxyAndDistributeBySignature(\\n            TEST_SIGNER, contestId, address(new_distributor), signature, sendingData\\n        );\\n    }\\n```\\n
Include the `distribution implementation` in the signature hash.\\n```\\n    function deployProxyAndDistributeBySignature(\\n        address organizer,\\n        bytes32 contestId,\\n        address implementation,\\n        bytes calldata signature,\\n        bytes calldata data\\n    ) public returns (address) {\\n// Remove the line below\\n// Remove the line below\\n      bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(contestId, data)));\\n// Add the line below\\n// Add the line below\\n      bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(contestId, implementation, data)));\\n```\\n
The caller who owns the signature, can distribute the prizes for a new distribution implementation using the same signature which was created for an old implementation. The `organizer` must create a new signature if there is a new implementation for the same `contestId`. The authorized signature is for one distribution implementation not for the future distribution implementations.\\nTools used\\nManual review
```\\nFile: ProxyFactory.sol\\n    function setContest(address organizer, bytes32 contestId, uint256 closeTime, address implementation)\\n        public\\n        onlyOwner\\n// rest of code\\n// rest of code\\n        bytes32 salt = _calculateSalt(organizer, contestId, implementation);\\n        if (saltToCloseTime[salt] != 0) revert ProxyFactory__ContestIsAlreadyRegistered();\\n        saltToCloseTime[salt] = closeTime;\\n```\\n