xanoutas/solidity-security-findings huggingface dataset:
RubiconMarket.setFeeBPS() Missing sanity checks 
Audit: 2023-04-rubicon-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-04-rubicon/blob/main/contracts/RubiconMarket.sol#L1466-L1469


# Vulnerability details

## Impact

The function setFeeBPS in RubiconMarket contract is missing sanity check as below:

https://github.com/code-423n4/2023-04-rubicon/blob/main/contracts/RubiconMarket.sol#L1466-L1469

```solidity

    function setFeeBPS(uint256 _newFeeBPS) external auth returns (bool) {
        feeBPS = _newFeeBPS;
        return true;
    }
    
```
A compromised/malicious governance can set feeBPS to a very high value and cause a loss of funds by frontrunning users trying to call buy.


https://github.com/code-423n4/2023-04-rubicon/blob/main/contracts/RubiconMarket.sol#L338

```solidity

        uint256 fee = mul(spend, feeBPS) / 100_000;
        
```

## Proof of Concept
Fee is set to 0.2%.
Alice approves infinite USDC token to RubiconMarket.
Alice has 100,000 USDC and tries to buy another token in RubiconMarket, paying 10,000 USDC with expected fee of 0.2% * 10,000 = 20 USDC.
Governance frontrun the transaction by setting the fee to 900%, taking all 90,000 USDC balance Alice has as fee.


## Tools Used

Manual Review

## Recommended Mitigation Steps

Add a sanity check in RubiconMarket.setFeeBPS to ensure fee can only be set to reasonable value:

```solidity

function setFeeBPS(uint256 _newFeeBPS) external auth returns (bool) {
 (+)require(_newFeeBPS <= 1000, "max fee is 1%")
    feeBPS = _newFeeBPS;
    return true;
}

```
buy tokens amount miscalculated, results in function never working.
Audit: 2023-04-rubicon-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-04-rubicon/blob/main/contracts/RubiconMarket.sol#L1273


# Vulnerability details

## Impact
This is a consequence of the `Miscalculation of fees in buy function` issue, in the `_matcho` function :

```solidity
   function _matcho(
        uint256 t_pay_amt, //taker sell how much
        ERC20 t_pay_gem, //taker sell which token
        uint256 t_buy_amt, //taker buy how much
        ERC20 t_buy_gem, //taker buy which token
        uint256 pos, //position id
        bool rounding, //match "close enough" orders?
        address owner,
        address recipient
    ) internal returns (uint256 id) {
        uint256 best_maker_id; //highest maker id
        uint256 t_buy_amt_old; //taker buy how much saved
        uint256 m_buy_amt; //maker offer wants to buy this much token
        uint256 m_pay_amt; //maker offer wants to sell this much token

        // there is at least one offer stored for token pair
        while (_best[address(t_buy_gem)][address(t_pay_gem)] > 0) {
            best_maker_id = _best[address(t_buy_gem)][address(t_pay_gem)];
            m_buy_amt = offers[best_maker_id].buy_amt;
            m_pay_amt = offers[best_maker_id].pay_amt;

            // Ugly hack to work around rounding errors. Based on the idea that
            // the furthest the amounts can stray from their "true" values is 1.
            // Ergo the worst case has t_pay_amt and m_pay_amt at +1 away from
            // their "correct" values and m_buy_amt and t_buy_amt at -1.
            // Since (c - 1) * (d - 1) > (a + 1) * (b + 1) is equivalent to
            // c * d > a * b + a + b + c + d, we write...
            if (
                mul(m_buy_amt, t_buy_amt) >
                mul(t_pay_amt, m_pay_amt) +
                    (
                        rounding
                            ? m_buy_amt + t_buy_amt + t_pay_amt + m_pay_amt
                            : 0
                    )
            ) {
                break;
            }
            // ^ The `rounding` parameter is a compromise borne of a couple days
            // of discussion.
            buy(best_maker_id, min(m_pay_amt, t_buy_amt));
            emit LogMatch(id, min(m_pay_amt, t_buy_amt));
            t_buy_amt_old = t_buy_amt;
            t_buy_amt = sub(t_buy_amt, min(m_pay_amt, t_buy_amt));
            t_pay_amt = mul(t_buy_amt, t_pay_amt) / t_buy_amt_old;

            if (t_pay_amt == 0 || t_buy_amt == 0) {
                break;
            }
        }

        if (
            t_buy_amt > 0 &&
            t_pay_amt > 0 &&
            t_pay_amt >= _dust[address(t_pay_gem)]
        ) {
            //new offer should be created
            id = super.offer(
                t_pay_amt,
                t_pay_gem,
                t_buy_amt,
                t_buy_gem,
                owner,
                recipient
            );
            //insert offer into the sorted list
            _sort(id, pos);
        }
    }
```

