xanoutas/solidity-security-findings huggingface dataset:
Missing FraxPool address validation allows for malicious strategies to steal FraxConnector contract funds.
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Summary & Impact
Due to a lack of pool address and position whitelisting of the `IFraxPair pool` argument of the `FraxConnector:repay` function, arbitrary spender addresses can be set in internal calls to `SafeERC20:forceApprove`, giving malicious Frax pool addresses the ability to drain the FraxConnector contract of any deposited asset through the inherited call to `BaseConnector:_approveOperations`.

Malicious token owners may engineer enticing strategies implying high returns which involve malicious token 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 FraxConnector, 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.

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 `FraxConnector` contract do not sufficiently protect against external threats from abusing this issue. 

It is recommended to either use a trusted spender address in the `FraxConnector:repay` function, or to enforce position whitelisting via the public `Registry:isPositionTrustedForConnector` function before operating on tokens in `FraxConnector:repay`.

## Root Cause - Missing spender verification in `FraxConnector:repay`
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 `FraxConnector:repay` 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 the required function selectors seen below for `toBorrowAmount`, `userBorrowShares`, `repayAsset` may simply return the required values to pass the `sharesToRepay > sharesOwed` check. Similarly, the malicious token `asset` may be configured to return the address of any token the `FraxConnector` contract has a balance for - e.g., USDC.

```solidity

//File:
LidoConnector: Unrestricted Ether Withdrawal
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-noya/blob/main/contracts/connectors/LidoConnector.sol#L37-L45


# Vulnerability details

## Impact
This vulnerability is brought about by the interaction of two primary pieces of the contract. 

1. The `deposit()` function allows the contract to receive ETH and then deposit it into the Lido contract. There exists no limitation on who can call this function.
2. The `claimWithdrawal()` function retrieves ETH from the Lido contract and converts it back to WETH. 

When the `deposit()` and `claimWithdrawal()` functions are used in combination, an attacker could gain the ability to deposit Ethereum into the contract and trigger the `claimWithdrawal()` function to retrieve the same funds, effectively making illicit withdrawals. Depending on the balance of the contract's account in Lido, an attacker may steal an amount of Ethereum higher than what they initially deposited.

## Proof of Concept

https://github.com/code-423n4/2024-04-noya/blob/main/contracts/connectors/LidoConnector.sol#L37-L45


```Solidity
function deposit(uint256 amountIn) external onlyManager nonReentrant {
    IWETH(weth).withdraw(amountIn); // deposit received eth into Lido
    // referral address can be different
    ILido(lido).submit{ value: amountIn }(address(0));
    _updateTokenInRegistry(steth);
    _updateTokenInRegistry(weth);
    emit Deposit(amountIn);
}
```

https://github.com/code-423n4/2024-04-noya/blob/main/contracts/connectors/LidoConnector.sol#L69-L87

```Solidity
function claimWithdrawal(uint256 requestId) public onlyManager nonReentrant {
    // approve to lidoWithdrawal to spend withdrawal NFT
    ILidoWithdrawal(lidoWithdrawal).approve(lidoWithdrawal, requestId);
    // eth balance before claim
    uint256 beforeClaimBalance = address(this).balance;
    // claim request withdrawal
    ILidoWithdrawal(lidoWithdrawal).claimWithdrawal(requestId);
    // emit ClaimWithdrawal event
    IWETH(weth).deposit{ value: address(this).balance - beforeClaimBalance }();
    registry.updateHoldingPosition(
        vaultId,
        registry.calculatePositionId(address(this), LIDO_WITHDRAWAL_REQUEST_ID, ""),
        abi.encode(requestId),
        "",
        true
    );
    _updateTokenInRegistry(weth);
    emit ClaimWithdrawal(requestId);
}
```

## Tools Used

Manual Review

## Recommended Mitigation Steps

Consider the following improvements:

1. Implement access control measures in the `deposit()` function to restrict who is authorized to make deposits.
2. In the `claimWithdrawal()` function, add a check that verifies that the Ethereum being retrieved actually belongs to the calling address, this will help prevent unauthorized withdrawals. Ensure withdrawal requests are tracked and always linked to the Ethereum address who deposited the funds.



## Assessed type

Other
Value of asset token can be incorrect when usage of ETH/USD Chainlink oracle is needed
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-noya/blob/cc3854f634a72bd4a8b597021887088ca2d6d29f/contracts/helpers/valueOracle/NoyaValueOracle.sol#L61-L64
https://github.com/code-423n4/2024-04-noya/blob/cc3854f634a72bd4a8b597021887088ca2d6d29f/contracts/helpers/valueOracle/oracles/ChainlinkOracleConnector.sol#L89-L103
https://github.com/code-423n4/2024-04-noya/blob/cc3854f634a72bd4a8b597021887088ca2d6d29f/contracts/helpers/valueOracle/oracles/ChainlinkOracleConnector.sol#L138-L141
https://github.com/code-423n4/2024-04-noya/blob/cc3854f634a72bd4a8b597021887088ca2d6d29f/contracts/helpers/valueOracle/oracles/ChainlinkOracleConnector.sol#L115-L135


# Vulnerability details

## Impact
There are tokens that have token/ETH but no token/USD Chainlink oracles currently in which these tokens have the in scope token behaviors described in https://code4rena.com/audits/2024-04-noya and should be supported by this protocol. To support these tokens, the ETH/USD Chainlink oracle can be used as a part of the route set by the following `NoyaValueOracle.updatePriceRoute` function for converting the token amount to ETH first and then converting the converted ETH amount to USD.

https://github.com/code-423n4/2024-04-noya/blob/cc3854f634a72bd4a8b597021887088ca2d6d29f/contracts/helpers/valueOracle/NoyaValueOracle.sol#L61-L64
```solidity
    function updatePriceRoute(address asset, address base, address[] calldata s) external onlyMaintainer {
        priceRoutes[asset][base] = s;
        emit UpdatedPriceRoute(asset, base, s);
    }
