xanoutas/solidity-security-findings huggingface dataset:
`decreaseDepositRequest()` and `handleExecutedDecreaseInvestOrder()` lack financial bookeeping present in other functions
Audit: 2023-09-centrifuge-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/InvestmentManager.sol#L174-L185
https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/InvestmentManager.sol#L277-L292


# Vulnerability details

### Impact
The functions `decreaseDepositRequest()` and `handleExecutedDecreaseInvestOrder()` lack the necessary bookeeping for proper functionality. Hence, malicious users may use this function to potentially drain the project of all its funds.
### POC
The LiquidityPool contract supports a variety of functionalities. One such functionality is the ability to create orders via calling the function `requestDeposit()`. As per the image [here](https://raw.githubusercontent.com/code-423n4/2023-09-centrifuge/main/images/investment_flow.png) provided in the C4 spec, `requestDeposit()` in LiquidityPool.sol will call `requestDeposit()` in InvestmentManager.sol, which sends an `increaseInvestmentOrder` message to the outgoing router. 
The outgoing router receives and parses the message via the `Messages.isExecutedCollectInvest` branch on lines [329-340](https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/gateway/Gateway.sol#L329-L340). The request will then undergo the necessary transfers and bookkeeping of internal contract balances on lines [247-249](https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/InvestmentManager.sol#L247-L249).


Before these orders are locked in, it is possible to decrease the amount requested in the order via the `decreaseDepositRequest()` function in LiquidityPool.sol. The call flow of this function is:
1. [`decreaseDepositRequest()`](https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/LiquidityPool.sol#L247) in LiquidityPool.sol -> 
2. [`decreaseDepositRequest()`](https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/InvestmentManager.sol#L174) in InvestmentManager.sol -> 
3. [`decreaseInvestOrder` message](https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/gateway/Gateway.sol#L212) in Gateway.sol ->
4. [`executeDecreaseInvestOrder` message](https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/gateway/Gateway.sol#L319) in Gateway.sol ->
5. [`handleExecutedDecreaseInvestOrder()`](https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/InvestmentManager.sol#L277) in InvestmentManage.sol
Throughout all of these functions, there lacks the bookeeping present in the `requestDeposit()` function call flow. There are no checks or updates on the deposit amount stored in the caller's order. The requested amount to decrease is simply transfered.

Hence, it is possible to craft a scenario such as this:
1. User Bob makes a
Checking only one value for zero could allow manipulation of the deposit price calculation.
Audit: 2023-09-centrifuge-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/InvestmentManager.sol#L552-L554


# Vulnerability details

## Impact
This can incorrectly value deposits.

## Proof of Concept
It concludes a deposit price of 0 means no deposits yet. But a manipulated orderbook could set maxDeposit non-zero and maxMint to 0 to produce an incorrect 0 price

The relevant code is in calculateDepositPrice: https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/InvestmentManager.sol#L552-L554
It currently checks if maxMint is zero to determine if a deposit price can be calculated.
But this could be manipulated by:
1. Calling requestDeposit to set maxDeposit > 0
2. Calling cancelInvestOrder to set maxMint = 0
Now maxDeposit is non-zero but maxMint is zero.
The calculateDepositPrice will see maxMint = 0 and return 0, even though deposits have occurred.
This could incorrectly represent the deposit price as 0 when it should be non-zero.
## Tools Used
Manual


## Recommended Mitigation Steps 
calculateDepositPrice should check that both maxDeposit and maxMint are greater than zero to determine if a price can be calculated


## Assessed type

Other
Unchecked Token Transfers.
Audit: 2023-09-centrifuge-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/LiquidityPool.sol#L295-L299
https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/LiquidityPool.sol#L301-L305
https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/LiquidityPool.sol#L307-L311


# Vulnerability details

## Impact

Malicious users or smart contracts can craft and send arbitrary data to vulnerable functions (`transferFrom`, `transfer`, and `approve`) without proper validation. 

## Proof of Concept

The `transferFrom`, `transfer`, and `approve` functions are used to interact with the `share` ERC20 token contract. These functions pass `msg.data` to the `share` contract without verifying the input data, which could potentially lead to vulnerabilities if malicious data is passed.

The "Unchecked Token Transfers" vulnerability occurs in multiple functions that interact with the share ERC20 token contract within the provided smart contract. Specifically, it affects the `transferFrom`, `transfer`, and `approve` functions. The vulnerability lies in the fact that these functions pass `msg.data` to the share ERC20 token contract without verifying the input data. Here are the lines of code where this occurs:

1. **In the **[transferFrom](https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/LiquidityPool.sol#L295-L299)** function:** 

   ```solidity
   function transferFrom(address, address, uint256) public returns (bool) {
       (bool success, bytes memory data) = address(share).call(bytes.concat(msg.data, bytes20(msg.sender)));
       _successCheck(success);
       return abi.decode(data, (bool));
   }
   ```
- In this code, the `transferFrom` function is intended to allow users to transfer tokens on behalf of another address.
   - However, instead of validating the input data, the function blindly forwards `msg.data` to the `share` ERC20 token contract using `address(share).call(...)`.
   - This means that any arbitrary data provided by the caller will be sent to the `share` contract without verification.
   - Malicious users or contracts could craft `msg.data` to trigger unintended actions on the `share` contract or potentially exploit vulnerabilities in the token contract.

2. **In the **[transfer](https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/LiquidityPool.sol#L301-L305)** function:** 

   ```solidity
   function transfer(address, uint256) public returns (bool) {
       (bool success, bytes memory data) = address(share).call(bytes.concat(msg.data, bytes20(msg.sender)));
       _successCheck(success);
       return abi.decode(data, (bool));
   }
   ```
- Similarly, the `transfer` function is intended for token transfers between addresses.
   - It also blindly forwards `msg.data` to the `share` ERC20 token contract without validation.
   -
No validation or verification of the source of the funds being collected.
Audit: 2023-09-centrifuge-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/LiquidityPool.sol#L259-L262
https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/LiquidityPool.sol#L265-L268


# Vulnerability details

## Impact

The `collectDeposit` and `collectRedeem` functions can be used to recover deposited assets. However, these functions don't verify the source of the funds. If a malicious user or contract interacts with these functions, it could lead to asset loss.

## Proof of Concept

In these functions, the `investmentManager.collectDeposit` and `investmentManager.collectRedeem` functions are called without any checks on the source of the assets being collected. This means that any address can call these functions to recover assets, potentially leading to asset loss if a malicious user or contract interacts with them.

Below are the relevant lines of code where this occurs:

* **[collectDeposit](https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/LiquidityPool.sol#L259-L262)** function
```solidity
function collectDeposit(address receiver) public {
    investmentManager.collectDeposit(receiver);
    emit DepositCollected(receiver);
}
```

* **[collectRedeem](https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/LiquidityPool.sol#L265-L268)** function
```solidity
function collectRedeem(address receiver) public {
    investmentManager.collectRedeem(receiver);
    emit RedeemCollected(receiver);
}
```
The potential asset recovery vulnerability in the `LiquidityPool` contract can be found in the `collectDeposit` and `collectRedeem` functions. These functions allow the recovery of deposited assets without verifying the source of the funds.

**Potential Exploit Scenarios:**

Consider the following exploit scenarios:

1. **Malicious Contract:** A malicious smart contract is created with the sole purpose of draining assets from the LiquidityPool. This contract calls the `collectDeposit` or `collectRedeem` functions with a destination address controlled by the attacker, leading to the loss of assets from the pool.

2. **Front-Running:** An attacker monitors pending transactions and identifies a user about to call the `collectDeposit` or `collectRedeem` functions. The attacker quickly submits a transaction to call the same function with their address as the receiver, causing the user's assets to be redirected to the attacker's account.

## Tools Used
Manual review

## Recommended Mitigation Steps

Implement source fund verification in the `collectDeposit` and `collectRedeem` functions. 

First:

1. Modify the `collectDeposit` and `collectRedeem` functions to include source fund verification.

2. Check if the sender's address matches the expected source of funds (e.g., the user who originally deposited the assets). If the sender's address does not match, revert the transaction.
Price Manipulation in Asset to Share Conversion.
Audit: 2023-09-centrifuge-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/LiquidityPool.sol#L115-L120
https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/LiquidityPool.sol#L122-L127


# Vulnerability details

## Impact

An attacker, who is also the contract owner, can manipulate the `latestPrice` variable within the LiquidityPool contract to exploit the asset to share conversion functions. The attacker artificially inflated the `latestPrice` to undervalue shares and assets during conversions, potentially causing financial losses for users.

## Proof of Concept

In functions like [`convertToShares`](https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/LiquidityPool.sol#L115-L120) and [`convertToAssets`](https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/LiquidityPool.sol#L122-L127), the contract calculates the number of shares or assets based on the provided input. These calculations are dependent on the `latestPrice`, which can be manipulated if not properly secured. It's crucial to implement checks to verify that the `latestPrice` is reasonable and within expected bounds.

Example:
```solidity
function convertToShares(uint256 assets) public view returns (uint256 shares) {
    require(latestPrice > 0, "Price not available");
    // Implement a check to ensure that the calculated shares are reasonable
    require(shares <= MAX_CALCULATED_SHARES, "Invalid conversion");
    shares = investmentManager.convertToShares(assets, address(this));
}
```
Share conversion calculations being dependent on the potentially manipulated `latestPrice` occurs in the following lines of code within the `LiquidityPool` contract:

```solidity
/// @notice Calculates the amount of shares that any user would approximately get for the amount of assets provided.
///         The calculation is based on the token price from the most recent epoch retrieved from Centrifuge.
///         The actual conversion will likely differ as the price changes between order submission and execution.
function convertToShares(uint256 assets) public view returns (uint256 shares) {
    shares = investmentManager.convertToShares(assets, address(this));
}

/// @notice Calculates the asset value for an amount of shares provided.
///         The calculation is based on the token price from the most recent epoch retrieved from Centrifuge.
///         The actual conversion will likely differ as the price changes between order submission and execution.
function convertToAssets(uint256 shares) public view returns (uint256 assets) {
    assets = investmentManager.convertToAssets(shares, address(this));
}
```
In both of these functions, the calculation of shares or assets is based on the `latestPrice`, which is set by the contract owner and can be manipulated. The conversion depends on this price, and if the `latestPrice` is ma
Lack of Price Validation and Potential Manipulation.
Audit: 2023-09-centrifuge-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/LiquidityPool.sol#L324-L328


# Vulnerability details

## Impact

- Users relying on the `latestPrice` within the contract can make decisions based on incorrect and manipulated price data.
- The attacker can gain an unfair advantage by exploiting the discrepancies between the manipulated `latestPrice` and the actual market price.
- Such actions can result in financial losses for legitimate users and disrupt the intended functioning of the LiquidityPool contract.

## Proof of Concept

The contract allows for the update of the `latestPrice` variable via the `updatePrice` function. However, this update mechanism is controlled by the contract owner, and there are no sanity checks to ensure that the updated price is accurate and obtained from a reliable source.

```solidity
function updatePrice(uint128 price) public auth {
    latestPrice = price;
    lastPriceUpdate = block.timestamp;
    emit UpdatePrice(price);
}
```
It relies on the contract owner to manually update the `latestPrice` variable without any checks to ensure the accuracy or reliability of the provided price. This could potentially lead to incorrect or malicious price updates, allowing the owner to manipulate the price unfairly.

1. The `updatePrice` function can be called by the contract owner (`auth` modifier), giving them complete control over setting the `latestPrice` variable.
2. There are no checks or validations in place to ensure that the provided `price` is accurate or obtained from a trusted source. This means that the owner can set arbitrary or incorrect prices without any restrictions.
3. The absence of checks opens up the possibility of price manipulation, where the contract owner could set prices that are not reflective of the actual market conditions, potentially impacting the fairness and integrity of the contract.

Now, consider the following steps in the scenario:

1. **Initial State**:
   - The LiquidityPool contract is deployed, and the contract owner becomes the owner of the contract.
   - The `latestPrice` variable is initialized with an accurate and legitimate price, e.g., 100.

2. **Manipulation by the Attacker**:
   - The contract owner, who is also the attacker, decides to manipulate the price for their advantage.
   - The attacker calls the `updatePrice` function with an arbitrary and incorrect price, e.g., 1000, which is significantly higher than the actual market price.

   ```solidity
   // Attacker's transaction
   liquidityPoolContract.updatePrice(1000);
   ```

3. **Manipulated State**:
   - As a result of the attacker's transaction, the `latestPrice` variable within the contract is now set to 1000, even though the actual market price remains at 100.

4. **Exploitation**:
   - Users of the LiquidityPool contract rely on the `latestPrice` variable for various calculations and asset conversions.
   - Due to the manipulated `latestPrice`, use
Signature malleability in permit function
Audit: 2023-09-centrifuge-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/token/ERC20.sol#L196-L214


# Vulnerability details

## Impact
In the `ERC20` contract used to define the interface for tranche tokens the permit function uses `ecrecover` to verify a signature submitted by the token owner to approve an address to spend its tranche tokens. However there is a well known issue related to signature malleability where if the r and s values are observed they can be used to craft another valid signature on the other side of the elliptic curve ([1](https://solodit.xyz/issues/transaction-processing-doesnt-reject-malleable-signatures-spearbit-none-polygon-zkevm-pdf)). 

This would allow any attacker that observers a permit call to a `LiquidityPool`'s `share` token, as is done in `requestRedeemWithPermit` to be able to create a new valid signature approving a malicious address which they can use to transfer the tokens to themselves before the redemption request is executed on the centrifuge chain.

## Proof of Concept
1. investor calls `LiquidityPool::requestRedeemWithPermit` passing in a signature to redeem their tranche tokens
2. attacker observers this call and uses the r and s values to craft a new valid signature which they pass by calling `permit` directly on the tranche token contract
3. attacker transfers permitted funds to their own contract before the request is executed given that requests are only executed at the end of an epoch

## Tools Used
Manual review

## Recommended Mitigation Steps
Implement the [OpenZeppelin ECDSA library](https://docs.openzeppelin.com/contracts/2.x/utilities) in order to restrict signatures to one side of the elliptic curve. 


## Assessed type

Access Control
`LiquidityPool.sol` doesn't respect fully EIP 4626
Audit: 2023-09-centrifuge-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/InvestmentManager.sol#L383-L393
https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/InvestmentManager.sol#L396-L406


# Vulnerability details

## Impact
The EIP-4626 states that the function `previewMint` and `previewWithdraw` should be rounded up always, but that is not the case in the `InvestmentManager.sol` which makes it not fully compliant.

## Proof of Concept
As can be seen by EIP-4626 
https://eips.ethereum.org/EIPS/eip-4626
the function `previewDeposit` and `previewRedeem` must round down, which is done trough the whole functions, but `previewMint` and `previewWithdraw` should be rounded up, which is not the case as can be seen here https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/InvestmentManager.sol#L579-L581. 
This will make the `LiquidityPool.sol` not fully EIP-4626 compliant as stated, which can lead to wrong assumptions if other protocol interact with Centrifuge.

## Tools Used
Manual review
## Recommended Mitigation Steps
Consider implementing EIP-4626 fully and correctly to prevent wrong assumptions.


## Assessed type

ERC4626
transferIn() is susceptible to a front-running attack
Audit: 2023-09-centrifuge-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-centrifuge/blob/main/src/UserEscrow.sol#L32
https://github.com/code-423n4/2023-09-centrifuge/blob/main/src/UserEscrow.sol#L30


# Vulnerability details

## Impact
Another auth caller can exploit the vulnerability by executing front-running transactions using the source address that has just given allowance approval to the contract. This allows the attacker to acquire and increase the destination amount of the destination address he specifies, all by using the opportunity of the allowance. The attacker only needs to for when a source grant the contract allowance, and then they can quickly use the source address and specify the destination address they want to increase amount for.

## Proof of Concept
https://github.com/code-423n4/2023-09-centrifuge/blob/main/src/UserEscrow.sol#L32

## Tools Used
Manaul Review

## Recommended Mitigation Steps
Consider validating msg.sender is source


## Assessed type

Access Control
Restriction Manager does not check the source address
Audit: 2023-09-centrifuge-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/token/RestrictionManager.sol#L28-L42


# Vulnerability details

## Impact
The ``detectTransferRestriction`` takes in all the necessary parameters:
```solidity
  function detectTransferRestriction(address from, address to, uint256 value) public view returns (uint8)
```

However it only performs a check on the address ``to``
The restriction used here is timebased, however a user can continuing using, swapping, transferring and even redeeming their tranche tokens long after their restriction time has passed.

## Proof of Concept
This is caused by the function below
```solidity

    function hasMember(address user) public view returns (bool) {
        if (members[user] >= block.timestamp) {
            return true;
        }
        return false;
    }

    function detectTransferRestriction(address from, address to, uint256 value) public view returns (uint8) {
        if (!hasMember(to)) {
            return DESTINATION_NOT_A_MEMBER_RESTRICTION_CODE;
        }

        return SUCCESS_CODE;
    }

```
This function ``detectTransferRestriction`` checks only the destination, not the origin address
## Tools Used
Manual review
## Recommended Mitigation Steps
Implement checks for the ``from`` address


## Assessed type

ERC20