xanoutas/solidity-security-findings huggingface dataset:
NFTFloorOracle's assets will use old prices if added back after removal
Audit: 2022-11-paraspace-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/misc/NFTFloorOracle.sol#L296-L305


# Vulnerability details

`assetFeederMap` mapping has elements of the `FeederRegistrar` structure type, that contains nested `feederPrice` mapping. When an asset is being removed with _removeAsset(), its `assetFeederMap` entry is deleted with the plain `delete assetFeederMap[_asset]` operation, which leaves `feederPrice` mapping intact.

This way if the asset be added back after removal its old prices will be reused via _combine() given that their timestamps are fresh enough and the corresponding feeders stay active.

## Impact

Old prices can be irrelevant in the big enough share of cases. The asset can be removed due to its internal issues that are usually coupled with price disruptions, so it is reasonable to assume that recent prices of a removed asset can be corrupted for the same reason that caused its removal.

Nevertheless these prices will be used as if they were added for this asset after its return. Recency logic will work as usual, so the issue is conditional on `config.expirationPeriod` being substantial enough, which might be the case for all illiquid assets.

Net impact is incorrect valuation of the corresponding NFTs, that can lead to the liquidation of the healthy accounts, which is the permanent loss of the principal funds for their owners. However, due to prerequisites placing the severity to be medium.

## Proof of Concept

_removeAsset() will `delete` the `assetFeederMap` entry, setting its elements to zero:

https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/misc/NFTFloorOracle.sol#L296-L305

```solidity
    function _removeAsset(address _asset)
        internal
        onlyWhenAssetExisted(_asset)
    {
        uint8 assetIndex = assetFeederMap[_asset].index;
        delete assets[assetIndex];
        delete assetPriceMap[_asset];
        delete assetFeederMap[_asset];
        emit AssetRemoved(_asset);
    }
```

Notice, that `assetFeederMap` is the mapping with `FeederRegistrar` elements:

https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/misc/NFTFloorOracle.sol#L86-L88

```solidity
    /// @dev Original raw value to aggregate with
    // the NFT contract address -> FeederRegistrar which contains price from each feeder
    mapping(address => FeederRegistrar) public assetFeederMap;
```

`FeederRegistrar` is a structure with a nested `feederPrice` mapping.

`feederPrice` nested mapping will not be deleted with `delete assetFeederMap[_asset]`:

https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/misc/NFTFloorOracle.sol#L32-L41

```solidity
struct FeederRegistrar {
    // if asset registered or not
    bool registered;
    // index in asset list
    uint8 i
CENTRALIZATION RISK: ADMIN HAS PRIVILEGES: POOL ADMIN CAN STEAL UNDERLYING OF A NTOKEN
Audit: 2022-11-paraspace-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/tokenization/NToken.sol#L127-L166
https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/tokenization/base/MintableIncentivizedERC721.sol#L45-L54


# Vulnerability details

## Impact
Admin has privileges: pool admin can steal all the underlying assets of a NToken contract.
There are numerous methods that the admin could apply to rug pull the `NToken.sol` and take all its ERC20, ERC1155 & ERC721 underlying tokens, making users unable to recover their assets.

Note the modifier below; the pool admin set in `aclManager` can access all the privileged functions on `NToken.sol` and its parent contract `MintableIncentivizedERC721.sol`:
```
modifier onlyPoolAdmin() {
        IACLManager aclManager = IACLManager(
            _addressesProvider.getACLManager()
        );
        require(
            aclManager.isPoolAdmin(msg.sender),
            Errors.CALLER_NOT_POOL_ADMIN
        );
        _;
    }
