xanoutas/solidity-security-findings huggingface dataset:
Insufficient Whitelist Validation in `Vultisig` Token Transfer Process
Audit: 2024-06-vultisig-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/hardhat-vultisig/contracts/extensions/VultisigWhitelisted.sol#L26-L33
https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/hardhat-vultisig/contracts/Whitelist.sol#L197-L226


# Vulnerability details



## Impact

The `Vultisig` contract fails to correctly validate whether the recipient of a token transfer is whitelisted. This oversight allows non-whitelisted addresses to receive tokens, potentially breaching security measures intended to restrict token distribution only to approved recipients. 

1. **Compliance Violation**: The failure to enforce whitelisting can result in violations of compliance requirements, especially in regulated environments.

## Proof of Concept

 Scenario: Failure to Properly Enforce Whitelisting in `Vultisig`

1. **Token Transfer Hook**: The `_beforeTokenTransfer` function is designed to validate transfers using the `checkWhitelist` function.

 ```solidity
   function _beforeTokenTransfer(address from, address to, uint256 amount) internal override {
       if (_whitelistContract != address(0)) {
           IWhitelist(_whitelistContract).checkWhitelist(from, to, amount);
       }
       super._beforeTokenTransfer(from, to, amount);
   }
 ```

2. **Whitelist Check**: The `checkWhitelist` function performs several validations but fails to properly check if the recipient (`to`) is whitelisted.

Link to the intended behaviour in the README 
https://code4rena.com/audits/2024-06-vultisig#:~:text=%2D%20Buyer%20whitelist%20index%20is%20within%20allowed%20index%20range(starting%20from%201%20and%20within%201%20~%20allowedWhitelistIndex%20%2D%20inclusive)

   >  /// @dev Revert if locked,
  > not whitelisted, 
 >blacklisted or 
>already contributed more than capped amount

 ```solidity
   function checkWhitelist(address from, address to, uint256 amount) external onlyVultisig {
       if (from == _pool && to != owner()) {
           if (_locked) {
               revert Locked();
           }

           if (_isBlacklisted[to]) {
               revert Blacklisted();
           }

           if (_allowedWhitelistIndex == 0 || _whitelistIndex[to] > _allowedWhitelistIndex) {
               revert NotWhitelisted();
           }

           uint256 estimatedETHAmount = IOracle(_oracle).peek(amount);
           if (_contributed[to] + estimatedETHAmount > _maxAddressCap) {
               revert MaxAddressCapOverflow();
           }

           _contributed[to] += estimatedETHAmount;
       }
   }
 ```

   **Issue**: The check `_whitelistIndex[to] > _allowedWhitelistIndex` incorrectly passes when `_whitelistIndex[to]` is `0`, which means `to` is not whitelisted.

3. **Failure Scenario**: If the `_whitelistIndex` of the recipient is `0`, the check `if (_allowedWhitelistIndex == 0 || _whitelistIndex[to] > _allowedWhitelistIndex)` incorrectly evaluates to `ok`, allowing the transfer to proceed eve
Whitelist check can be bypassed by any non-whitelisted address because it does not check if `to` is in whiltelist.
Audit: 2024-06-vultisig-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-06-vultisig/blob/main/hardhat-vultisig/contracts/Whitelist.sol#L216-L219


# Vulnerability details

## Impact
Any address can bypass whitelist check.

## Proof of Concept
`Whitelist.checkWhitelist()` does not check if `to` is added to whitelist.

https://github.com/code-423n4/2024-06-vultisig/blob/main/hardhat-vultisig/contracts/Whitelist.sol#L216-L219

```
      if (_allowedWhitelistIndex == 0 || _whitelistIndex[to] > _allowedWhitelistIndex) {
             revert NotWhitelisted();
       }
```

## Tools Used
Manual Review

## Recommended Mitigation Steps
`Whitelist.checkWhitelist()` should check if `to` is in whitelist.

https://github.com/code-423n4/2024-06-vultisig/blob/main/hardhat-vultisig/contracts/Whitelist.sol#L216-L219
```
-       if (_allowedWhitelistIndex == 0 || _whitelistIndex[to] > _allowedWhitelistIndex) {
+       if (_allowedWhitelistIndex == 0 || _whitelistIndex[to] == 0 || _whitelistIndex[to] > _allowedWhitelistIndex) {
                revert NotWhitelisted();
       }

```