```

When converting the value of such token in ETH to USD, the following `ChainlinkOracleConnector.getValue` function would return `getValueFromChainlinkFeed(AggregatorV3Interface(primarySource), amount, getTokenDecimals(decimalsSource), isPrimaryInverse)`; because `amountIn` is in ETH's decimals that is 18 and `uintprice` and `sourceTokenUnit` are in USD's decimals that is 8 in the `getValueFromChainlinkFeed` function below, the value returned by the `ChainlinkOracleConnector.getValue` function is in ETH's decimals. Hence, when converting such token amount to ETH first and then to USD, the value of the corresponding token amount is in ETH's decimals instead of USD's decimals. In comparison, if the token/USD oracle could exist and be used for such token, the value of such token returned by `ChainlinkOracleConnector.getValue` function would be in USD's decimals instead of ETH's decimals. Thus, the value of such token is much higher when using the token/ETH and ETH/USD oracles indirectly comparing to using the token/USD oracle directly given if such token/USD oracle can become existent in the future. If such token/USD oracle does become existent and be used in the `ChainlinkOracleConnector` contract in the future, the value of such token amount calculated previously using the token/ETH and ETH/USD oracles indirectly would be incorrectly much higher than the value of the same token amount newly calculated using the token/USD oracle di
Malicious user can set receiver to blocked address to DOS withdrawls
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Summary

A malicious user can pass an address on the USDC or USDT blocklist to DOS the withdrawl process.

## Vulnerability Details

The [withdraw()](https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/accountingManager/AccountingManager.sol#L304-L316) function allows the user to set any arbitrary address to receive the withdrawn funds.

```
    // @audit : setting receiver to blacklisted address on USDC DOS withdrawls
    function withdraw(uint256 share, address receiver) public nonReentrant whenNotPaused {
        if (balanceOf(msg.sender) < share + withdrawRequestsByAddress[msg.sender]) {
            revert NoyaAccounting_INSUFFICIENT_FUNDS(
                balanceOf(msg.sender), share, withdrawRequestsByAddress[msg.sender]
            );
        }
        withdrawRequestsByAddress[msg.sender] += share;

        // adding the withdraw request to the withdraw queue
        withdrawQueue.queue[withdrawQueue.last] = WithdrawRequest(msg.sender, receiver, block.timestamp, 0, share, 0);
        emit RecordWithdraw(withdrawQueue.last, msg.sender, receiver, share, block.timestamp);
        withdrawQueue.last += 1;
    }
```

In the case of tokens with a blacklist like USDC; a malicious user can set the receiver to a blocked address to ensure the transfer of funds will fail and revert in [executeWithdraw()](https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/accountingManager/AccountingManager.sol#L396-L446).
Since the USDC contract will return false or revert when trying to transfer to a blacklisted address, the safeTransfer function will revert the entire transaction.

Because there is no function to remove items from the withdraw queue; the failing withdraw order cannot be removed from the withdraw queue this will completely brick withdrawls from the vault.

## Impact

The withdraw process, a core vault functionality will no longer work and users will lose access to their funds.

## Tools Used

Manual Review
Foundry Testing

## Recommendations
Do not allow users to set an arbitrary receiver but rather they can only withdraw to themselves.



## Assessed type

DoS
`BalancerFlashLoan::receiveFlashLoan()` does not work on zkSync
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact

FlashLoan does not work for zkSync

## Proof of Concept

```solidity
            for (uint256 i = 0; i < destinationConnector.length; i++) {
                // execute the transactions
                (bool success,) = destinationConnector[i].call{ value: 0, gas: gas[i] }(callingData[i]);
                require(success, "BalancerFlashLoan: Flash loan failed");
            }