The buy mecha
TVL at the connector level is manipulable
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/accountingManager/AccountingManager.sol#L344
https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/accountingManager/AccountingManager.sol#L239
https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/helpers/BaseConnector.sol#L249
https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/accountingManager/AccountingManager.sol#L627
https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/accountingManager/AccountingManager.sol#L592


# Vulnerability details

## Impact
For most connectors, such as AMMs, the function `_getPositionTVL` is highly trickable, the manager can get sandwich with the attacker inflating or deflating depending the usecase the TVL of the connector. Whenever the manager calls `calculateDepositShares` and `calculateWithdrawShares`, there is a call to `previewDeposit` and `previewRedeem`  both of this function relies on computing the TVL at the time of the call. If the TVL can be inflated or deflated, the number of shares minted or redeemed will vary. An attacker could you use that to completely empty the vault.

## Proof of Concept
Let's take the example of the `AerodromConnector`, and he wants to exit the pool with more capital than expected: 

- His first tx is a `withdraw`
- Then he waits a manager to call `calculateWithdrawShares` with his included withdraw request.
- He insert a previous transaction flashloaning token0 and swap to imbalance nearly entirely the pool such that the invariant is still holding, by moving the pool price away the number of both tokens (not the value) is way smaller. As the Connector looks at the oracle value of both tokens, the value increased a lot such that the previewRedeem increased a lot
- He insert the manager tx  
- He reswap to reset the pool at the initial (or close) state

He can now withdraw more in value than he should have

## Tools Used
Foundry


## Assessed type

Other
Missing address validation in GearBoxV3:executeCommands allows GearBoxV3 connector funds to be stolen via malicious strategies submitted to Strategy Managers
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/connectors/GearBoxV3.sol#L79


# Vulnerability details

## Summary & Impact
Due to a lack of facade address and position whitelisting of the `facade` address argument of the `GearBoxV3:executeCommands` function, `CreditFacadeV3` addresses addresses can be set in internal calls to `SafeERC20:forceApprove`, giving malicious contracts  used in strategies the ability to drain the GearBoxV3 connector of deposited asset through the inherited call to `BaseConnector:_approveOperations`.

Malicious actors may engineer enticing strategies implying high returns which involve malicious CreditFacadeV3 contracts. Strategies may also be directly created by malicious token owners. 

AI agent or human Strategy Managers may therefore inadvertedly select individual or batched strategies involving such maliocus tokens for use against the GearBoxV3, based on the implication of high returns. It is also possible for such transactions to be hidden between batches of attractive, legitimate trades which may be executed in a batch manner via the `BalancerFlashLoan:makeFlashLoan` function if supported.