## Assessed type

Access Control
Use of Uniswap V3's `slot0`  for Price Retrieval Can Lead to Severe Price Manipulation 
Audit: 2024-06-vultisig-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/src/ILOManager.sol#L115
https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/src/ILOManager.sol#L189


# Vulnerability details



## Impact
`ILOManager` relies on the `slot0`(heavily frowned at) of the `IUniswapV3Pool` interface to retrieve the current price (`sqrtPriceX96`) of a trading pair in the Uniswap V3 pool. This exposes the contract and its users to significant risks of price manipulation that can cause losses.

By using the most recent price data from `slot0`, the contract becomes vulnerable to price manipulation attacks executed through techniques such as MEV bots and flash loans. Malicious actors can artificially inflate or deflate the price of a token pair momentarily, causing the `ILOManager` contract to use the manipulated price for its internal calculations and transactions.

The consequences of using a manipulated price can be severe:
1. Users may receive unfair token swap rates.
2. The contract's internal calculations for token allocations, fee distributions, and other critical operations may be based on the manipulated price.
3. Malicious actors can profit from the price manipulation by back-running transactions and exploiting the contract's reliance on the manipulated price.

Furthermore, the use of `slot0` for price retrieval is not recommended by the Uniswap V3 documentation, as it provides only a single data point and does not account for price fluctuations over time. This approach fails to ensure a fair and stable price for the contract's operations.


## Proof of Concept
The `ILOManager` contract uses the `slot0` function to retrieve the current price (`sqrtPriceX96`) in two critical functions:

1. In the `_initUniV3PoolIfNecessary` function:

https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/src/ILOManager.sol#L115

```solidity
        function _initUniV3PoolIfNecessary(PoolAddress.PoolKey memory poolKey, uint160 sqrtPriceX96) internal returns (address pool) {
            // ...
            else {
@>                (uint160 sqrtPriceX96Existing, , , , , , ) = IUniswapV3Pool(pool).slot0();
                if (sqrtPriceX96Existing == 0) {
                    IUniswapV3Pool(pool).initialize(sqrtPriceX96);
                } else {
                    require(sqrtPriceX96Existing == sqrtPriceX96, "UV3P");
                }
            }
        }
```

2. In the `launch` function:

https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/src/ILOManager.sol#L189

```solidity
        function launch(address uniV3PoolAddress) external override {
            // ...
@>            (uint160 sqrtPriceX96, , , , , , ) = IUniswapV3Pool(uniV3PoolAddress).slot0();
            require(_cachedProject[uniV3PoolAddress].initialPoolPriceX96 == sqrtPriceX96, "UV3P");
            // ...
        }
```

In both cases, the
Whitelist doesn't work as intended
Audit: 2024-06-vultisig-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/hardhat-vultisig/contracts/Whitelist.sol#L204-L228


# Vulnerability details

## Impact
User can bypass whitelist functionality because it doesn't work as intended

## Proof of Concept
Whenever a `buy` of **VULT** tokens is initiated during the whitelist period, the `_beforeTokenTransfer` is called:

```
function _beforeTokenTransfer(address from, address to, uint256 amount) internal override {

        if (_whitelistContract != address(0)) {
            IWhitelist(_whitelistContract).checkWhitelist(from, to, amount);
        }

        super._beforeTokenTransfer(from, to, amount);

    }
```

which calls the function `checkWhitelist` in `Whitelist.sol`:

```
    /// @notice Check if address to is eligible for whitelist
    /// @param from sender address
    /// @param to recipient address
    /// @param amount Number of tokens to be transferred
    /// @dev Check WL should be applied only
    /// @dev Revert if locked, not whitelisted, blacklisted or already contributed more than capped amount
    /// @dev Update contributed amount

    function checkWhitelist(address from, address to, uint256 amount) external onlyVultisig {

        if (from == _pool && to != owner()) {

            // We only add limitations for buy actions via uniswap v3 pool
            // Still need to ignore WL check if it's owner related actions
            if (_locked) {
                revert Locked();
            }

            if (_isBlacklisted[to]) {
                revert Blacklisted();
            }

            if (_allowedWhitelistIndex == 0 || _whitelistIndex[to] > _allowedWhitelistIndex) {
                revert NotWhitelisted();
            }

            // // Calculate rough ETH amount for VULT amount
            uint256 estimatedETHAmount = IOracle(_oracle).peek(amount);
            
            if (_contributed[to] + estimatedETHAmount > _maxAddressCap) {
                revert MaxAddressCapOverflow();
            }

            _contributed[to] += estimatedETHAmount;
        }

    }
```

As said by the comments above, the function should revert if a user is not whitelisted, however this is exactly where the bug generates from.

Focus on this line here:

```
            if (_allowedWhitelistIndex == 0 || _whitelistIndex[to] > _allowedWhitelistIndex) {
                revert NotWhitelisted();
            }
```

Firstly, `_allowedWhitelistIndex` is set by the owner to indicate which whitelist indexes are able to pass this check for example:

**_allowedWhitelistIndex = 5**
So users with whitelist indexes <= 5 can buy vult tokens.

But a user that hasn't been whitelisted - his index will be 0, so this right here:

`_whitelistIndex[to] > _allowedWhitelistIndex`

Doesn't hold, a non whitelisted user can still buy vult tokens from the pool as long as **_allowedWhitelistIndex != 0**


## Tools Used
Manual Review

## Recommended Mitigation Steps
Make a ch
Non-whitelisted addresses can also buy liquidity and claim VULT token
Audit: 2024-06-vultisig-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/hardhat-vultisig/contracts/Whitelist.sol#L204
https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/src/ILOPool.sol#L133


# Vulnerability details

## Impact
Non-whitelisted address can buy liquidity and can claim VULT tokens which was meant for only whitelisted addresses.

## Proof of Concept
Github:- https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/hardhat-vultisig/contracts/Whitelist.sol#L204

In `Whitelist.sol::checkWhitelist` function we are not checking the value of the `_whitelistIndex` mapping of the address, means if a address is not whitelisted it will return 0, and the whitelisted index starts with index 1, but we are not checking if the _whitelistIndex is greater then 0 or not. 

```javascript
function checkWhitelist(address from, address to, uint256 amount) external onlyVultisig {
        if (from == _pool && to != owner()) {
            // We only add limitations for buy actions via uniswap v3 pool
            // Still need to ignore WL check if it's owner related actions
            if (_locked) {
                revert Locked();
            }
            if (_isBlacklisted[to]) {
                revert Blacklisted();
            }
            if (_allowedWhitelistIndex == 0 || _whitelistIndex[to] > _allowedWhitelistIndex) {
                revert NotWhitelisted();
            }
            // // Calculate rough ETH amount for VULT amount
            uint256 estimatedETHAmount = IOracle(_oracle).peek(amount);
            if (_contributed[to] + estimatedETHAmount > _maxAddressCap) {
                revert MaxAddressCapOverflow();
            }
            _contributed[to] += estimatedETHAmount;
        }
    }

```

## Tools Used
Manual Review

## Recommended Mitigation Steps
Add a if condition inside the `Whitelist.sol::checkWhitelist` function to check the value of `_whitelistIndex[to]` and if it's equal to 0 then revert.
```diff
            if (_isBlacklisted[to]) {
                revert Blacklisted();
            }

+           if(_whitelistIndex[to] == 0){
+               revert NotWhitelisted();
+           }

            if (_allowedWhitelistIndex == 0 || _whitelistIndex[to] > _allowedWhitelistIndex) {
                revert NotWhitelisted();
            }
```





## Assessed type

Other
Any address that is not registered in `_whitelistIndex` can pass the `checkWhitelist()` as long as it is not in the blacklist
Audit: 2024-06-vultisig-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/hardhat-vultisig/contracts/Whitelist.sol#L216


# Vulnerability details

# Summary
The `Whitelist` contract implements mechanism to allow whitelisted addresses to buy `VULT` token from uniswap pool. The rules are ordered as follows:

1. Prevent blacklisted addresses
2. If the address is not blacklisted, only allow if it is whitelisted

