xanoutas/solidity-security-findings huggingface dataset:
Tranche Token cant file name and symbol
Audit: 2023-09-centrifuge-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/util/Factory.sol#L98-L100


# Vulnerability details

In the provided code,  the newTrancheToken function is attempting to set the name and symbol of the TrancheToken using the token.file("name", name) and token.file("symbol", symbol) lines of code. However, the file function in the TrancheToken contract is designed to handle changes to the restrictionManager address, not the name and symbol. 
This indicates a potential issue in the code because it's attempting to use the file function for a purpose it wasn't designed for. As a result, filing the name and symbol in this way would not work as intended, and the `TrancheToken's` name and symbol would likely remain unset or in an incorrect state.

## Proof of Concept
```
function newTrancheToken(
        uint64 poolId,
        bytes16 trancheId,
        string memory name,
        string memory symbol,
        uint8 decimals,
        address[] calldata trancheTokenWards,
        address[] calldata restrictionManagerWards
    ) public auth returns (address) {
        address restrictionManager = _newRestrictionManager(restrictionManagerWards);

        // Salt is hash(poolId + trancheId)
        // same tranche token address on every evm chain
        bytes32 salt = keccak256(abi.encodePacked(poolId, trancheId));

        TrancheToken token = new TrancheToken{salt: salt}(decimals);

        token.file("name", name);
        token.file("symbol", symbol);
        token.file("restrictionManager", restrictionManager);

        token.rely(root);
        for (uint256 i = 0; i < trancheTokenWards.length; i++) {
            token.rely(trancheTokenWards[i]);
        }
        token.deny(address(this));

        return address(token);
    }

function file(bytes32 what, address data) public auth {
        if (what == "restrictionManager") restrictionManager = ERC1404Like(data);
        else revert("TrancheToken/file-unrecognized-param");
        emit File(what, data);
    }
```
In those two functions one In Factory.sol and the other in Tranche.sol, when you try to create a new tranche token and you call the file function in the TrancheToken contract you will find that it reverts if anything you pass is not `restrictionManger`.

## Recommended Mitigation Steps

Rewrite the file function in Tranche.sol to also accept data that includes the name and the symbol of the tranche 


## Assessed type

Error
Potential Reentrancy Vulnerability
Audit: 2023-09-centrifuge-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-centrifuge/blob/main/src/util/SafeTransferLib.sol#L15
https://github.com/code-423n4/2023-09-centrifuge/blob/main/src/util/SafeTransferLib.sol#L26


# Vulnerability details

## Description 
In the safeTransferFrom and safeTransfer functions, the success of the token transfer is checked before checking if the data length is zero. This creates a potential reentrancy vulnerability, as an attacker could potentially execute malicious code before the require statement is reached.

## Impact 

The bug introduces a potential reentrancy vulnerability in the safeTransferFrom and safeTransfer functions. In the current implementation, the success of the token transfer is checked after the data length, which could allow an attacker to execute malicious code before the require statement is reached. This means that during a failed transfer, an attacker might exploit this window of opportunity to launch reentrant attacks.

## Reproduction Steps
- Deploy a contract that uses SafeTransferLib.
- Trigger a token transfer using safeTransferFrom or safeTransfer in a way that will fail (e.g., insufficient allowance or balance).
- During the revert process, an attacker could potentially execute reentrant code.

## Proposed Solution
To fix this issue, it is recommended to check the data length before checking the success of the token transfer. This will ensure that the reentrancy vulnerability is addressed.

Code Suggestion
Replace:


(bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), "SafeTransferLib/safe-transfer-from-failed");


With:


(bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value));
require(data.length == 0 || (success && abi.decode(data, (bool))), "SafeTransferLib/safe-transfer-from-failed");



Repeat the same modification for the safeTransfer and safeApprove functions.

## Tools used 

Manual Review 


## Assessed type

Reentrancy
Tokens can be minted before user is ready to mint
Audit: 2023-09-centrifuge-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/LiquidityPool.sol#L148-L152
https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/InvestmentManager.sol#L451-L465


# Vulnerability details

## Impact
After request for deposit, any user can mint the tranche tokens of a user before the user is ready to mint, and that is because anybody can call the mint() in the receiver's address which is passed in through the processMint() in the InvestmentManager contract. The tokens minted are subtracted from the order book from the receiver address which could have ben inputed by any msg.sender from the mint()

## Proof of Concept
```solidity
function mint(uint256 shares, address receiver) public returns (uint256 assets) {
        // require(receiver == msg.sender, "LiquidityPool/not-authorized-to-mint");
        assets = investmentManager.processMint(receiver, shares);
        emit Deposit(address(this), receiver, assets, shares);
    }
```