```

The admin can:

```
steal ERC20 assets calling rescueERC20() from NToken.sol 
steal ERC1155 assets calling rescueERC1155() from NToken.sol 
steal ERC721 assets calling rescueERC721() from NToken.sol 
```

## Recommended Mitigation Steps
Without significant redesign it is not possible to avoid the admin being able to rug pull each NToken.

As a result the recommendation is to set all admin functions behind either a timelocked DAO or at least a timelocked multisig contract.
UNRELIABLE MEDIAN CUMULATIVEPRICES
Audit: 2022-11-paraspace-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/misc/NFTFloorOracle.sol#L202
https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/misc/NFTFloorOracle.sol#L229
https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/misc/NFTFloorOracle.sol#L429


# Vulnerability details

## Impact
`twap` determined by a median cumulativePrice could be tricky devoid of proper measures in place.

## Proof of Concept
Here are the factors that could affect its supposed value:

- The admin could bypass all efforts rendered by the feeders via the following code lines:

[NFTFloorOracle.sol#L201-L205](https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/misc/NFTFloorOracle.sol#L201-L205)

```
        bool dataValidity = false;
        if (hasRole(DEFAULT_ADMIN_ROLE, msg.sender)) {
            _finalizePrice(_asset, _twap);
            return;
        }
```
- Results could be skewed with small sample sizes:

[NFTFloorOracle.sol#L9](https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/misc/NFTFloorOracle.sol#L9)

```
uint8 constant MIN_ORACLES_NUM = 3;
```
- Results correctly returned only when `feeders.length` is an odd integer:

[NFTFloorOracle.sol#L429](https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/misc/NFTFloorOracle.sol#L429)

```
        return (true, validPriceList[validNum / 2]);
```
## Tools Used
Manual

## Recommended Mitigation Steps
It is recommended that:
- Admin would assume the same role privilege as other updaters when setting prices.
- `MIN_ORACLES_NUM` be assigned a higher integer.
- the return logic of `_combine()` be refactored to accommodate situations when `feeders.length` is an even integer.
the MAX_DEVIATION_RATE restriction can reject valid NFT price oracle.
Audit: 2022-11-paraspace-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/misc/NFTFloorOracle.sol#L14
https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/misc/NFTFloorOracle.sol#L371
https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/misc/NFTFloorOracle.sol#L206


# Vulnerability details

## Impact

the MAX_DEVIATION_RATE restriction can reject valid NFT price oracle.

## Proof of Concept

the MAX_DEVIATION_RATE is hardcoded in the NFTFloorPrice.sol

```solidity
//reject when price increase/decrease 1.5 times more than original value
uint128 constant MAX_DEVIATION_RATE = 150;
```

then we set:

```solidity
_setConfig(EXPIRATION_PERIOD, MAX_DEVIATION_RATE);
```

the parameter is used below:

```solidity
function _checkValidity(address _asset, uint256 _twap)
	internal
	view
	returns (bool)
{
	require(_twap > 0, "NFTOracle: price should be more than 0");
	PriceInformation memory assetPriceMapEntry = assetPriceMap[_asset];
	uint256 _priorTwap = assetPriceMapEntry.twap;
	uint256 _updatedAt = assetPriceMapEntry.updatedAt;
	uint256 priceDeviation;
	//first price is always valid
	if (_priorTwap == 0 || _updatedAt == 0) {
		return true;
	}
	priceDeviation = _twap > _priorTwap
		? (_twap * 100) / _priorTwap
		: (_priorTwap * 100) / _twap;

	// config maxPriceDeviation as multiple directly(not percent) for simplicity
	if (priceDeviation >= config.maxPriceDeviation) {
		return false;
	}
	return true;
}
```

note the line:

```solidity
	// config maxPriceDeviation as multiple directly(not percent) for simplicity
	if (priceDeviation >= config.maxPriceDeviation) {
		return false;
	}
	return true;
