xanoutas/solidity-security-findings huggingface dataset:
Non-EVM chains are supported on InterchainTokenService but aren't supported on InterchainProposalExecutor
Audit: 2023-07-axelar-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/interchain-token-service/InterchainTokenService.sol#L502-L523
https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/interchain-token-service/InterchainTokenService.sol#L599-L615
https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/interchain-governance-executor/InterchainProposalExecutor.sol#L41-L67


# Vulnerability details

## Impact

Non-EVM chain is supported on InterchainTokenService but doesn't support on InterchainProposalExecutor

The word "Interchain" means it should support Cosmos because it's a Cosmos terms. Google "Interchain" for proof.

Axelar isn't supporting only EVM but also non-EVM especially Cosmos which is their focus. So, I think this should be in scope.

If InterchainProposalExecutor doesn't support Cosmos, Cosmos governance modules such as the Axelar chain itself can't execute proposals on EVM chains.

## Proof of Concept

InterchainTokenService support non-EVM wallet addresses for both send and receive as it uses bytes as destinationAddress.

```solidity
    function transmitSendToken(
        bytes32 tokenId,
        address sourceAddress,
        string calldata destinationChain,
        bytes memory destinationAddress,
        uint256 amount,
        bytes calldata metadata
    ) external payable onlyTokenManager(tokenId) notPaused {
        bytes memory payload;
        if (metadata.length < 4) {
            payload = abi.encode(SELECTOR_SEND_TOKEN, tokenId, destinationAddress, amount);
            _callContract(destinationChain, payload, msg.value, sourceAddress);
            emit TokenSent(tokenId, destinationChain, destinationAddress, amount);
            return;
        }
        uint32 version;
        (version, metadata) = _decodeMetadata(metadata);
        if (version > 0) revert InvalidMetadataVersion(version);
        payload = abi.encode(SELECTOR_SEND_TOKEN_WITH_DATA, tokenId, destinationAddress, amount, sourceAddress.toBytes(), metadata);
        _callContract(destinationChain, payload, msg.value, sourceAddress);
        emit TokenSentWithData(tokenId, destinationChain, destinationAddress, amount, sourceAddress, metadata);
    }
```

```solidity
    function _processSendTokenPayload(string calldata sourceChain, bytes calldata payload) internal {
        (, bytes32 tokenId, bytes memory destinationAddressBytes, uint256 amount) = abi.decode(payload, (uint256, bytes32, bytes, uint256));
        bytes32 commandId;


        assembly {
            commandId := calldataload(4)
        }
        address destinationAddress = destinationAddressBytes.toAddress();
        ITokenManager tokenManager = ITokenManager(getValidTokenManagerAddress(tokenId));
        address expressCaller = _popExpressReceiveToken(tokenId, destinationAddress, amount, commandId);
        if (expressCaller == address(0)) {
expressReceiveToken function on InterchainTokenService cannot be paused, express caller may lose funds when the transaction is settled after token service is paused
Audit: 2023-07-axelar-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/interchain-token-service/InterchainTokenService.sol#L439-L453


# Vulnerability details

## Impact
In InterchainTokenService contract, a caller can call `expressReceiveToken()` to send tokens directly to destination address, with the expectation that the tokens will be reimbursed after the request is executed through `execute()`.

## Proof of Concept
However, `execute()` can be paused by design in InterchainTokenService through `notPaused` modifier on `_execute()`. And there is no pausing mechanism for `expressReceiveToken()`.

This might allow a caller to lose funds if their transaction is settled after the token service is paused. This is an edge case where a caller can initiate `expressReceiveToken()` before the token service is paused. Under the current implementation, this edge case cannot be prevented.

```solidity
//InterchainTokenService.sol
    function expressReceiveToken(
        bytes32 tokenId,
        address destinationAddress,
        uint256 amount,
        bytes32 commandId
    ) external {
        if (gateway.isCommandExecuted(commandId)) revert AlreadyExecuted(commandId);

        address caller = msg.sender;
        ITokenManager tokenManager = ITokenManager(getValidTokenManagerAddress(tokenId));
        IERC20 token = IERC20(tokenManager.tokenAddress());

        SafeTokenTransferFrom.safeTransferFrom(token, caller, destinationAddress, amount);

        _setExpressReceiveToken(tokenId, destinationAddress, amount, commandId, caller);
    }