```solidity
function processMint(address user, uint256 trancheTokenAmount) public auth returns (uint256 currencyAmount) {
        address liquidityPool = msg.sender;
        uint128 _trancheTokenAmount = _toUint128(trancheTokenAmount);
        require(
            (_trancheTokenAmount <= orderbook[user][liquidityPool].maxMint && _trancheTokenAmount != 0),
            "InvestmentManager/amount-exceeds-mint-limits"
        );

        uint256 depositPrice = calculateDepositPrice(user, liquidityPool);
        require(depositPrice != 0, "LiquidityPool/deposit-token-price-0");

        uint128 _currencyAmount = _calculateCurrencyAmount(_trancheTokenAmount, liquidityPool, depositPrice);
        _deposit(_trancheTokenAmount, _currencyAmount, liquidityPool, user);
        currencyAmount = uint256(_currencyAmount);
    }
```

## Tools Used
Manual Review

## Recommended Mitigation Steps
The mint() should ensure that receiver == msg.sender.
```solidity
function mint(uint256 shares, address receiver) public returns (uint256 assets) {
        require(receiver == msg.sender, "LiquidityPool/not-authorized-to-mint");
        assets = investmentManager.processMint(receiver, shares);
        emit Deposit(address(this), receiver, assets, shares);
    }
```


## Assessed type

Access Control
increaseAllowance and decreaseAllowance has been deprecated by OZ
Audit: 2023-09-centrifuge-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-centrifuge/blob/main/src/token/ERC20.sol#L139
https://github.com/code-423n4/2023-09-centrifuge/blob/main/src/token/ERC20.sol#L148


# Vulnerability details

## Impact
Using these functions will result in unexpected behaviour which will cause contracts to become corrupted.

## Proof of Concept

increaseAllowance and decreaseAllowance are already considered non-standard function and has now been deprecated. Check [this](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/4585).

## Tools Used
Manual Review

## Recommended Mitigation Steps
Utilise updated version of OZ ERC-20 contracts.



## Assessed type

ERC20
Unrestricted Permissions Assignment in newLiquidityPool Function
Audit: 2023-09-centrifuge-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/util/Factory.sol#L48


# Vulnerability details

**Summary: The newLiquidityPool function can potentially assign crucial permissions to unintended addresses, including the zero address. This introduces a risk of unintended behavior and vulnerabilities.**

## Impact
By granting permissions to the zero address, malicious entities can exploit the protocol by simulating actions from a null address. Similarly, by not ensuring that the contract's own address isn't included in the wards array before revoking its permissions, the function creates potential conflicting permission settings.

## Proof of Concept
Alice deploys the contract and decides to create a new liquidity pool. Alice intends to assign Bob as a ward for the new pool. However, due to an oversight or bug in the frontend application, the zero address is included in the list of wards.

## Tools Used

## Recommended Mitigation Steps
Ensure input validation for the wards array to prevent adding the zero address or any other unintended address.






## Assessed type

Access Control
[M-01 ]Lack of Parameter Validation and Individual Expiration Times in updateMembers Function
Audit: 2023-09-centrifuge-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-centrifuge/blob/main/src/token/RestrictionManager.sol#L62


# Vulnerability details

Vulnerable code:

```function updateMembers(address[] memory users, uint256 validUntil) public auth {
        uint256 userLength = users.length;
        for (uint256 i = 0; i < userLength; i++) {
            updateMember(users[i], validUntil);
        }
```
Issue summary:
In the updateMembers function, there are no restrictions or checks on the validUntil parameter, and the same validUntil time period is applied to all users in the array. 

Vulnerability details:
This vulnerability arises due to how the loop iterates through the winners array and distributes amounts to them based on corresponding percentages. It lacks proper validation to prevent repeating addresses or ensure consistent percentages.

Impact:
This design may pose security and operational risks. Without validation checks, the validUntil parameter can be set to arbitrary values, potentially causing unintended or malicious actions. Additionally, applying the same time period to all users may not be suitable for scenarios where individual users require different expiration times.

Recommendations:
·  To address this issue and improve the security and flexibility of the updateMembers function, consider implementing the following remediation steps:
    1. Parameter Validation: Add parameter validation checks to ensure that the validUntil parameter is within acceptable bounds and that it cannot be set to a value that poses security risks.
    2. Individual Expiration Times: Modify the function to accept an array of validUntil values, allowing different expiration times for individual users. A mapping or a struct to store and manage user-specific expiration times can be used.