```

if the price moves below or above MAX_DEVIATION_RATE, the price update is rejected.

```solidity
dataValidity = _checkValidity(_asset, _twap);
require(dataValidity, "NFTOracle: invalid price data");
```

but here is the issue: NFT print is very volatitle, if can increase by 200% of drop 60%, in the case when the NFT price does drop or increase, the onchain data rejects the valid price.

Consider this case:

1. a NFT is worth 80 ETH.
2. suddenly the NFT project rugs and the price drops to 10 ETH.
3. The deviation is too high and there is no way to adjust MAX_DEVIATION_RATE and the valid price cannot be recorded as TWAP price on-chain.
4. User's position cannot liquidated and user consider to borrow as if the NFT worht 80 ETH and leave the project in a insovlent position.

## Tools Used

Manual Review

## Recommended Mitigation Steps

Check timestamp of the updated price, make the MAX_DEVIATION_RATE adjustable.
Admin can set any price in `NFTFloorOracle::setPrice`
Audit: 2022-11-paraspace-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-paraspace/blob/6542d6e946762fc7a914a3f6f2e08fcbbf6c8a13/paraspace-core/contracts/misc/NFTFloorOracle.sol#L195


# Vulnerability details

### Proof of Concept

In the beginning of `setPrice` there is the following code

```solidity
if (hasRole(DEFAULT_ADMIN_ROLE, msg.sender)) {
            _finalizePrice(_asset, _twap);
            return;
}
```

which basically translates to the option for an admin to set any price immediately, even 0 or some very high number. This is a centralisation vulnerability as we are seeing a lot of compromised/malicious admin/owner attacks in the space.

### Severity

The impact of this issue if exploited is High but the likelihood is Low since it requires a malicious/compromised owner. This results in a Medium severity.

### Recommendation

Remove the option for an admin to set any price without input validation.
Missing `whenNotPaused` modifier in `NFTFloorOracle::getPrice`
Audit: 2022-11-paraspace-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-paraspace/blob/6542d6e946762fc7a914a3f6f2e08fcbbf6c8a13/paraspace-core/contracts/misc/NFTFloorOracle.sol#L236


# Vulnerability details

### Proof of Concept

Currently the `NFTFloorOracle` allows for an asset to be paused and the `setPrice` functionality has `whenNotPaused(_asset)` modifier. This modifier is missing in the `getPrice` functionality, so even if an asset was paused any contract calling `getPrice` will get the stale price successfully.

### Severity

The impact of this issue is High because protocol will be running with a stale price, but the likelihood is Medium because it needs an asset to be paused. This results in Medium severity.

### Recommendation

Add `whenNotPaused(_asset)` modifier to `getPrice`
Centralization risk: admin can with rug the project by removing asset and price manipulation on oracle.
Audit: 2022-11-paraspace-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/misc/ParaSpaceOracle.sol#L21
https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/configuration/ACLManager.sol#L14
https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/tokenization/NToken.sol#L29


# Vulnerability details

## Impact

admin can with rug the project by removing asset and price manipulation on oracle.

## Proof of Concept

As we see what happens in ankr when the admin is compromised, the centralization risk should not be taken lightly.

https://rekt.news/ankr-helio-rekt/

If the admin is compromised in paraspace, the admin can set the reward controller to invalid address, and can call resuceERC20 and rescue721 token from NToken to remove asset with no restriction or add or disable address in ACL manager directly.

```solidity
28 results - 11 files

