xanoutas/solidity-security-findings huggingface dataset:
Borrower isolated asset may get liquidated
Audit: 2022-11-paraspace-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/libraries/logic/LiquidationLogic.sol#L300-L302
https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/libraries/logic/LiquidationLogic.sol#L383-L384
https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/libraries/logic/LiquidationLogic.sol#L585-L594


# Vulnerability details

## Impact

Borrower may decided to isolate some asset stored in the protocol by not using them as collateral.

https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/libraries/logic/SupplyLogic.sol#L610-L614

```solidity
        if (useAsCollateral) {
            userConfig.setUsingAsCollateral(reserve.id, true);
            emit ReserveUsedAsCollateralEnabled(asset, msg.sender);
        } else {
            userConfig.setUsingAsCollateral(reserve.id, false);
```

However, upon a liquidation the borrower's asset can be forced to set as collateral. The user might not expect some asset that he explicitly set to not use as collateral being liquidated.

https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/libraries/logic/LiquidationLogic.sol#L585-L594

```solidity
        if (!userConfig.isUsingAsCollateral(vars.liquidationAssetReserveId)) {
            userConfig.setUsingAsCollateral(
                vars.liquidationAssetReserveId,
                true
            );
            emit ReserveUsedAsCollateralEnabled(
                params.liquidationAsset,
                params.borrower
            );
        }
```

## Proof of Concept

https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/libraries/logic/LiquidationLogic.sol#L300-L302

```solidity
        DataTypes.UserConfigurationMap storage userConfig = usersConfig[
            params.borrower
        ];
```

https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/libraries/logic/LiquidationLogic.sol#L383-L384

```solidity
        _supplyNewCollateral(reservesData, userConfig, params, vars);

```
Anyone Can Remove Feeder From NFTFloorOracle
Audit: 2022-11-paraspace-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Proof of Concept

The `NFTFloorOracle.removeFeeder` function allows the caller to remove a feeder from the NFTFloorOracle. However, this function lacks of access control, and thus anyone can call this function to remove all feeders from the NFTFloorOracle.

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

```solidity
File: NFTFloorOracle.sol
165:     /// @notice Allows owner to remove feeder.
166:     /// @param _feeder feeder to remove
167:     function removeFeeder(address _feeder)
168:         external
169:         onlyWhenFeederExisted(_feeder)
170:     {
171:         _removeFeeder(_feeder);
172:     }
```

The `onlyWhenFeederExisted` modifier only checks if the feeder exists. It does not check if the caller is the owner.

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

```solidity
File: NFTFloorOracle.sol
127:     modifier onlyWhenFeederExisted(address _feeder) {
128:         require(_isFeederExisted(_feeder), "NFTOracle: feeder not existed");
129:         _;
130:     }
```
https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/misc/NFTFloorOracle.sol#L274

```solidity
File: NFTFloorOracle.sol
274:     function _isFeederExisted(address _feeder) internal view returns (bool) {
275:         return feederPositionMap[_feeder].registered;
276:     }
```

Similarly, the `_removeFeeder` internal function only checks if the feeder exists. None of the code check if the caller is the owner.

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

```solidity
File: NFTFloorOracle.sol
326:     function _removeFeeder(address _feeder)
327:         internal
328:         onlyWhenFeederExisted(_feeder)
329:     {
330:         uint8 feederIndex = feederPositionMap[_feeder].index;
331:         if (feederIndex >= 0 && feeders[feederIndex] == _feeder) {
332:             feeders[feederIndex] = feeders[feeders.length - 1];
333:             feeders.pop();
334:         }
335:         delete feederPositionMap[_feeder];
336:         revokeRole(UPDATER_ROLE, _feeder);
337:         emit FeederRemoved(_feeder);
338:     }
```

Thus, anyone can call `NFTFloorOracle.removeFeeder` function.

## Impact