## Assessed type

Invalid Validation
Missing update on `_calculateDomainSeparator` when name changed
Audit: 2023-09-centrifuge-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
In the `ERC20.sol` smart contract, the file function allows for updating the token name. However, it doesn't simultaneously trigger `_calculateDomainSeparator` to update the domain separator, which means that the `_DOMAIN_SEPARATOR` remains outdated even when the token name is altered. Currently, the `_DOMAIN_SEPARATOR` only updates when the chain ID changes. This could potentially compromise the processes of signature creation and verification.

## Proof of Concept
The `file` function can be found in the following script
```solidity
    function file(bytes32 what, string memory data) external auth {
        if (what == "name") name = data;
        else if (what == "symbol") symbol = data;
        else revert("ERC20/file-unrecognized-param");
        emit File(what, data);
    }

```

The logic to update the domain separator can be found as follow:
```solidity
    function DOMAIN_SEPARATOR() external view returns (bytes32) {
        return block.chainid == deploymentChainId ? _DOMAIN_SEPARATOR : _calculateDomainSeparator(block.chainid);
    }
```

## Tools Used
Manual

## Recommended Mitigation Steps
Recommend update domain separator when name changed.


## Assessed type

Other
processWithdraw() does not check if the user is have been restricted
Audit: 2023-09-centrifuge-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
Users that have been restricted are not prevented from accessing the InvestmentManager.processWithdraw() function, which they should be unable to call.

## Proof of Concept
No check that user address is not restricted.
```
 function processWithdraw(uint256 currencyAmount, address receiver, address user)
        public
        auth
        returns (uint256 trancheTokenAmount)
    {
        address liquidityPool = msg.sender;
        uint128 _currencyAmount = _toUint128(currencyAmount);
        require(
            (_currencyAmount <= orderbook[user][liquidityPool].maxWithdraw && _currencyAmount != 0),
            "InvestmentManager/amount-exceeds-withdraw-limits"
        );

        uint256 redeemPrice = calculateRedeemPrice(user, liquidityPool);
        require(redeemPrice != 0, "LiquidityPool/redeem-token-price-0");

        uint128 _trancheTokenAmount = _calculateTrancheTokenAmount(_currencyAmount, liquidityPool, redeemPrice);
        _redeem(_trancheTokenAmount, _currencyAmount, liquidityPool, receiver, user);
        trancheTokenAmount = uint256(_trancheTokenAmount);
    }
```

## Tools Used
Manual review. 

## Recommended Mitigation Steps
Inclue a check that prevents restricted address from processing withdrwals.
```
 require(liquidityPool.checkTransferRestriction(address(0), user, 0), "InvestmentManager/not-a-member");
```


## Assessed type

Invalid Validation
Lack of proper Event Data Validation, event data is not always validated before being emitted
Audit: 2023-09-centrifuge-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/PoolManager.sol#L96


# Vulnerability details


### Lack of proper Event Data Validation, event data is not always validated before being emitted

**Impact:** Events are essential for logging important contract actions and state changes, but in this contract, event data is not always validated before being emitted. This can lead to inconsistencies or inaccuracies in event logs.

**Proof of Concept:** In the provided contract code, there are several events used for logging contract actions. For example, here's an event declaration from the code:

```solidity
event PoolAdded(uint64 indexed poolId);
```

While the event declaration is correct, it doesn't include data validation for the event data it emits. In this specific example, there's no validation of the `poolId` before emitting the event. This means that invalid or unexpected data could be logged in the event, potentially leading to misleading or incorrect event logs.

**Recommended Mitigation Steps:** To ensure the accuracy and consistency of event logs, it's essential to validate event data before emitting events. Here are the recommended steps:

1. **Data Validation:** Before emitting an event, ensure that the event data accurately reflects the state changes or actions that have occurred. Validate the data to prevent inconsistencies or inaccuracies. For example:

   ```solidity
   event PoolAdded(uint64 indexed poolId);

   function addPool(uint64 poolId) public onlyGateway {
       Pool storage pool = pools[poolId];
       require(pool.createdAt == 0, "PoolManager/pool-already-added");
       
       // Perform data validation here to ensure poolId is valid
       require(poolId > 0, "PoolManager/invalid-pool-id");

       pool.poolId = poolId;
       pool.createdAt = block.timestamp;
       
       // Emit the event with validated data
       emit PoolAdded(poolId);
   }
   ```

   In this example, the `poolId` is validated to ensure it's greater than 0 before emitting the `PoolAdded` event.

