xanoutas/solidity-security-findings huggingface dataset:
Lack of Access Control in `setRouter` Function
Audit: 2024-01-decent-findings
Severity: HIGH

# Lines of code

https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DcntEth.sol#L20-L22


# Vulnerability details

## Impact:

The identified issue in the `setRouter` function represents a critical security vulnerability due to the absence of proper access control. As the function stands, any user can change the router address, granting them the ability to mint and burn DcntEth tokens. This lack of access control could lead to unauthorized manipulation of the protocol, posing a severe threat to the integrity of the system and potential financial losses for the protocol and users.

## Proof of Concept:

The issue is evident in the `setRouter` function provided below:

```solidity
function setRouter(address _router) public {
    //@audit Lack of access control: anyone can change the router address
    router = _router;
}
```

As it can be seen any user can change the router address and set it to any other address (even his own wallet) which will allow him to call `mint` and `burn` to mint or burn DcntEth tokens, this is supposed to be done only by the true router.

This issue will result in unauthorized minting of DcntEth tokens which will affect the protocol working and cause financial losses.

## Tools Used:

Manual review

## Recommended Mitigation Steps:

To address this issue, implement proper access control by adding the `onlyOwner` modifier to the `setRouter` function.


## Assessed type

Access Control
Missing Revert Statement in Modifier
Audit: 2024-01-decent-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/UTB.sol#L228


# Vulnerability details

## Impact
 in modifier code contains a modifier named retrieveAndCollectFees. Upon closer inspection, it is observed that there is a potential issue with incomplete handling when certain conditions are not met. Specifically, if the address of feeCollector is not equal to zero and none of the conditions within the if-else blocks are satisfied, the modifier executes without any revert statement

https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/UTB.sol#L228C1-L252C1

    modifier retrieveAndCollectFees(
        FeeStructure calldata fees,
        bytes memory packedInfo,
        bytes calldata signature
    ) {
        if (address(feeCollector) != address(0)) {
            uint value = 0;
            if (fees.feeToken != address(0)) {
                IERC20(fees.feeToken).transferFrom(
                    msg.sender,
                    address(this),
                    fees.feeAmount
                );
                IERC20(fees.feeToken).approve(
                    address(feeCollector),
                    fees.feeAmount
                );
            } else {
                value = fees.feeAmount;
            }
            feeCollector.collectFees{value: value}(fees, packedInfo, signature);
        }
        _;
    }



## Tools Used

## Recommended Mitigation Steps

 require statement has been added at the end of the modifier to verify that the feeCollector address is not zero. If the condition is not met, the transaction will revert with an appropriate error message.

modifier retrieveAndCollectFees(
    FeeStructure calldata fees,
    bytes memory packedInfo,
    bytes calldata signature
) {
    require(address(feeCollector) != address(0), "Fee collector address is not set");

    uint value = 0;
    if (fees.feeToken != address(0)) {
        IERC20(fees.feeToken).transferFrom(
            msg.sender,
            address(this),
            fees.feeAmount
        );
        IERC20(fees.feeToken).approve(
            address(feeCollector),
            fees.feeAmount
        );
    } else {
        value = fees.feeAmount;
    }

    feeCollector.collectFees{value: value}(fees, packedInfo, signature);

    _;
}






## Assessed type

Invalid Validation
Smart contracts interacting with the protocol may lose their refunds
Audit: 2024-01-decent-findings
Severity: HIGH

# Lines of code

https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentBridgeExecutor.sol#L35-L38
https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentBridgeExecutor.sol#L62-L64


# Vulnerability details

## Impact
Smart contracts that interact with the protocol may not receive their refund in case a transaction fails on the destination chain. This is because refunds are sent to the `from` address of the source chain. Smart contacts are not guaranteed to have the same address on different chains. This means that any funds sent to `from` address that is not controlled by the protocol interacting with Decent will probably be lost.

## Proof of Concept
There are two (Gnosis) Safes controlled by a protocol on two different chains. Since they are smart contracts, their addresses can be different. The protocol wants to use Decent for cross-chain execution. For some reason, their transaction fails on the destination chain.