Anyone can remove all feeders from the Oracle. Thus, no one will be able to feed the latest prices to NFTFloorOracle. The entire protocol relies on NFTFloorOracle to function properly. Without the latest price, the market value of NFT cannot be determined, and the collateral will end up being overvalued or undervalued. This causes ass
Asset index can realistically overflow in `NFTFloorOracle`
Audit: 2022-11-paraspace-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Due to an overflow of the assets index a wrong asset can be removed from the `assets` array, which may impair integration and off-chain monitors that read the array to get the list of supported assets.
## Proof of Concept
`NFTFloorOracle` maintains the list of asset for which feeders are allowed to report prices. Assets are stored in two data structures: the `assets` array and the `assetFeederMap` mapping ([NFTFloorOracle.sol#L83-L88](https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/misc/NFTFloorOracle.sol#L83-L88)). The values in the mapping have the `index` key to link assets from the mapping to their position in the array ([NFTFloorOracle.sol#L282-L284](https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/misc/NFTFloorOracle.sol#L282-L284)):
```solidity
assetFeederMap[_asset].registered = true;
assets.push(_asset);
assetFeederMap[_asset].index = uint8(assets.length - 1);
```

However, the maximal value of the index is only 255. Moreover, when an asset is removed the `assets` array is not shrinked ([NFTFloorOracle.sol#L300-L303](https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/misc/NFTFloorOracle.sol#L300-L303)):
```solidity
uint8 assetIndex = assetFeederMap[_asset].index;
delete assets[assetIndex];
delete assetPriceMap[_asset];
delete assetFeederMap[_asset];
```

Thus, for the index to overflow, the contract needs to have 256 assets *historically*, not currently. It seems realistic that over multiple years the contract will have more than 256 assets, including removed ones.

```solidity
// paraspace-core/test/_oracle_nft_floor_price.spec.ts
it("allows asset index to overflow [AUDIT]", async () => {
  const { nftFloorOracle } = testEnv;

  const numToAddress = (num) => '0x' + num.toString(16).padStart(40, '0');

  // There are 10 assets, so we'll add 246 more.
  const length = 246;
  const assets = Array.from({ length }, (_, i) => numToAddress(0x31337 + i));

  await nftFloorOracle.addAssets(assets);

  // The last asset has index 255.
  const lastAsset = assets[assets.length - 1];
  expect((await nftFloorOracle.assetFeederMap(lastAsset)).index).to.eq(255);

  // Removing an asset won't reduce the index.
  await nftFloorOracle.removeAsset(lastAsset);

  // Adding a new asset to overflow the index.
  const newAsset = numToAddress(0x31337 + length + 1);
  await nftFloorOracle.addAssets([newAsset]);
  expect((await nftFloorOracle.assetFeederMap(newAsset)).index).to.eq(0);

  const firstAsset = await nftFloorOracle.assets(0);
  expect(firstAsset).to.eq("0xD6C850aeBFDC46D7F4c207e445cC0d6B0919BDBe");

  // Removing the last asset...
  await nftFloorO
Buying from marketplace with borrowing may create bad debt for the buyer
Audit: 2022-11-paraspace-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/libraries/logic/MarketplaceLogic.sol#L114
https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/libraries/logic/ValidationLogic.sol#L1065-L1069


# Vulnerability details

## Impact
Buying NFT tokens from marketplaces using a credit from Paraspace can create bad debt for the buyer, which can manifest itself differently:
1. If a user doesn't have enough collateral to cover the bad debt, buying an NFT from the marketplace will fail.
1. If a user does have enough collateral, buying an NFT from the marketplace using a credit will increase their debt by an amount that's not covered by the value of the bought NFT. Which means that the purchase has generated more debt than the bought collateral value allows.
## Proof of Concept
The protocol implements a marketplace that allows users to buy NFTs from supported external marketplaces (LooksRare, Seaport, X2Y2) using a credit (optionally). Buying an NFT with a credit looks like that:
1. user calls `buyWithCredit` and specifies: the token they want to buy and the amount of credit they want to take ([PoolMarketplace.sol#L71-L87](https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/pool/PoolMarketplace.sol#L71-L87));
1. the protocol transfers the requested credit to the user ([MarketplaceLogic.sol#L443](https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/libraries/logic/MarketplaceLogic.sol#L443));
1. user contributes the downpayment ([MarketplaceLogic.sol#L400-L413](https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/libraries/logic/MarketplaceLogic.sol#L400-L413));
1. the total amount (credit + downpayment) is used to buy an NFT ([MarketplaceLogic.sol#L132-L140](https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/libraries/logic/MarketplaceLogic.sol#L132-L140));
1. the price of the NFT is set by the maker order, which is passed to the `buyWithCredit` function via the `payload` argument ([MarketplaceLogic.sol#L76-L77](https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/libraries/logic/MarketplaceLogic.sol#L76-L77), [LooksRareAdapter.sol#L59-L60](https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/misc/marketplaces/LooksRareAdapter.sol#L59-L60));
1. the bought NFT is supplied as a collateral to the buyer ([MarketplaceLogic.sol#L502-L527](https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/prot
Feeders can dictate the current price
Audit: 2022-11-paraspace-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Feeders can manipulate NFTFloorOracle prices for their benefit, for example to reduce the price of an asset and liquidate a big borrower or to keep the price of an asset a feeder is depositing to increase their collateral value.
## Proof of Concept
`NFTFloorOracle` allows trusted feeders to set prices of NFT tokens. The `setPrice` function accumulates prices from multiple feeders (the minimal number of feeders is 3) and choose the *median* of them ([NFTFloorOracle.sol#L413-L429](https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/misc/NFTFloorOracle.sol#L413-L429)):
```solidity
for (uint256 i = 0; i < feederSize; i++) {
    PriceInformation memory priceInfo = feederRegistrar.feederPrice[
        feeders[i]
    ];
    if (priceInfo.updatedAt > 0) {
        uint256 diffBlock = currentBlock - priceInfo.updatedAt;
        if (diffBlock <= config.expirationPeriod) {
            validPriceList[validNum] = priceInfo.twap;
            validNum++;
        }
    }
}
if (validNum < MIN_ORACLES_NUM) {
    return (false, assetPriceMap[_asset].twap);
}
_quickSort(validPriceList, 0, int256(validNum - 1));
return (true, validPriceList[validNum / 2]);
```

This allows a malicious feeder:
1. to report a price that's the median (prices reported by other feeders and the index of the next median price can be known in advance);
1. to adjust the price for their benefit (ensuring it's still stays the median one);
1. set the price they reported as the current one and affect collateral value calculation.

There are multiple ways how this can be exploited:
1. a malicious feeder may run a liquidation bot and manipulate prices to liquidate users with their bot;
1. a malicious feeder may be bribed by liquidation bot runners to manipulate prices for the benefit of the bot runners;
1. a malicious feeder may always report a higher price for their collateral tokens.

Consider the following test case:
1. users 1, 2, and 3 have reported prices;
1. the current price is 15, which is the median price;
1. user4 can choose which price to report: they can pick any price within the range `15 <= price <= 30`;
1. user4 reports whatever price is more beneficial for them.

```solidity
// paraspace-core/test/_oracle_nft_floor_price.spec.ts
it("allows feeder to set and finalize their price [AUDIT]", async () => {
  const { nftFloorOracle, users: [user1, user2, user3, user4] } = testEnv;

  await nftFloorOracle.connect(user1.signer).setPrice(mockToken.address, parseEther("30").toString());
  await nftFloorOracle.connect(user2.signer).setPrice(mockToken.address, parseEther("10").toString());
  await nftFloorOracle.connect(user3.signer).setPrice(mockToken.address, parseEther("15").toString());

  // Currently reported prices: [10, 15
Feeder index is not updated in `feederPositionMap` after a feeder is removed
Audit: 2022-11-paraspace-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
The NFTFloorOracle admin may not be able to remove a malicious or a hacked feeder due to the flawed feeder indexes updating. The malicious feeder may harm the protocol before the admin adds another feeder, which will unlock removal of the malicious one.
## Proof of Concept
The `NFTFloorOracle` contract maintains a list of allowed feeders who provide prices to the contract. Feeders are stored in two data structures: the `feeders` array and the `feederPositionMap` mapping ([NFTFloorOracle.sol#L76-L81](https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/misc/NFTFloorOracle.sol#L76-L81)). The values in the mapping have the `index` key to link feeders from the mapping to their position in the array ([NFTFloorOracle.sol#L311-L313](https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/misc/NFTFloorOracle.sol#L311-L313)):
```solidity
feeders.push(_feeder);
feederPositionMap[_feeder].index = uint8(feeders.length - 1);
feederPositionMap[_feeder].registered = true;
```

When a feeder is removed, it's being replaced by the last feeder in the array. However, that feeder's index is not updated in the mapping ([NFTFloorOracle.sol#L330-L335](https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/misc/NFTFloorOracle.sol#L330-L335)):
```solidity
uint8 feederIndex = feederPositionMap[_feeder].index;
if (feederIndex >= 0 && feeders[feederIndex] == _feeder) {
    feeders[feederIndex] = feeders[feeders.length - 1];
    feeders.pop();
}
delete feederPositionMap[_feeder];
```

This causes denial of service when trying to remove the feeder that was previously placed at a removed feeder's position. To unlock the removal of the feeder, the admin would need to add another feeder:
```js
// paraspace-core/test/_oracle_nft_floor_price.spec.ts
it("doesn't update feeder index when a feeder is removed [AUDIT]", async () => {
  const { nftFloorOracle } = testEnv;

  expect(await nftFloorOracle.getFeederSize()).to.eq(7);

  const feeder1 = await nftFloorOracle.feeders(1);
  const feeder6 = await nftFloorOracle.feeders(6); // last feeder

  await nftFloorOracle.removeFeeder(feeder1);

  // feeder6 was placed at the feeder1's position
  expect(await nftFloorOracle.feeders(1)).to.eq(feeder6);

  // Cannot remove feeder6 because its index is still 6 and feeders.length == 6
  await expect(
    nftFloorOracle.removeFeeder(feeder6)
  ).to.be.revertedWith("Array accessed at an out-of-bounds or negative index");

  // Had to add a new feeder to be able to remove feeder6
  await nftFloorOracle.addFeeders([feeder1]);
  expect(await nftFloorOracle.feeders(6)).to.eq(feeder1);

  await nftFloorOracle
An attacker can Drop a reserve
Audit: 2022-11-paraspace-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/protocol/pool/PoolParameters.sol#L139-L148


# Vulnerability details

## Impact
An attacker can Drop a reserve
## Proof of Concept
For the `executeDropReserve()` function, there is no permission check. An attacker can call directly. Delete `reservesData[asset]` directly. Resulting in loss of contract assets


```solidity
    function executeDropReserve(
        mapping(address => DataTypes.ReserveData) storage reservesData,
        mapping(uint256 => address) storage reservesList,
        address asset
    ) external {
        DataTypes.ReserveData storage reserve = reservesData[asset];
        ValidationLogic.validateDropReserve(reservesList, reserve, asset);
        reservesList[reservesData[asset].id] = address(0);
        delete reservesData[asset];
    }
```

Bypass `onlyPoolConfigurator` check

```solidity
    function dropReserve(address asset)
        external
        virtual
        override
        onlyPoolConfigurator
    {
        DataTypes.PoolStorage storage ps = poolStorage();

        PoolLogic.executeDropReserve(ps._reserves, ps._reservesList, asset);
    }

```



## Tools Used
vscode
## Recommended Mitigation Steps

Add permission check for `executeDropReserve()` call
Attackers can steal assets in the poollogic contract
Audit: 2022-11-paraspace-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/protocol/libraries/logic/PoolLogic.sol#L82-L93
https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/protocol/pool/PoolParameters.sol#L196-L203


# Vulnerability details

## Impact
Attackers can steal assets in the poollogic contract
## Proof of Concept
Attackers can directly transfer any assets in poollogic
[2022-11-paraspace/PoolLogic.sol at main · code-423n4/2022-11-paraspace · GitHub](https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/protocol/libraries/logic/PoolLogic.sol#L82-L93)
```solidity
    function executeRescueTokens( //@audit 
        DataTypes.AssetType assetType,
        address token,
        address to,
        uint256 amountOrTokenId
    ) external {
        if (assetType == DataTypes.AssetType.ERC20) {
            IERC20(token).safeTransfer(to, amountOrTokenId);
        } else if (assetType == DataTypes.AssetType.ERC721) {
            IERC721(token).safeTransferFrom(address(this), to, amountOrTokenId);
        }
    }
```

Bypass `onlyPoolAdmin` restriction, resulting in stolen assets in the contract
[2022-11-paraspace/PoolParameters.sol at main · code-423n4/2022-11-paraspace · GitHub](https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/protocol/pool/PoolParameters.sol#L196-L203)
```solidity
    function rescueTokens(
        DataTypes.AssetType assetType,
        address token,
        address to,
        uint256 amountOrTokenId
    ) external virtual override onlyPoolAdmin {
        PoolLogic.executeRescueTokens(assetType, token, to, amountOrTokenId);
    }
```
## Tools Used
vscode
## Recommended Mitigation Steps
Add permission check to executeRescueTokens() call