paraspace-core\contracts\protocol\configuration\PriceOracleSentinel.sol:
  20       **/
  21:     modifier onlyPoolAdmin() {
  22          IACLManager aclManager = IACLManager(

  92          external
  93:         onlyPoolAdmin
  94      {

paraspace-core\contracts\protocol\pool\PoolConfigurator.sol:
   31       **/
   32:     modifier onlyPoolAdmin() {
   33:         _onlyPoolAdmin();
   34          _;

   91      /// @inheritdoc IPoolConfigurator
   92:     function dropReserve(address asset) external override onlyPoolAdmin {
   93          _pool.dropReserve(asset);

   99          ConfiguratorInputTypes.UpdatePTokenInput calldata input
  100:     ) external override onlyPoolAdmin {
  101          ConfiguratorLogic.executeUpdatePToken(_pool, input);

  106          ConfiguratorInputTypes.UpdateNTokenInput calldata input
  107:     ) external override onlyPoolAdmin {
  108          ConfiguratorLogic.executeUpdateNToken(_pool, input);

  113          ConfiguratorInputTypes.UpdateDebtTokenInput calldata input
  114:     ) external override onlyPoolAdmin {
  115          ConfiguratorLogic.executeUpdateVariableDebtToken(_pool, input);

  186          override
  187:         onlyPoolAdmin
  188      {

  377  
  378:     function _onlyPoolAdmin() internal view {
  379          IACLManager aclManager = IACLManager(

paraspace-core\contracts\protocol\pool\PoolParameters.sol:
   63       **/
   64:     modifier onlyPoolAdmin() {
   65:         _onlyPoolAdmin();
   66          _;

   75  
   76:     function _onlyPoolAdmin() internal view virtual {
   77          require(

  200          uint256 amountOrTokenId
  201:     ) external virtual override onlyPoolAdmin {
  202          PoolLogic.executeRescueTokens(assetType, token, to, amountOrTokenId);

paraspace-core\contracts\protocol\tokenization\DelegationAwarePToken.sol:
  33       **/
  34:     function delegateUnderlyingTo(address delegatee) external onlyPoolAdm
User health factor will be incorrect if a user has existing sAPE before depositing
Audit: 2022-11-paraspace-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/libraries/logic/ValidationLogic.sol#L414-L440


# Vulnerability details

## Impact

Liquidation of assets when account isn't actually under-collateralized 

## Proof of Concept

    function validateSetUseERC20AsCollateral(
        DataTypes.ReserveCache memory reserveCache,
        uint256 userBalance
    ) internal pure {
        require(userBalance != 0, Errors.UNDERLYING_BALANCE_ZERO);

        IXTokenType xToken = IXTokenType(reserveCache.xTokenAddress);

        //@audit user is unable to change status of sApe manually
        require(
            xToken.getXTokenType() != XTokenType.PTokenSApe,
            Errors.SAPE_NOT_ALLOWED
        );

        (
            bool isActive,
            ,
            ,
            bool isPaused,
            DataTypes.AssetType reserveAssetType
        ) = reserveCache.reserveConfiguration.getFlags();

        require(
            reserveAssetType == DataTypes.AssetType.ERC20,
            Errors.INVALID_ASSET_TYPE
        );
        require(isActive, Errors.RESERVE_INACTIVE);
        require(!isPaused, Errors.RESERVE_PAUSED);
    }

When depositing a BAYC, MAYC and BAKC sAPE is not immediately enabled as collateral. It is only enabled when a user calls PoolApeStaking#borrowApeAndStake. Otherwise if the user tries to enable manually enable it as a collateral if will revert in 
ValidationLogic#validateSetUseERC20AsCollateral. The result is that if a user deposits an NFT already staked with APE, then it won't be accounted for in their health factor. This is possible because when APE is staked it is locked in the staking contract but the NFT is free to be transferred [link](https://www.msn.com/en-us/money/other/apecoin-holders-are-losing-their-staked-tokens-when-paired-bored-apes-get-sold/ar-AA14YVZ3). This can lead to users being unfairly liquidated and their collateral being sold for a large loss. They will also be force to pay a liquidation incentive even though their position isn't actually under-collateralized 

## Tools Used

Manual Review

## Recommended Mitigation Steps

When a user deposits a BAYC, MAYC or BAKC the contract should check if the NFT already has sAPE. If it does then the contract should automatically enable the sAPE as collateral.
`DEFAULT_ADMIN_ROLE` is able to manipulate NFT prices for profit
Audit: 2022-11-paraspace-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/misc/NFTFloorOracle.sol#L202-L205


# Vulnerability details

## Impact
The `DEFAULT_ADMIN_ROLE` user (single user) has the permission to set any prices of NFTs without any restriction according to [the price setting logic](https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/misc/NFTFloorOracle.sol#L202-L205). This is a serious risk to users as their assets's price can be controlled by a single user. The account with `DEFAULT_ADMIN_ROLE` can manipulate the NFT prices for purpose.

## Proof of Concept
The account (with `DEFAULT_ADMIN_ROLE`) call the `setPrice(anyNFT, anyPrice)` or `setMultiplePrices()` to set NFT's prices. After the prices have been set, the account can immediately perform some profitting actions, and set the prices back to normal, finally complete the transaction.


## Tools Used
manual audit

## Recommended Mitigation Steps
To increase the security by introduce multi-sig or DAO, or enhance the business logic so that there is no single account can change the price without verification. The simplest way currently is to remove [the risky logic](https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/misc/NFTFloorOracle.sol#L202-L205) and let a set of price feeders decide the price.
No `nonReentrant` modifier in `PoolApeStaking.repayAndSupply`.
Audit: 2022-11-paraspace-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/pool/PoolApeStaking.sol#L371-L377


# Vulnerability details

## Impact
Attackers can re-enter due to lack of `nonReentrant` modifier.

## Proof of Concept
There is no `nonReentrant` modifier in `PoolApeStaking.repayAndSupply`.
In `repayAndSupply`, `BorrowLogic.executeRepay` is called, and then `IPToken.handleRepayment` is called.
`handleRepayment` logic is not implemented inside this platform, so it can be abused by attackers, and there is an opportunity to re-enter.
So `nonReentrant` modifier is needed in `repayAndSupply`.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Add `nonReentrant` modifier to `PoolApeStaking.repayAndSupply`.
Interactions with AMMs do not use deadlines for operations
Audit: 2022-11-paraspace-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/tokenization/NTokenUniswapV3.sol#L51-L76


# Vulnerability details

## Impact
From a judge's contest in a previous [contest](https://code4rena.com/reports/2022-06-canto-v2/#m-01-stableswap---deadline-do-not-work):
```md
Because Front-running is a key aspect of AMM design, deadline is a useful tool to ensure that your tx cannot be “saved for later”.

Due to the removal of the check, it may be more profitable for a miner to deny the transaction from being mined until the transaction incurs the maximum amount of slippage.
```

Most of the functions that interact with AMM pools do not have a deadline parameter, but specifically the one shown below is passing `block.timestamp` to a pool, which means that whenever the miner decides to include the txn in a block, it will be valid at that time, since `block.timestamp` will _be_ the current timestamp.

## Proof of Concept
```solidity
File: /paraspace-core/contracts/protocol/tokenization/NTokenUniswapV3.sol   #1

51       function _decreaseLiquidity(
52           address user,
53           uint256 tokenId,
54           uint128 liquidityDecrease,
55           uint256 amount0Min,
56           uint256 amount1Min,
57           bool receiveEthAsWeth
58       ) internal returns (uint256 amount0, uint256 amount1) {
59           if (liquidityDecrease > 0) {
60               // amount0Min and amount1Min are price slippage checks
61               // if the amount received after burning is not greater than these minimums, transaction will fail
62               INonfungiblePositionManager.DecreaseLiquidityParams
63                   memory params = INonfungiblePositionManager
64                       .DecreaseLiquidityParams({
65                           tokenId: tokenId,
66                           liquidity: liquidityDecrease,
67                           amount0Min: amount0Min,
68                           amount1Min: amount1Min,
69 @>                        deadline: block.timestamp
70                       });
71   
72               INonfungiblePositionManager(_underlyingAsset).decreaseLiquidity(
73                   params
74               );
75           }
76:  
```
https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/tokenization/NTokenUniswapV3.sol#L51-L76

A malicious miner can hold the transaction, which may be being done in order to free up capital to ensure that there are funds available to do operations to prevent a liquidation. It is highly likely that a liquidation is more profitable for a miner to mine, with its associated follow-on transactions, than to allow the decrease of liquidity. A miner can also just hold it until maximum slippage is incurred, as the judge stated.

## Tools Used
Code inspection

## Recommended Mitigation Steps
Add deadline arguments to all functions that inte
`ParaSpaceOracle.getAssetPrice` can return stale price
Audit: 2022-11-paraspace-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/misc/ParaSpaceOracle.sol#L127-L132


# Vulnerability details

## Impact
When we get asset price from `ParaSpaceOracle.getAssetPrice`, it can return stale price due to an error.

## Proof of Concept
```solidity
        if (address(source) != address(0)) {
            price = uint256(source.latestAnswer()); //@audit wrong conversion
        }
        if (price == 0 && address(_fallbackOracle) != address(0)) {
            price = _fallbackOracle.getAssetPrice(asset);
        }
```
When `source.latestAnswer` returns negative value for stale price, `price` will be positive after it is converted to `uint256`.
So we can't try fallback oracle in the next if statement, and `ParaSpaceOracle.getAssetPrice` will return stale price.

## Tools Used
Manual Review

## Recommended Mitigation Steps

Check `price` before it is converted to `uint256`.