[`DecentBridgeExecutor`](https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentBridgeExecutor.sol#L62-L64) will send the funds to the `from` address

```solidity
    function _executeEth(
        address from,
        address target,
        uint256 amount,
        bytes memory callPayload
    ) private {
        weth.withdraw(amount);
        (bool success, ) = target.call{value: amount}(callPayload);
        if (!success) {
            // here
            payable(from).transfer(amount);
        }
    }
```

This `from` will be different from the address of the smart contract on the currentChain. Funds will be send to the wrong address and will be lost.
## Tools Used

## Recommended Mitigation Steps
Add a new parameter `address destinationRefund` that can be specified on the source chain. Then use it instead of `from`.

```diff
 if (!success) {
-            payable(from).transfer(amount);
+            payable(destinationRefund).transfer(amount);
 }
```


## Assessed type

Other
Reentrancy Vulnerability in User Withdrawal
Audit: 2024-01-decent-findings
Severity: HIGH

# Lines of code

https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentEthRouter.sol#L60
https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentEthRouter.sol#L313
https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentEthRouter.sol#L330


# Vulnerability details

## Impact
A reentrancy vulnerability exists in the `userIsWithdrawing` modifier of `DecentEthRouter`'s withdrawal mechanism. The vulnerability arises from the order of operations within the modifier, specifically the placement of the `_;` statement.

The current implementation allows for potential reentrancy attacks when the contract interacts with external contracts during the execution of functions using the `userIsWithdrawing` modifier (`removeLiquidityEth` and `removeLiquidityWeth`). Because the balance deduction occurs after the `_;` statement, an external contract could maliciously re-enter the function, leading to unexpected and potentially harmful behavior.

## Proof of Concept
[The modifier:](https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentEthRouter.sol#L60)

    60    modifier userIsWithdrawing(uint256 amount) {
    61        uint256 balance = balanceOf[msg.sender];
    62        require(balance >= amount, "not enough balance");
    63        _;         // External contract call occurs here
    64        balanceOf[msg.sender] -= amount;
    65    }

The affected functions in the `DecentEthRouter` contract:

[1)](https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentEthRouter.sol#L313)

    313    function removeLiquidityEth(
    314        uint256 amount
    315    ) public onlyEthChain userIsWithdrawing(amount) {
    316        dcntEth.burn(address(this), amount);
    317        weth.withdraw(amount);
    318        payable(msg.sender).transfer(amount);
    319    }

[2)](https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentEthRouter.sol#L330)

    330    function removeLiquidityWeth(
    331        uint256 amount
    332    ) public userIsWithdrawing(amount) {
    333        dcntEth.burn(address(this), amount);
    334        weth.transfer(msg.sender, amount);
    335    }



## Recommended Mitigation Steps

To mitigate this vulnerability, it is recommended to re-order the operations within the modifier. Move the balance deduction before the `_;` statement to ensure that state changes, such as balance deductions, occur before any external calls. By making this adjustment, the contract reduces the risk of reentrancy attacks by finalizing state changes before external calls take place, improving the overall security of the withdrawal mechanism.


## Assessed type

Reentrancy
Lack of ACL in receiveFromBridge function
Audit: 2024-01-decent-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-01-decent/blob/main/src/UTB.sol#L311-L319


# Vulnerability details

## Impact
In the UTB contract, we have the receiveFromBridge function:
[UTB.sol#L311-L319](https://github.com/code-423n4/2024-01-decent/blob/main/src/UTB.sol#L311-L319)
```solidity
    function receiveFromBridge(
        SwapInstructions memory postBridge,
        address target,
        address paymentOperator,
        bytes memory payload,
        address payable refund
    ) public {
        _swapAndExecute(postBridge, target, paymentOperator, payload, refund);
    }
```
This function does not have access control, so it can be called by any user, rendering the swapAndExecute function useless, which has access control and fees to be paid:
[UTB.sol#L108-L124](https://github.com/code-423n4/2024-01-decent/blob/main/src/UTB.sol#L108-L124)
```solidity
    function swapAndExecute(
        SwapAndExecuteInstructions calldata instructions,
        FeeStructure calldata fees,
        bytes calldata signature
    )
        public
        payable
        retrieveAndCollectFees(fees, abi.encode(instructions, fees), signature)
    {
        _swapAndExecute(
            instructions.swapInstructions,
            instructions.target,
            instructions.paymentOperator,
            instructions.payload,
            instructions.refund
        );
    }
```

## Proof of Concept
The following test can be used to validate missing ACL:

```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import {ERC20} from "solmate/tokens/ERC20.sol";
import {UTB, SwapInstructions, SwapAndExecuteInstructions, FeeStructure} from "../src/UTB.sol";
import {UTBExecutor} from "../src/UTBExecutor.sol";
import {UniSwapper} from "../src/swappers/UniSwapper.sol";
import {SwapParams} from "../src/swappers/SwapParams.sol";
import {XChainExactOutFixture} from "./helpers/XChainExactOutFixture.sol";
import "forge-std/console.sol";

contract UTBExactOutRoutesTest is XChainExactOutFixture {
    function setUp() public {
        src = optimism;
        dst = arbitrum;
        preSlippage = 2;
        postSlippage = 3;
        initialEthBalance = 1 ether;
        initialUsdcBalance = 10e6;
        MINT_GAS = 9e5;

        setRuntime(ENV_FORGE_TEST);
        loadAllChainInfo();
        setupUsdcInfo();
        setupWethHelperInfo();
        loadAllUniRouterInfo();
        setSkipFile(true);
        vm.label(alice, "alice");
        vm.label(bob, "bob");
    }

    function testSwapWethToUsdcAndMintAnNft() public {
        string memory chain = arbitrum;
        (
            UTB utb,
            /*UTBExecutor executor*/,
            UniSwapper swapper,
            ,
            ,

        ) = deployUTBAndItsComponents(chain);
        uint256 slippage = 1;

        address weth = getWeth(chain);
        address usdc = getUsdc(chain);

        cat = deployTheCat(chain);
        uint usdcOut = cat.price();

        (SwapParams memory swapParams, uint expe
Lack of access control on UTB::receiveFromBridge() makes it possible to bypass fees
Audit: 2024-01-decent-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-01-decent/blob/main/src/UTB.sol#L311-L319
https://github.com/code-423n4/2024-01-decent/blob/main/src/UTB.sol#L108-L124


# Vulnerability details

## Impact
The function `UTB::receiveFromBridge()` lacks access control, and its expected only to be called by the bridge adapter. 

The fact that it can be called by any user can make a malicious user to bypass access control and fees to be paid by the `swapAndExecute` function. 

[UTB.sol#L311-L319](https://github.com/code-423n4/2024-01-decent/blob/main/src/UTB.sol#L311-L319)
```solidity
    function receiveFromBridge(
        SwapInstructions memory postBridge,
        address target,
        address paymentOperator,
        bytes memory payload,
        address payable refund
    ) public {
        _swapAndExecute(postBridge, target, paymentOperator, payload, refund);
    }
```

This function does not have access control, so it can be called by any user, rendering the swapAndExecute function useless, which has access control and fees to be paid:
[UTB.sol#L108-L124](https://github.com/code-423n4/2024-01-decent/blob/main/src/UTB.sol#L108-L124)
```solidity
    function swapAndExecute(
        SwapAndExecuteInstructions calldata instructions,
        FeeStructure calldata fees,
        bytes calldata signature
    )
        public
        payable
        retrieveAndCollectFees(fees, abi.encode(instructions, fees), signature)
    {
        _swapAndExecute(
            instructions.swapInstructions,
            instructions.target,
            instructions.paymentOperator,
            instructions.payload,
            instructions.refund
        );
    }
```

## Proof of Concept
`receiveFromBridge` comments shows the intent of only being able to be called by the bridge adapter, and the logic is pretty straight forward of how it bypasses the fees of `swapAndExecute` since they execute the same thing.


## Tools Used
vscode, foundry

## Recommended Mitigation Steps
Allow the function `UTB::receiveFromBridge()` to only be called by the bridge adapter.


## Assessed type

Access Control
Absence of Blacklist Checks for ERC-20 Token Transactions
Audit: 2024-01-decent-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/UTB.sol#L108
https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/UTB.sol#L259


# Vulnerability details

## Description:
In the UTB.sol contract, there is no explicit check for blacklisted addresses when interacting with certain ERC-20 tokens. The addresses involved in token transactions, namely receiver and refund, lack a verification mechanism against a potential blacklist. The absence of this check may expose the contract to security risks, allowing transactions involving blacklisted addresses to proceed without restriction.

## Impact:
If blacklisted addresses are not prevented from participating in ERC-20 token transactions, it could lead to unauthorized or malicious activities. For instance, an attacker could exploit vulnerabilities or manipulate the contract's behavior by utilizing blacklisted addresses during token transfers. This poses a risk to the integrity and security of the contract.

## Mitigation Steps:

Implement a comprehensive blacklist mechanism that includes checks for both receiver and refund addresses before proceeding with ERC-20 token transactions.
Maintain and regularly update the blacklist to include addresses that are identified as potential security threats.
Consider using a modular approach, allowing for easy updates to the blacklist logic without modifying the core functionality of the contract.


## Assessed type

DoS
Exposure to Market Volatility and MEV Risks Due to Absence of User-Defined Deadlines in swapExactOut() Transactions
Audit: 2024-01-decent-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-01-decent/blob/main/src/swappers/UniSwapper.sol#L153


# Vulnerability details

## Issue
In function swapExactOut() in UniSwapper.sol, it also does not allow users to supply their own deadline, as the deadline parameter is simply passed in currently as block.timestamp, in which the transaction occurs. This effectively means, that the transaction has no deadline; which means that a swap transaction may be included anytime by validators and remain pending in mempool, potentially exposing users to sandwich attacks by attackers or MEV bots.

## Impact
User can face loss of funds due to sandwich attack or MEV attack

## Proof of Concept
In the Decent project, specifically within the `UniSwapper.sol` contract's `swapExactOut()` function, a significant risk scenario can arise due to the absence of a user-defined deadline for transactions. Let's consider a practical example to illustrate this vulnerability:

Alice, a user of the Decent platform, decides to execute a token swap using the `swapExactOut()` function. Her goal is to exchange 30 units of a token, say Token A, for a precise amount of another token, Token B, with the minimum amount she is willing to accept being 0.99 units of Token B. The `swapExactOut()` function in `UniSwapper` is structured to facilitate this kind of trade, ensuring that Alice receives at least the minimum amount of Token B she has specified.

However, a critical aspect of the `swapExactOut()` function is that it internally uses `block.timestamp` as the deadline for the transaction, effectively meaning that the transaction lacks a real deadline. Alice proceeds with her transaction, but she selects a gas fee that is relatively low, causing her transaction to remain pending in the mempool for an extended duration, potentially lasting from hours to weeks.

During this waiting period, the market conditions fluctuate significantly. The value of Token A, which Alice is trading, decreases. Meanwhile, a MEV (Miner Extractable Value) bot, scanning the mempool for profitable opportunities, notices Alice's pending transaction. The bot observes that the outdated `minOutput` value Alice provided now permits a considerable amount of slippage due to the market changes.

Capitalizing on this opportunity, the MEV bot orchestrates a sandwich attack. It executes transactions strategically around Alice's swap, exploiting the slippage and making a profit at Alice's expense. By the time Alice's transaction is eventually processed, the market value of Token B has changed, and although she receives the 0.99 units of Token B as initially intended, the value of these tokens in terms of her original asset (Token A) is substantially lower than expected. This unfavorable outcome could have been mitigated if a deadline had been set for her transaction, potentially causing it to expire and be cancelled if not executed within a suitable timeframe.

This scenario highlights the risks inherent in not allowin
cross reply is possible because there is no chain id in signed data
Audit: 2024-01-decent-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-01-decent/blob/07ef78215e3d246d47a410651906287c6acec3ef/src/UTBFeeCollector.sol#L44-L63


# Vulnerability details

## Impact
cross reply is possible because there is no chain id in signed data
in the contract `UTBFeeCollector.sol`  the function `collectFees` is vulnerable to cross chain sig reply attack because the signed data don't actually include the `chainId` in signed data which creates this problem. 
so what it creates is it makes 
## Proof of Concept
```solidity
    function collectFees(
        FeeStructure calldata fees,
        bytes memory packedInfo,
        bytes memory signature
    ) public payable onlyUtb {
        bytes32 constructedHash = keccak256(
            abi.encodePacked(BANNER, keccak256(packedInfo))
        );
        (bytes32 r, bytes32 s, uint8 v) = splitSignature(signature);
        address recovered = ecrecover(constructedHash, v, r, s);
        require(recovered == signer, "Wrong signature");
        if (fees.feeToken != address(0)) {
            IERC20(fees.feeToken).transferFrom(
                utb,
                address(this),
                fees.feeAmount
            );
        }
    }


```
## Tools Used
vscode
## Recommended Mitigation Steps
- consider adding the `chainId` value for the singed data 


## Assessed type

Other
Native tokens of different chains are not the same in terms of value
Audit: 2024-01-decent-findings
Severity: MEDIUM

# Lines of code

https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentEthRouter.sol#L302-L310


# Vulnerability details

## Impact
[`DecentEthRouter.sol`](https://github.com/decentxyz/decent-bridge/blob/7f90fd4489551b69c20d11eeecb17a3f564afb18/src/DecentEthRouter.sol#L302C1-L319C6) has two functions for adding and removing ETH liquidity. It accepts native token and mints the same amount of dcntEth tokens. However, some native tokens are a lot more cheaper than others. For example, Polygon's MATIC is way way more cheaper than mainnet ETH. This difference is not accounted for in this functions.

## Proof of Concept
An attacker can call `addLiqudityEth` on Polygon to pay a large amount of `msg.value` with MATIC

```solidity
    function addLiquidityEth()
        public
        payable
        onlyEthChain
        userDepositing(msg.value)
    {
        weth.deposit{value: msg.value}();
        // here
        dcntEth.mint(address(this), msg.value);
    }
```


Then, these tokens will be bridged to mainnet, and the opposite function will be called

```solidity
    function removeLiquidityEth(
        uint256 amount
    ) public onlyEthChain userIsWithdrawing(amount) {
        dcntEth.burn(address(this), amount);
        weth.withdraw(amount);
        payable(msg.sender).transfer(amount);
    }
```
The caller will have made a significant profit

## Tools Used
Manual Review

## Recommended Mitigation Steps
Account for different tokens


## Assessed type

Error
Signature can be re-used to pay less fees if fees are changed
Audit: 2024-01-decent-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-01-decent/blob/011f62059f3a0b1f3577c8ccd1140f0cf3e7bb29/src/UTBFeeCollector.sol#L44-L62


# Vulnerability details

## Impact
If Decent increase there fees, user could still replay old signatures and pay the old fee instead.

## Proof of Concept

There is no nonce, deadline or variable in the digest that limits old signatures from being replayed:

```solidity
function collectFees(
        FeeStructure calldata fees,
        bytes memory packedInfo,
        bytes memory signature
    ) public payable onlyUtb {
        bytes32 constructedHash = keccak256(
            abi.encodePacked(BANNER, keccak256(packedInfo))
        );
        (bytes32 r, bytes32 s, uint8 v) = splitSignature(signature);
        address recovered = ecrecover(constructedHash, v, r, s);
        require(recovered == signer, "Wrong signature");
        if (fees.feeToken != address(0)) {
            IERC20(fees.feeToken).transferFrom(
                utb,
                address(this),
                fees.feeAmount
            );
        }
    }

```

If Decent increases their fee, users can use old signatures to use the bridge with a cheaper fee.

This can go on until Decent realizes that this is possible and they change the `signer` at which point all previous signatures are invalid.


## Tools Used
vscode
## Recommended Mitigation Steps
Add a feeNonce that is updates when fees are changed such that all previous signatures are invalidated.


## Assessed type

Invalid Validation