```

`receiveFlashLoan()` functions to receive the flashloan made and send back the funds obtained from the flashloan. In this function, Noya applies a low level call (`call` function) to send flashloan funds to the balancer contract. Because there are differences between Ethereum and zkSync this creates a problem. It is stated in the docs about [zkSync integration](https://docs.zksync.io/build/developer-reference/differences-with-ethereum.html#call-staticcall-delegatecall), especially the difference between Ethereum and zkSync for low level calls, `call` function. 

> Additionally, there is no native support for passing Ether on zkSync Era, so it is handled by a special system contract called `MsgValueSimulator`. The simulator receives the callee address and Ether amount, performs all necessary balance changes, and then calls the callee.
> 

In this way, if there is direct call to the `destinationConnector`, it will make any flashloan will revert.

## Tools Used

Manual review

## Recommended Mitigation Steps

Consider following and applying existing differences, especially zkSync


## Assessed type

DoS
Failure to execute withdrawal of attacker who is blocked by `baseToken` causes executions of other users' subsequent withdrawals to fail
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-noya/blob/cc3854f634a72bd4a8b597021887088ca2d6d29f/contracts/accountingManager/AccountingManager.sol#L396-L446


# Vulnerability details

## Impact
According to https://code4rena.com/audits/2024-04-noya, tokens with blocklists, such as USDC, are in scope and should be supported by this protocol. For `baseToken` that is such token, an attacker can deposit only a very small `baseToken` amount and then withdraw all of her or his shares after such deposit is executed. Later, other users' withdrawals can be queued after the attacker's withdrawal. Before the following `executeWithdraw` function is called for these queued withdrawals, the attacker can maliciously make herself or himself be blocked by `baseToken`'s admin. Since calling the `executeWithdraw` function would execute the attacker's withdrawal before other users' withdrawals and the attacker is blocked by `baseToken`'s admin, executing `baseToken.safeTransfer(data.receiver, baseTokenAmount)` for the attacker's withdrawal reverts, which stops the operation in the `while` loop. Hence, all of the withdrawals queued after the attacker's withdrawal cannot be executed. As a result, the corresponding users lose their deposits.

https://github.com/code-423n4/2024-04-noya/blob/cc3854f634a72bd4a8b597021887088ca2d6d29f/contracts/accountingManager/AccountingManager.sol#L396-L446
```solidity
    function executeWithdraw(uint256 maxIterations) public onlyManager nonReentrant whenNotPaused {
        ...
        uint64 i = 0;
        uint256 firstTemp = withdrawQueue.first;

        uint256 withdrawFeeAmount = 0;
        uint256 processedBaseTokenAmount = 0;
        // loop through the withdraw queue and execute the withdraws
        while (
            currentWithdrawGroup.lastId > firstTemp
                && withdrawQueue.queue[firstTemp].calculationTime + withdrawWaitingTime <= block.timestamp
                && i < maxIterations
        ) {
            i += 1;
            WithdrawRequest memory data = withdrawQueue.queue[firstTemp];
            uint256 shares = data.shares;
            // calculate the base token amount that the user will receive based on the total available amount
            uint256 baseTokenAmount =
                data.amount * currentWithdrawGroup.totalABAmount / currentWithdrawGroup.totalCBAmountFullfilled;

            withdrawRequestsByAddress[data.owner] -= shares;
            _burn(data.owner, shares);

            processedBaseTokenAmount += data.amount;
            {
                uint256 feeAmount = baseTokenAmount * withdrawFee / FEE_PRECISION;
                withdrawFeeAmount += feeAmount;
                baseTokenAmount = baseTokenAmount - feeAmount;
            }

            baseToken.safeTransfer(data.receiver, baseTokenAmount);
            ...
            delete withdrawQueue.queue[firstTemp];
            // increment the first index of the withdraw queue
            firstTemp += 1;
        }
        totalWith
`whenNotPaused` modifier is not properly implemented and applied breaking core protocol functionality
Audit: 2024-04-noya-findings
Severity: HIGH

# Lines of code

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/d947fb056d6a7eb099013076ac5ea5a69e9fec06/contracts/utils/Pausable.sol#L55
https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/connectors/BalancerConnector.sol#L70
https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/connectors/BalancerConnector.sol#L115-L119


# Vulnerability details

## Impact

- The `whenNotPaused` modifier is not properly applied in the protocol preventing the watchers from pausing some parts of the protocol contrary to the documentation breaking core protocol functionality

- contrary to the documentation as shown below, the watchers cannot choose to pause either the protocol or chain or asset. They can pause only the protocol.






## Summary
From the Noya documentation
[Enhanced watchers](https://docs.noya.ai/noyas-infrastructure/why-rebuild-everything/strategy-security-watchers#noyas-enhanced-watchers)
> These watchers autonomously execute critical transactions, pause or halt operations when necessary,

[risk management point 3](https://docs.noya.ai/noya-protocol/noyas-building-blocks/risk-management):
> The WATCHERs ... have the ability to pause withdrawals for a certain asset, chain, **or** protocol..


The problem is 
- Firstly, the `whenNotPaused` modifier is applied only in the `AccountingManager.sol` contract meanwhile there are other critical functions for instance [`BalancerConnector::openPosition()`](https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/connectors/BalancerConnector.sol#L70), [`BalancerConnector::decreasePosition(...)`](https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/connectors/BalancerConnector.sol#L115-L119) (to mention a few) that are missing this critical modifier that gives control to the watchers when they need to act. This means that the the positions can still be open in the strategy when the protocol is paused. All the  strategies are missing the `whenNotPaused` modifier hence the watchers have no control in the strategy connector. Hence the watchers will not have full control of the protocol is anything goes wrong.

- secondly, because the `AccountingManager.sol` contract inherits from the openzeppelin `Pausable` contract, watchers can only pause the protocol as a whole without the ability to pause a particular asset or chain due to the implementation of the [`whenNotPaused`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/d947fb056d6a7eb099013076ac5ea5a69e9fec06/contracts/utils/Pausable.sol#L55) modifier, only the protocol can be paused and as such the watchers do not have the autonomy to pause a particular asset or chain.




## Tools Used
Manual review

## Recommended Mitigation Steps

- Implement the `whenNotPaused` Modifier in the contract giving the watchers the autonomy to choose between pausing either an as
Management Fees can be skewed
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
https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/accountingManager/AccountingManager.sol#L517


# Vulnerability details

## Impact
Any user can transfer their shares to either the `managerFeeReceiver` or the `performanceFeeReceiver` and distort the current shares. 

## Proof of Concept
During the collection of the Management fees ,[current shares](https://github.com/code-423n4/2024-04-noya/blob/9c79b332eff82011dcfa1e8fd51bad805159d758/contracts/accountingManager/Accounting) is calculated using the balances of the manager and the performance fee receiver ,these two balances can be updated through either a call to `transfer` or `transferFrom`( the two external functions).As a result of this ,balances can be updated arbitrarily which can lead to the `managementFeeAmount` being lower than expected.The only way that these specific balances should be updated is through `_mint`.The POC below demonstrates this issue :

```solidity "
    function testManagementFees() public {
        console.log("-----------Base Workflow--------------");


        uint256 _amount = 10_000 * 1e6;
        _dealWhale(baseToken, address(owner), address(0x1AB4973a48dc892Cd9971ECE8e01DcC7688f8F23), _amount);


        vm.startPrank(owner);
        accountingManager.setFeeReceivers(
            withdrawFeeReceiver, address(performanceFeeReceiver), address(managementFeeReceiver)
        );
        // -------------------- ---------- deposit ------------------------------
        SafeERC20.forceApprove(IERC20(USDC), address(accountingManager), _amount);
        accountingManager.deposit(address(owner), _amount, address(0));


        accountingManager.setFees(0, 0, 1e5);


        accountingManager.calculateDepositShares(10);


        vm.warp(block.timestamp + 35 minutes);


        accountingManager.executeDeposit(10, connector, "");

         //@audit allow owner or any user who owns shares to transfer their shares 
         to increase any of the fee receivers' balance;
         uint256 oBal =accountingManager.balanceOf(owner);
          //@audit just testing with mR ,can do the same with performance fee. 
          receiver 
         address mR=accountingManager.managementFeeReceiver();
      
          vm.startPrank(address(owner));
         //@audit this will allow the below assertions to fail;
           
           /* vm.expectRevert(); */ //after issue is resolved this should revert;
           accountingManager.transfer(mR,oBal);

    
        (uint256 sharesAmount, uint256 timePassed) = accountingManager.collectManagementFees();


        assertEq(sharesAmount, 0, "sharesAmount is not correct");
        assertEq(timePassed, 0, "timePassed is not correct");


        vm.warp(block.timestamp + 5 days);
        uint256 expectedShares = uint256(
            accountin