Point `1` is addressed correctly. But for point `2`, the checking is using indexing where the whitelisted address will contain index less than `_allowedWhitelistIndex`. The problem is solidity assume the value of `_whitelistIndex[to]` is equal to 0 by default making the `_whitelistIndex[to] > _allowedWhitelistIndex` test evaluated to false so that any address which is not registered in `_whitelistIndex` will pass the check.

## Example case
There are three addresses: A, B and C and assume the contract is not locked and the expression `(from == _pool && to != owner())` is true.

First, register the address A and B to whitelist:

`whitelist.addBatchWhitelist([A, B])`

The `_whitelistIndex` map will be updated to:

```
_whitelistIndex[A] = 1
_whitelistIndex[B] = 2
```

Second, call `whitelist.setAllowedWhitelistIndex(1)` to allow whitelisted address with index up to `1`.

The value of `_allowedWhitelistIndex` state becomes `1`

Now, if we call `whitelist.checkWhitelist(pool, A, amount)` will succeed because address A is in `_allowedWhitelistIndex` range.

If we call `whitelist.checkWhitelist(pool, B, amount)`, the function will revert because the index of B is not in `_allowedWhitelistIndex` range.

Now if we call `whitelist.checkWhitelist(pool, C, amount)`, since we never blacklist and never register address C in whitelist, it must not pass because it is not in whitelist. But something doesn't work as expected, the expression `_allowedWhitelistIndex == 0 || _whitelistIndex[to] > _allowedWhitelistIndex` in if statement will be evaluated to `false` making the if statement will not be executed.

```
_allowedWhitelistIndex == 0 => 1 == 0 => false

_whitelistIndex[C] > _allowedWhitelistIndex
_whitelistIndex[C] equals to 0 by default
0 > 1 => false

so that, false || false => false
```

The address C successfully pass the `checkWhitelist()`

# Impact
Whitelist mechanism is broken, allowing any address which is not registered in `_whitelistIndex` to buy `VULT` token from uniswap pool as long as it is not in the blacklist.

# Proof of Concept
Here is the PoC:

Add the following code to unit testing in `hardhat-vultisig/test/unit/Whitelist.ts`
```typescript
describe("Whitelist", function () {
    ...
    describe("Checkwhitelist", function () {
        ...
        it("Will always allow the non whitelisted address to pass the `checkWhitelist()` check", async function () {
            const { whitelist, pool, otherAccount, batchedAccount, mockContract, mockOracleSuccess } = await loadFixture(deployWhitelistFixture);
Whitelist and MaxAddressCap protections for Vultisig token can be bypassed
Audit: 2024-06-vultisig-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-06-vultisig/blob/main/hardhat-vultisig/contracts/Whitelist.sol#L204-L229


# Vulnerability details

## Impact
The idea of Whitelist for Vultisig is to restrict access for purchasing a token via Pool for some period of time. 
But actual whitelisting functionality in Whitelist implemented incorrectly and will open access for everyone.
Also, because of this vulnerability **maxAddressCap** protection also wouldn't work, because there is no issues for any user to buy tokens for other their addresses.

While it's hard to evaluate how critical it can be for a contract, but such issue breaks Main invariants described in the contract details. So it should be considered as an issue.

## Proof of Concept
1) Whitelist check validates existing users in whitelist array, and enable transaction for N amount of users by their index. To enable whitelist logic admin should put  **_allowedWhitelistIndex > 0**. But because index for a non existing address always will be 0 every user that not included in whitelist can bypass this check.

```
            if (_allowedWhitelistIndex == 0 || _whitelistIndex[to] > _allowedWhitelistIndex) {
                revert NotWhitelisted();
            }
```

2) The side effect of this issue also brings another vulnerability with **maxAddressCap**. Without working whitelist, such cap make no sense, because attacker can create multiple addresses and bypass it.
```
            uint256 estimatedETHAmount = IOracle(_oracle).peek(amount);
            if (_contributed[to] + estimatedETHAmount > _maxAddressCap) {
                revert MaxAddressCapOverflow();
            }

            _contributed[to] += estimatedETHAmount;