```
(https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/interchain-token-service/InterchainTokenService.sol#L439-L453)

```solidity
//InterchainTokenService.sol
//@audit-info Note that _execute() can be paused but expressReceiveToken() cannot be paused.
    function _execute(
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes calldata payload
    ) internal override onlyRemoteService(sourceChain, sourceAddress) notPaused {
...
```
I think this is medium in severity because of the edge case where a caller might lose funds against their will through normal interactions with the contract.

## Tools Used
Manual review

## Recommended Mitigation Steps
Allow `expressReceiveToken()` to be paused before the token interchain service pause, which requires a different modifier for `expressReceiveToken()` such that the execution of pause would allow some delay to ensure all express receive token requests can be settled before the token interchain service pause.



## Assessed type

Timing
_scheduleTimeLock should revert when eta < minimumEta
Audit: 2023-07-axelar-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/gmp-sdk/util/TimeLock.sol#L56


# Vulnerability details

## Impact
Voters of a proposal may suffer from financial loss because eta becomes bigger.

## Proof of Concept
In TimeLock.sol:_scheduleTimeLock(), we increase the eta passed into minimumEta if eta < minimumEta, and then set the timelock:

```solidity
    function _scheduleTimeLock(bytes32 hash, uint256 eta) internal returns (uint256) {
        if (hash == 0) revert InvalidTimeLockHash();
        if (_getTimeLockEta(hash) != 0) revert TimeLockAlreadyScheduled();

        uint256 minimumEta = block.timestamp + _minimumTimeLockDelay;

        if (eta < minimumEta) eta = minimumEta;

        _setTimeLockEta(hash, eta);

        return eta;
    }
```

If Voters propose a proposal with eta less than minimumEta and pass other checks, this proposal will be set with eta bigger than the voters set first. This can lead to financial loss in some cases.

## Tools Used

## Recommended Mitigation Steps
We should revert instead of increasing the eta if it's less than minimumEta.



## Assessed type

Context
InitProxy and Proxy should be abstract or override contractId
Audit: 2023-07-axelar-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/gmp-sdk/upgradable/InitProxy.sol#L50
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/gmp-sdk/upgradable/InitProxy.sol#L50


# Vulnerability details

## Impact
The contractId() check can be bypassed if someone deploys and use InitProxy/Proxy directly or if someone inherited InitProxy/Proxy without overriding contractId().

## Proof of Concept
In InitProxy/Proxy, the contractId() check will be bypassed because BaseProxy.contractId() will return zero:

```solidity
        bytes32 id = contractId();
        if (id != bytes32(0) && IUpgradable(implementationAddress).contractId() != id) revert InvalidImplementation();
```

Although there is no code in Axelar that uses these two contracts directly or inherited them without overriding contractId(), I think it's important to avoid things like that happening since InitProxy/Proxy are not abstract and contractId() is meant to be overridden, also users may get there assets lost if they falsely used a wrong implementation.

## Tools Used
Manual Review.

## Recommended Mitigation Steps
InitProxy and Proxy should be abstract or override contractId() with unique bytes.



## Assessed type

Upgradable
Case sensitive wallet address chains are not supported by InterchainTokenService
Audit: 2023-07-axelar-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/remote-address-validator/RemoteAddressValidator.sol#L69-L89
https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/interchain-token-service/InterchainTokenService.sol#L707-L724
https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/remote-address-validator/RemoteAddressValidator.sol#L133-L138


# Vulnerability details

## Impact

Case sensitive wallet address chains are not supported by InterchainTokenService

For instance, both P2PKH and P2SH wallet address formats are case-sensitive, so you have to make sure that you're using uppercase and lowercase letters where appropriate. An example of a case-sensitive address is 1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2.

It will cause fund loss on bridging to that chain since it will always send messages to the contract address in lowercase format. But since that chain is case sensitive, the contract address in lowercase format is invalid and not found, so the token can't be unlocked on the destination chain and lost forever!

Since Axelar plans to support more chains in the future, there may be some chain whose address is case sensitive

## Proof of Concept

RemoteAddressValidator validateSender and addTrustedAddress convert address to lowercase before storing or comparing. This limits the support to only case-insensitive chain

```solidity
    function validateSender(string calldata sourceChain, string calldata sourceAddress) external view returns (bool) {
        string memory sourceAddressLC = _lowerCase(sourceAddress);
        bytes32 sourceAddressHash = keccak256(bytes(sourceAddressLC));
        if (sourceAddressHash == interchainTokenServiceAddressHash) {
            return true;
        }
        return sourceAddressHash == remoteAddressHashes[sourceChain];
    }

    function addTrustedAddress(string memory chain, string memory addr) public onlyOwner {
        if (bytes(chain).length == 0) revert ZeroStringLength();
        if (bytes(addr).length == 0) revert ZeroStringLength();
        remoteAddressHashes[chain] = keccak256(bytes(_lowerCase(addr)));
        remoteAddresses[chain] = addr;
        emit TrustedAddressAdded(chain, addr);
    }

    function getRemoteAddress(string calldata chainName) external view returns (string memory remoteAddress) {
        remoteAddress = remoteAddresses[chainName];
        if (bytes(remoteAddress).length == 0) {
            remoteAddress = interchainTokenServiceAddress.toString();
        }
    }
```

On token bridging, it will send message to AxelarGateway with lower case destination address due to `string memory destinationAddress = remoteAddressValidator.getRemoteAddress(destinationChain);` and `remoteAddressValidator.getRemoteAddress` returns lowercase address

```solidity
    function _callContract(
        string calldata destinationChain,
_getProposalHash should add a nonce to prevent hash collision DoS
Audit: 2023-07-axelar-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/governance/InterchainGovernance.sol#L148
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/gmp-sdk/util/TimeLock.sol#L52


# Vulnerability details

## Impact
Proposals can't ve scheduled because of hash collision.

## Proof of Concept

We calculated the proposal hash only using target, callData, and value transferred:

```solidity
    function _getProposalHash(
        address target,
        bytes memory callData,
        uint256 nativeValue
    ) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(target, callData, nativeValue));
    }
```

When scheduling a proposal, timelock will revert if there exists a proposal with the same hash:

```solidity
    function _scheduleTimeLock(bytes32 hash, uint256 eta) internal returns (uint256) {
        if (hash == 0) revert InvalidTimeLockHash();
        if (_getTimeLockEta(hash) != 0) revert TimeLockAlreadyScheduled();

        uint256 minimumEta = block.timestamp + _minimumTimeLockDelay;

        if (eta < minimumEta) eta = minimumEta;

        _setTimeLockEta(hash, eta);

        return eta;
    }
```

So if there is a service that requires the same data/value for all users, we can't propose two proposals if the first one is not executed yet, which may bring Economic loss to users. Or if an attacker can predict (or front-run) a user's proposal and he manages to propose a proposal with the same hash, the victim will suffer DoS.

## Tools Used
Manual Review.

## Recommended Mitigation Steps
Add a nonce for every proposal's hash computing.



## Assessed type

DoS
External calls in a loop with a possibility of DoS.
Audit: 2023-07-axelar-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/interchain-governance-executor/InterchainProposalExecutor.sol#L73-L84


# Vulnerability details

## Impact
The whole loop may revert with out of gas exception if the number of calls is too large.
It causes the whole loop to revert and results in the loss of enormous amount of gas.

## Recommended Mitigation Steps
To mitigate this, iterate in batches as per the gas required per call and the total block gas limit and be absolutely sure that it will never revert with out of gas exception.


## Assessed type

DoS
One-Step Operator Change Risks Loss of Control Over Contracts
Audit: 2023-07-axelar-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/utils/Operatable.sol#L46-L53


# Vulnerability details

## Impact
The operator can set a new operator, however, such an operation does not require
a confirmation. Incorrect use of such functionality can lead to losing control over contracts, which could not be undone.

## Proof of Concept

```solidity
  /**
     * @notice Change the operator of the contract
     * @dev Can only be called by the current operator
     * @param operator_ The address of the new operator
     */
    function setOperator(address operator_) external onlyOperator {
        _setOperator(operator_);
    }
```


## Tools Used
Manual

## Recommended Mitigation Steps

It is recommended to split the operator transfer functionality in two steps;  
operator setter and acceptOperator functions. The latter function allows the transfer to be completed by the recipient.


## Assessed type

Access Control
LACK OF INPUT VALIDATION FOR THE `minimumTimeDelay ` PARAMETER COULD BREAK THE FUNCTIONALITY OF THE ` TimeLock.sol` CONTRACT
Audit: 2023-07-axelar-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/governance/InterchainGovernance.sol#L33-L38
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/gmp-sdk/util/TimeLock.sol#L21-L23
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/gmp-sdk/util/TimeLock.sol#L54


# Vulnerability details

## Impact

In the `TimeLock.sol` contract `constructor`, there is no input validation for the `minimumTimeDelay` input parameter. The `TimeLock.constructor` is shown below:

    constructor(uint256 minimumTimeDelay) {
        _minimumTimeLockDelay = minimumTimeDelay; //@audit-issue - no input validation on the minimumTimeDelay
    } 

As seen above the `immutable` variable `_minimumTimeLockDelay` is set to `minimumTimeDelay` input parameter when the `TimeLock` contract is deployed. Since the `TimeLock` is inherited by the `InterchainGovernance` contract, when the `InterchainGovernance` is deployed the `TimeLock` contract is also deployed. But there is no input validation check for the `minimumTimeDelay`, hence if the `minimumTimeDelay` is set to zero this will break the intended purpose of the `TimeLock` contract (which is to execute proposals after the TimeLock period has passed).

The `minimumTimeDelay` is used inside the `TimeLock._scheduleTimeLock` function to compute the execution timestamp `eta` of the proposal as follows:

        uint256 minimumEta = block.timestamp + _minimumTimeLockDelay;

Hence if the `minimumTimeDelay` is set to zero, this could lead to proposals getting executed instantly thus violating safety concerns of the protocol users.

## Proof of Concept

```solidity
    constructor(
        address gateway,
        string memory governanceChain_,
        string memory governanceAddress_,
        uint256 minimumTimeDelay
    ) AxelarExecutable(gateway) TimeLock(minimumTimeDelay) {
```

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/governance/InterchainGovernance.sol#L33-L38

```solidity
    constructor(uint256 minimumTimeDelay) {
        _minimumTimeLockDelay = minimumTimeDelay;
    }
```

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/gmp-sdk/util/TimeLock.sol#L21-L23

```solidity
        uint256 minimumEta = block.timestamp + _minimumTimeLockDelay;
```

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/gmp-sdk/util/TimeLock.sol#L54


## Tools Used
Manual Review and VSCode

## Recommended Mitigation Steps

Since `TimeLock` is a critical functionality of this protocol it is recommended to check the `minimumTimeDelay` is higher than a `minimum constant value` inside the `constructor` when being set. 


## Assessed type

Other
MALICIOUS USER CAN EXECUTE PROPOSAL USING THE DEPOSITED FUNDS OF OTHER USERS IN THE CONTRACT
Audit: 2023-07-axelar-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/governance/InterchainGovernance.sol#L68-L79
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/util/Caller.sol#L16
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/governance/Multisig.sol#L30-L36


# Vulnerability details

## Impact

The `InterchainGovernance.executeProposal` `payable` function is used to execute a proposal by calling the target contract with calldata. Native value is transferred with the call to the target contract. 

The native value passed in is denoted by the `nativeValue` input parameter. `executeProposal` function calls the `Caller._call` function to execute the low level `call` function. The only check performed to make sure there is enough balance to transfer the `nativeValue` is performed inside the `Caller._call` function as shown below:

        if (nativeValue > address(this).balance) revert InsufficientBalance();

But the problem here is that there is no check verify that the `msg.sender` passed in enough `nativeValue`. If the contract has enough balance from other user deposits to cover for the `nativeValue`, the transaction will execute successfully. Hence a malicious proposal execution can happen by using the deposited funds of other users in the contract.

## Proof of Concept

```solidity
    function executeProposal(
        address target,
        bytes calldata callData,
        uint256 nativeValue
    ) external payable {
        bytes32 proposalHash = keccak256(abi.encodePacked(target, callData, nativeValue));

        _finalizeTimeLock(proposalHash);
        _call(target, callData, nativeValue);

        emit ProposalExecuted(proposalHash, target, callData, nativeValue, block.timestamp);
    }
```

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/governance/InterchainGovernance.sol#L68-L79

```solidity
        if (nativeValue > address(this).balance) revert InsufficientBalance();
```

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/util/Caller.sol#L16

```solidity
    function execute(
        address target,
        bytes calldata callData,
        uint256 nativeValue
    ) external payable onlySigners {
        _call(target, callData, nativeValue);
    }
```

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/governance/Multisig.sol#L30-L36

## Tools Used
Manual Review and VSCode

## Recommended Mitigation Steps

Hence it is recommended to check that `msg.value == nativeValue` in the `InterchainGovernance.executeProposal` function, to make sure `msg.sender` has transferred in the required amount of `native` tokens for the function call in the destination contract.

    require(msg.value == nativeValue, "Not enough native tokens");


## Assessed type

Invalid Validation
Lack of input validation for `StandardizedToken` contract `setup` function
Audit: 2023-07-axelar-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/token-implementations/StandardizedToken.sol#L56


# Vulnerability details

## Impact
`tokenManager` state variable can be set to zero. That can cause the [`interchainTransfer`](https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/interchain-token/InterchainToken.sol#L43-L67) and [`interchainTransferFrom`](https://github.com/code-423n4/2023-07-axelar/blob/2f9b234bb8222d5fbe934beafede56bfb4522641/contracts/its/interchain-token/InterchainToken.sol#L79-L106) functions DOS.

## Proof of Concept
Due to absence of a zero check for the `tokenManager_` variable in the `setup` function, the `tokenManager` state variable can be set to zero.
```solidity
54:            (tokenManager_, distributor_, tokenName, symbol, decimals) = abi.decode(params, (address, address, string, string, uint8));
55:            _setDistributor(distributor_);
56:            tokenManager = tokenManager_;
```

## Tools Used
Manual review

## Recommended Mitigation Steps
I suggest using zero check for this parameter in addition to the automated findings recommendations.


## Assessed type

Invalid Validation