2. **Consistent Event Data:** Ensure that the data emitted in events accurately reflects the state changes and actions taken by the contract. This helps maintain a consistent and reliable event log.

By implementing these steps, you can improve the accuracy and reliability of event logs in the contract, making it easier to track and analyze contract actions and state changes.


## Assessed type

Invalid Validation
Frontrunning Vulnerability in `approve()` function of ERC20
Audit: 2023-09-centrifuge-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-centrifuge/blob/main/src/token/ERC20.sol#L131-L137


# Vulnerability details

## Impact
The `approve()` function in the provided ERC20 contract is susceptible to a frontrunning attack. Malicious actors can exploit the timing between an `approve()` transaction being sent and it getting mined, potentially allowing them to spend more tokens than the token owner intended. If an owner tries to change the allowance of a spender, the spender can spend the original allowance before the new one takes effect, and then spend the new allowance, resulting in a total expenditure exceeding the latest set amount.

## Proof of Concept
Consider the following scenario:

1. Token owner Alice has [approved](https://github.com/code-423n4/2023-09-centrifuge/blob/main/src/token/ERC20.sol#L131-L137) spender Bob to withdraw 1,000 tokens.

https://github.com/code-423n4/2023-09-centrifuge/blob/main/src/token/ERC20.sol#L131-L137

```solidity
    function approve(address spender, uint256 value) external returns (bool) {
        allowance[_msgSender()][spender] = value;

        emit Approval(_msgSender(), spender, value);

        return true;
    }
```
2. Alice decides to modify this approval to allow Bob to withdraw only 1,500 tokens. She sends a transaction to set this new allowance.
3. Bob, monitoring the transaction pool, sees Alice's pending transaction.
4. Before Alice's transaction is mined, Bob sends a transaction (with a higher gas price for priority) to withdraw the initial 1,000 tokens.
5. If Bob's transaction is mined before Alice's, he will have withdrawn the 1,000 tokens.
6. Once Alice's transaction is mined, the allowance is reset to 1,500 tokens, allowing Bob to withdraw an additional 1,500 tokens.
7. In total, Bob can withdraw 2,500 tokens, even though Alice intended a maximum of 1,500 tokens in the second approval.

## Tools Used
Manual

## Recommended Mitigation Steps
Before increasing or decreasing the allowance, the contract could require that the current allowance be set to 0. This way, Alice would have to first send a transaction setting the allowance to 0 before setting it to a new value, ensuring Bob cannot front-run in between. The downside is that this requires two separate transactions, which might not be very user-friendly.

https://github.com/code-423n4/2023-09-centrifuge/blob/main/src/token/ERC20.sol#L131-L137

```diff
    function approve(address spender, uint256 value) external returns (bool) {
+        require((allowance[msg.sender][spender] == 0), "ERC20: set allowance to 0 first");
        allowance[_msgSender()][spender] = value;

        emit Approval(_msgSender(), spender, value);

        return true;
    }
```
Alternatively, simply remove `approve()` since users can always use `increaseAllowance()` for the same intended purpose.

In your contract, you've provided the `increaseAllowance()` and `decreaseAllowance()` functions, which is good. Informing users to prefer these methods o
There is not enough protection against signature malleability in the permit function in `ERC20.sol`
Audit: 2023-09-centrifuge-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
`_isValidSignature` used in tranche tokens in the `ERC20.sol` is susceptible to signature malleability since it allows `s` values to be also in the upper range, which can cause users to be forced to deposit or withdraw funds.

## Proof of Concept
`_isValidSignature` is used to validate the signature of a user by using the `r`,`s` and `v` values, which will let users `requestDepositWithPermit` and `requestRedeemWithPermit`. The problem relies in the fact that the `_isValidSignature` doesn't check if the `s` value is in the upper or lower range as can be seen here 
https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/token/ERC20.sol#L201-L208
, and basically allows for both ranges, which can lead to signature malleability. When you are verifying signatures you don't need to check the `v` since `ecrecover` does it, after the EIP-2 passed, but it can still be malleable if you let both upper and lower ranges of `s` as can be seen here https://medium.com/draftkings-engineering/signature-malleability-7a804429b14a .

## Tools Used
Manual review
## Recommended Mitigation Steps
Consider implementing protection against this case, similar to what OpenZeppelin does, by verifying the `s` value like here https://github.com/OpenZeppelin/openzeppelin-contracts/blob/3bd9ed377e738a1cc66cca180a2a26426c63b8dc/contracts/utils/cryptography/ECDSA.sol#L134-L136


## Assessed type

Error