```


## Tools Used
Manual

## Recommended Mitigation Steps
Add check for 0 index.
```
            if (_allowedWhitelistIndex == 0 || _whitelistIndex[to] == 0 || _whitelistIndex[to] > _allowedWhitelistIndex) {
                revert NotWhitelisted();
            }
```





## Assessed type

Access Control
Token can be transfered to non-whitelisted because of wrong input validation
Audit: 2024-06-vultisig-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/hardhat-vultisig/contracts/Whitelist.sol#L216
https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/hardhat-vultisig/contracts/extensions/VultisigWhitelisted.sol#L30


# Vulnerability details

## Vulnerability details
Whenever a token is transfered, it calls checkWhitelist() on Whitelist.sol to check  if locked, not whitelisted, blacklisted or already contributed more than capped amount
```solidity
   /// @notice Before token transfer hook
    /// @dev It will call `checkWhitelist` function and if it's succsessful, it will transfer tokens, unless revert
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal override {
        if (_whitelistContract != address(0)) {
   @>         IWhitelist(_whitelistContract).checkWhitelist(from, to, amount);
        }
        super._beforeTokenTransfer(from, to, amount);
    }
```
```solidity
 function checkWhitelist(address from, address to, uint256 amount) external onlyVultisig {
       ....

        @>    if (_allowedWhitelistIndex == 0 || _whitelistIndex[to] > _allowedWhitelistIndex) {
                revert NotWhitelisted();
            }

          ....
    }
```
To check 'to' is whitelisted, it checks its _whitelistIndex is less than _allowedWhitelistIndex or not. Now this is problem because non-whitelisted user returns '0', which will be always less than _allowedWhitelistIndex (if _allowedWhitelistIndex is set). As result this check will be easily bypassed by a non-whitelisted user


## Impact
Non-whitelisted user will be able to receive tokens

## Proof of Concept
https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/hardhat-vultisig/contracts/extensions/VultisigWhitelisted.sol#L29C8-L31C10
https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/hardhat-vultisig/contracts/Whitelist.sol#L216C11-L218C14


## Tools Used
Manual Review

## Recommended Mitigation Steps
Use bool in mapping like _isBlacklisted mapping for whitelisting & verification


## Assessed type

Invalid Validation
checkWhitelist() is broken, causing non-whitelisted user to have token
Audit: 2024-06-vultisig-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/hardhat-vultisig/contracts/Whitelist.sol#L216
https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/hardhat-vultisig/contracts/Whitelist.sol#L234


# Vulnerability details

## Impact
When a user is whitelisted its index starts from 1 but if a user is not whitelisted its index is 0. The issue is while checking for whitelist it checks `_whitelistIndex[to] > _allowedWhitelistIndex`. This can be bypassed as non-whitelisted user will returns 0, which will be less than `allowedWhitelistIndex`
```solidity
  function checkWhitelist(address from, address to, uint256 amount) external onlyVultisig {
        if (from == _pool && to != owner()) {
            // We only add limitations for buy actions via uniswap v3 pool
            // Still need to ignore WL check if it's owner related actions
            if (_locked) {
                revert Locked();
            }

            if (_isBlacklisted[to]) {
                revert Blacklisted();
            }

        --->    if (_allowedWhitelistIndex == 0 || _whitelistIndex[to] > _allowedWhitelistIndex) {
                revert NotWhitelisted();
            }

            // // Calculate rough ETH amount for VULT amount
            uint256 estimatedETHAmount = IOracle(_oracle).peek(amount);
            if (_contributed[to] + estimatedETHAmount > _maxAddressCap) {
                revert MaxAddressCapOverflow();
            }

            _contributed[to] += estimatedETHAmount;
        }
    }
```

## Proof of Concept
https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/hardhat-vultisig/contracts/Whitelist.sol#L216C13-L218C14
https://github.com/code-423n4/2024-06-vultisig/blob/cb72b1e9053c02a58d874ff376359a83dc3f0742/hardhat-vultisig/contracts/Whitelist.sol#L232C1-L236C6

## Tools Used
VS code

## Recommended Mitigation Steps
Add this line 
```solidity
if(_whitelistIndex[to] == 0){
     revert;
}
```


## Assessed type

Error