The efficacy of Watchers or other AI agents cannot be relied upon to prevent abuse of this issue, as the NOYA documentation at the time of audit states that all roles are [managed by the NOYA team](https://docs.noya.ai/technical-architecture/technical-architecture/roles-in-noya#watchers). Therefore, the current technical controls of the `GearBoxV3` contract do not sufficiently protect against external threats from abusing this issue. 

## Root Cause - Missing `facade` address verification in `GearBoxV3:executeCmmands`
Most other Connector functions invokable by Strategy Managers only allowed whitelisted positions to be executed, via calls to `Registry:updateHoldingPosition` using a calculated `positionId` hash from `Registry:calculateHoldingPosition`. Other connectors instead relied on token whitelisting, or a trusted receiver address, to achieve a similar level of protection against potentially harmful strategies being executed. However, the `GearBoxV3:executeCommands` function lacked any protection against malicious strategy execution, and as such an arbitrary spender address can be set in an internal call to the inherited `BaseConnector:_approveOperations` function.  

A malicious contract implementing a function for `multicall` may then transfer the approved asset to an arbitrary address. This is necessary as the `BaseConnector:_revokeApproval` function is called immediately following the multicall.

```solidity

//File:contracts/connectors/GearBoxV3.sol

    /**
     * @notice Execute multiple commands on a credit account
     * @param facade - CreditFacade address
     * @param creditAccount - credit account address
     * @param calls - array of multicalls
     * @param approvalToken - token to approve
     * @param amount - amount to approve
     */
CamelotConnector:  wrong TVL calculations
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/connectors/CamelotConnector.sol#L88-L96


# Vulnerability details

## Impact
CamelotConnector calculates erroneously the connector's TVL

## Proof of Concept

CamelotConnector calculates erroneously the connector's TVL.
This will affect NOYA total value locked amount and vault share price. 

https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/connectors/CamelotConnector.sol#L88-L96

```solidity

CamelotConector `_getPositionTVL` calculates erroneously the TVL:

    function _getPositionTVL(HoldingPI memory p, address base) public view override returns (uint256 tvl) {
        (address tokenA, address tokenB) =
            abi.decode(registry.getPositionBP(vaultId, p.positionId).data, (address, address));
        address pool = factory.getPair(tokenA, tokenB);
        uint256 totalSupply = IERC20(pool).totalSupply();
        (uint256 reserves0, uint256 reserves1,,) = ICamelotPair(pool).getReserves();

        uint256 balanceThis = IERC20(pool).balanceOf(address(this));
        return balanceThis * (_getValue(tokenA, base, reserves0) + _getValue(tokenB, base, reserves1)) / totalSupply;
```

## Tools Used

## Recommended Mitigation Steps
First it should get the connector's provided liquidity, calculate how much of tokenA and tokenB it is corresponding to, and after that use `_getValue` to calculate the tokens value in `base` token. Then sum the those 2 values.


## Assessed type

Error
SNXConnector: Inadequate Access Control
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-noya/blob/main/contracts/connectors/SNXConnector.sol#L46-L59


# Vulnerability details

## Impact
The contracts do not currently have adequate access control in place, meaning that anyone can call most of the functions in the contract. This is because these functions are marked with the `onlyManager` modifier. However, there is no implementation of this modifier in the provided code. 

## Proof of Concept
for example,


https://github.com/code-423n4/2024-04-noya/blob/main/contracts/connectors/SNXConnector.sol#L46-L59

```solidity 
function withdraw(address _token, uint256 _amount, uint128 _accountId) public onlyManager { 
// Function implementation 
}

function mintOrBurnSUSD(
        uint256 _amount, 
        uint128 _accountId, 
        uint128 poolId, 
        address collateralType, 
        bool mintOrBurn 
    ) public onlyManager { 
    // Function implementation 
}
```

Anyone can call these functions as there's no implementation of the `onlyManager` modifier to check the caller's address and ensure they have the required access level.



## Tools Used

Manual Review

## Recommended Mitigation Steps

The contract should implement access control to restrict access to functions like `withdraw()`, `mintOrBurnSUSD()`, etc. 

This could be something as simple as creating an `onlyManager` modifier that restricts access to only a specified address or addresses:

```solidity
modifier onlyManager() {
    require(msg.sender == manager, "Only the manager can call this function");
    _;
}
```

In the above example, `manager` is an address variable that is assigned to the desired manager's address. Furthermore, contract should have a function to change the manager's address if required.

Also, always make sure that high-risk functions (like `withdraw()`, `mintOrBurnSUSD()`, etc.) have sufficient access control. These functions should be accessible only to the contract owner or a selected group of managers.


## Assessed type

Access Control
`managementFeeReceiver` and `performanceFeeReceiver` can make it so they receive to much management fees
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/accountingManager/AccountingManager.sol#L514


# Vulnerability details



## Impact
`managementFeeReceiver` and `performanceFeeReceiver` can make it so they receive to much management fees.

## Proof of Concept
```solidity
function collectManagementFees() public onlyManager nonReentrant returns (uint256, uint256) {
    if (block.timestamp - lastFeeDistributionTime < 1 days) {
        return (0, 0);
    }
    uint256 timePassed = block.timestamp - lastFeeDistributionTime;
    if (timePassed > 10 days) {
        timePassed = 10 days;
    }
    uint256 totalShares = totalSupply();
@>  uint256 currentFeeShares = balanceOf(managementFeeReceiver) + balanceOf(performanceFeeReceiver)
        + preformanceFeeSharesWaitingForDistribution;

    uint256 managementFeeAmount =
@>      (timePassed * managementFee * (totalShares - currentFeeShares)) / FEE_PRECISION / 365 days;
    _mint(managementFeeReceiver, managementFeeAmount);
    emit CollectManagementFee(managementFeeAmount, timePassed, totalShares, currentFeeShares);
    lastFeeDistributionTime = block.timestamp;
    return (managementFeeAmount, timePassed);
}
```
`managementFeeReceiver` and `performanceFeeReceiver` can transfer out their balance of shares to decrease `currentFeeShares`. This increases `managementFeeAmount`, the amount minted when collecting fees.

## Recommended Mitigation Steps
Track the amount of fees received by `managementFeeReceiver` and `performanceFeeReceiver` instead of relying on `balanceOf()`.


## Assessed type

Other
MorphoBlueConnector: Incorrect calculation of TVL 
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-noya/blob/main/contracts/connectors/MorphoBlueConnector.sol#L125-L132


# Vulnerability details

## Impact
The vulnerability is related to the calculation of total value locked (TVL) in the `_getPositionTVL` function.

Currently, the calculation for TVL sums up the supply amount, borrow amount, and converted collateral amount. This leaves room for an inflated total value locked (TVL) estimation if a user borrows a large amount.

## Proof of Concept
https://github.com/code-423n4/2024-04-noya/blob/main/contracts/connectors/MorphoBlueConnector.sol#L125-L132

```solidity
            uint256 borrowAmount =
                uint256(pos.borrowShares).toAssetsUp(market.totalBorrowAssets, market.totalBorrowShares);
            uint256 supplyAmount =
                uint256(pos.supplyShares).toAssetsUp(market.totalSupplyAssets, market.totalSupplyShares);
            tvl = _getValue(
                params.loanToken,
                base,
                supplyAmount + borrowAmount + convertCToL(pos.collateral, params.oracle, params.collateralToken)
```

## Tools Used

Manual Review

## Recommended Mitigation Steps

The calculation for TVL should not involve the borrowed assets as it creates an illusion of it being part of your total investments. The calculation should only consider the tokens which are supplied and collateral provided by the user. Still, accuracy would depend on how the platform treats borrowed tokens. 

To mitigate this, you can modify the calculation of `tvl` as follows:
```solidity
tvl = _getValue(params.loanToken, base, supplyAmount + convertCToL(pos.collateral, params.oracle, params.collateralToken));
```

Please also consider rounding errors and carefully review and test cases where `_.toAssetsUp()` returns large values.




## Assessed type

Math
Tokens with blacklisting capabilities may block the withdrawal queue. 
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-noya/blob/main/contracts/accountingManager/AccountingManager.sol#L428


# Vulnerability details

## Impact
withdrawal execution will be blocked and DOS'ed. 

## Proof of Concept
In accounting manager : 
'Executewithdraw' function exexutes the withdrawal request listed one by one. It pulls of the data from queue and executes.  However if receiver of withdrawal is blacklisted, the loop will revert [here](https://github.com/code-423n4/2024-04-noya/blob/main/contracts/accountingManager/AccountingManager.sol#L428) and execution gets halted and DOS'ed permanently. 

```solidity 
 function executeWithdraw(uint256 maxIterations) public onlyManager nonReentrant whenNotPaused {
        //..
        while (
            currentWithdrawGroup.lastId > firstTemp
                && withdrawQueue.queue[firstTemp].calculationTime + withdrawWaitingTime <= block.timestamp
                && i < maxIterations
        ) {
           ....
 @>         baseToken.safeTransfer(data.receiver, baseTokenAmount);
            emit ExecuteWithdraw(
                firstTemp, data.owner, data.receiver, shares, data.amount, baseTokenAmount, block.timestamp
            );
            delete withdrawQueue.queue[firstTemp];
            // increment the first index of the withdraw queue
            firstTemp += 1;
        }
      ...
```
## Tools Used
Manual review 
## Recommended Mitigation Steps
Try implementing a claim function. Without directly sending funds to the user,   Withdraws should be available serially . 



## Assessed type

DoS
Missing Validation of Token when transferring to Trusted address.
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-noya/blob/main/contracts/accountingManager/AccountingManager.sol#L150-L160
https://github.com/code-423n4/2024-04-noya/blob/main/contracts/interface/IPositionRegistry.sol#L31-L36


# Vulnerability details

## Impact
The `sendTokensToTrustedAddress` is used to transfer tokens from accounting manager to other contracts, only the whitelisted connectors of the vault are allowed to call this function. And every connector has a `trustedTokens` mapping which contains the allowed tokens that the connector can hold but this important validation is missing, this results any whitelisted connector will be able to withdraw any asset from the vault, and also one more important missing validation here is that the amount to withdraw is also not validated, as a result anyone malicious connector can withdraw all the assets from the vault.


## Proof of Concept
```sol
/**
 * @Title: ConnectorData
 * @dev This struct is used to store the information of a connector
 * @param connectorAddress The address of the connector
 * @param enabled A boolean indicating if the connector is enabled
 * @param isPositionUsed A mapping of positionIds that the connector is using
 * @param holdingPositions An array of HoldingPI that the connector is holding
 * @param trustedTokens A mapping of additional tokens that the connector is allowed to hold
 */
struct ConnectorData {
    bool enabled;
    mapping(address => bool) trustedTokens;
}
```

```sol
/// @notice sendTokensToTrustedAddress is used to transfer tokens from accounting manager to other contracts
    function sendTokensToTrustedAddress(address token, uint256 amount, address _caller, bytes calldata _data)
        external
        returns (uint256)
    {
        emit TransferTokensToTrustedAddress(token, amount, _caller, _data);//@note unnecessary event emission, only emit event after validating the caller
        if (registry.isAnActiveConnector(vaultId, msg.sender)) {
            IERC20(token).safeTransfer(address(msg.sender), amount);//@audit (token is not validated) check that the token is authorized to the specific connector, vaults.connectors.trustedTokens or baseToken holds the valid tokens fo the user.
            //@audit also the amount is not validated, leading the connector to drain all the funds.
            return amount;
        }
        return 0;
    }
```


## Tools Used
Manual Review

## Recommended Mitigation Steps
The token should be validated before transfer. Check that the token is whitelisted in the `ConnectorData.trustedTokens`.


## Assessed type

Invalid Validation