xanoutas/solidity-security-findings huggingface dataset:
Issue M-02 not correctly fixed since the check is not inclusive 
Audit: 2024-06-panoptic-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-06-panoptic/blob/153f0d82440b7e63075d55b0659706531431145f/contracts/PanopticPool.sol#L1371-L1384


# Vulnerability details


## Proof of Concept

See M-02 _[here](https://github.com/code-423n4/2024-04-panoptic-findings/issues?q=is%3Aissue+is%3Aopen+_validatePositionList%28%29+does+not+check+for+duplicate+tokenIds%2C+allowing+attackers+to+bypass+solvency+checks)_

TLDR of the report is that the `_validatePositionList()` function fails to detect duplicate token IDs. And this oversight allows attackers to bypass solvency checks, enabling insolvent users to perform actions like minting, burning, liquidating, and force exercising options, as well as settling long premiums on behalf of other insolvent users. 

Now, the bug stems from the function's inability to differentiate between unique and duplicated token IDs, which can be exploited by adding multiple instances of the same token ID to generate a duplicate position hash. This manipulation can inflate an account's collateral balance beyond its required level, falsely indicating solvency. 

So to mitigate this vulnerability, wardens recommended to introduce a check in `_validatePositionList()` to ensure the token ID array length is shorter than `MAX_POSITIONS` `(32).`, however going to the implemented fix, this has not been accurately done, as the check is inclusive and actually erroneously accepts the data passed in even if the  array length is **not** shorter than `MAX_POSITIONS`, see https://github.com/code-423n4/2024-06-panoptic/blob/153f0d82440b7e63075d55b0659706531431145f/contracts/PanopticPool.sol#L1371-L1384

```solidity
    function _updatePositionsHash(address account, TokenId tokenId, bool addFlag) internal {
        // Get the current position hash value (fingerprint of all pre-existing positions created by '_account')
        // Add the current tokenId to the positionsHash as XOR'd
        // since 0 ^ x = x, no problem on first mint
        // Store values back into the user option details with the updated hash (leaves the other parameters unchanged)
        uint256 newHash = PanopticMath.updatePositionsHash(
            s_positionsHash[account],
            tokenId,
            addFlag
        );
        if ((newHash >> 248) > MAX_POSITIONS) revert Errors.TooManyPositionsOpen();
        s_positionsHash[account] = newHash;
    }

```

Which would mean that when `(newHash >> 248) == MAX_POSITIONS` the execution does not revert where as it should.


## Impact
M-02 has not been accurately fixed, leaving protocol susceptible to all the attacks mentioned in the [original submission](https://github.com/code-423n4/2024-04-panoptic-findings/issues?q=is%3Aissue+is%3Aopen+_validatePositionList%28%29+does+not+check+for+duplicate+tokenIds%2C+allowing+attackers+to+bypass+solvency+checks).

## Recommended Mitigation Steps
Consider applying the fix as was in the   [original submission](https://github.com/code-423n4/2024-04-panoptic-findings/issues?q=
`FactoryNFT#tokenURI()` does not comply with 721 since it doedne check if the tokenId is valid
Audit: 2024-06-panoptic-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-06-panoptic/blob/153f0d82440b7e63075d55b0659706531431145f/contracts/base/FactoryNFT.sol#L39-L51


# Vulnerability details


## Proof of Concept

Take a look at https://github.com/code-423n4/2024-06-panoptic/blob/153f0d82440b7e63075d55b0659706531431145f/contracts/base/FactoryNFT.sol#L39-L51

```solidity

    function tokenURI(uint256 tokenId) public view override returns (string memory) {
        address panopticPool = address(uint160(tokenId));

        return
            constructMetadata(
                panopticPool,
                PanopticMath.safeERC20Symbol(PanopticPool(panopticPool).univ3pool().token0()),
                PanopticMath.safeERC20Symbol(PanopticPool(panopticPool).univ3pool().token1()),
                PanopticPool(panopticPool).univ3pool().fee()
            );
    }

```

According to the EIP, the `tokenURI` is expected to revert/throw in the case where the `tokenId` is not a valid one, see https://eips.ethereum.org/EIPS/eip-721
```marldaown
/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
    /// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
    ///  3986. The URI may point to a JSON file that conforms to the "ERC721
    ///  Metadata JSON Schema".
    function tokenURI(uint256 _tokenId) external view returns (string);
}
```
## Impact
>Borderline low,  medium, no QA so attaching as med.
## Recommended Mitigation Steps
Consider ensuring that the `tokenId` is valid so as to conform with the EIP.




## Assessed type

Context
getChainName()'s implementation is somewhat broken on the Blast chain.
Audit: 2024-06-panoptic-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-06-panoptic/blob/153f0d82440b7e63075d55b0659706531431145f/contracts/base/FactoryNFT.sol#L181-L204


# Vulnerability details




## Proof of Concept

Take a look at https://github.com/code-423n4/2024-06-panoptic/blob/153f0d82440b7e63075d55b0659706531431145f/contracts/base/FactoryNFT.sol#L181-L204

```solidity
    function getChainName() internal view returns (string memory) {
        if (block.chainid == 1) {
            return "Ethereum Mainnet";
        } else if (block.chainid == 56) {
            return "BNB Smart Chain Mainnet";
        } else if (block.chainid == 42161) {
            return "Arbitrum One";
        } else if (block.chainid == 8453) {
            return "Base";
        } else if (block.chainid == 43114) {
            return "Avalanche C-Chain";
        } else if (block.chainid == 137) {
            return "Polygon Mainnet";
        } else if (block.chainid == 10) {
            return "OP Mainnet";
        } else if (block.chainid == 42220) {
            return "Celo Mainnet";
        } else if (block.chainid == 238) {//@audit
            return "Blast Mainnet";
        } else {
            return LibString.toString(block.chainid);
        }
    }

```

Evidently  attempt of getting the chain name checks the current block chainId and then attaches it, it's name.

Now note that this function is used in multiple instances in protocol, from generating the SVG of the NFTs and what not, as confirmed by this search command: [https://github.com/search?q=repo%3Acode-423n4%2F2024-06-panoptic%20getChainName&type=code](https://github.com/search?q=repo%3Acode-423n4%2F2024-06-panoptic%20getChainName&type=code)

Problem however, is that this implementation doesn't work as expected on the blast chain due to the protocol using a wrong chain ID for blast mainnet.

Going to the official Blast docs: https://docs.blast.io/building/network-information

We can see that that the correct chainId for the blast mainnet should be `81457`, see the below:
![](https://cdn.discordapp.com/attachments/1235966308542185483/1249503254648459346/Screenshot_2024-06-10_at_02.19.44.png?ex=66678a16&is=66663896&hm=0a0f4d267b9d36e0b89384d7e4325e6f62e40cdf52477dc475dd1385142df5d2&)

The above would mean that generating SVG info would not work as expected on the Blast mainnet unlike other chains.
## Impact
Generating SVGs would be broken for the Blast mainnet.
## Recommended Mitigation Steps
Consider applying these changes to https://github.com/code-423n4/2024-06-panoptic/blob/153f0d82440b7e63075d55b0659706531431145f/contracts/base/FactoryNFT.sol#L181-L204

```diff
    function getChainName() internal view returns (string memory) {
        if (block.chainid == 1) {
            return "Ethereum Mainnet";
        } else if (block.chainid == 56) {
            return "BNB Smart Chain Mainnet";
        } else if (block.chainid == 42161) {
            return "Arbitrum One";
        } else if (block.chainid == 8453) {
The issue around validating the position list from the previous audit seems to have not been fixed
Audit: 2024-06-panoptic-findings
Severity: MEDIUM

# Lines of code

 https://github.com/code-423n4/2024-06-panoptic/blob/153f0d82440b7e63075d55b0659706531431145f/contracts/PanopticPool.sol#L1371-L1384


# Vulnerability details


## Proof of Concept

First see the [previous issue](https://github.com/code-423n4/2024-04-panoptic-findings/issues?q=is%3Aissue+_validatePositionList%28%29+positionIdList+can+forgery+is%3Aclosed).

Take a look at https://github.com/code-423n4/2024-06-panoptic/blob/153f0d82440b7e63075d55b0659706531431145f/contracts/PanopticPool.sol#L1334-L1361


    
TLDR of the issue is that the ` _validatePositionList()`  function is used multiple places and is primarily used to validate the legality of `positionIdList`. So the main logic involves iterating through tokenIds and performing XOR operations, then comparing the result with `s_positionsHash[account]`, however previously the overflow was ignored, the suggested fix was tol include axcheck that the returned value is less than 255, but the fix implemented as seen below  have the check against `MAX_POSITIONS` which is `32` instead of `255`: https://github.com/code-423n4/2024-06-panoptic/blob/153f0d82440b7e63075d55b0659706531431145f/contracts/PanopticPool.sol#L1371-L1384

```solidity
    function _updatePositionsHash(address account, TokenId tokenId, bool addFlag) internal {
        // Get the current position hash value (fingerprint of all pre-existing positions created by '_account')
        // Add the current tokenId to the positionsHash as XOR'd
        // since 0 ^ x = x, no problem on first mint
        // Store values back into the user option details with the updated hash (leaves the other parameters unchanged)
        uint256 newHash = PanopticMath.updatePositionsHash(
            s_positionsHash[account],
            tokenId,
            addFlag
        );
        if ((newHash >> 248) > MAX_POSITIONS) revert Errors.TooManyPositionsOpen();
        s_positionsHash[account] = newHash;
    }

```
## Impact
M-02 has not been correctly mitigated, leaving it vulnerable to the same attacks as hinted in the [previous issue](https://github.com/code-423n4/2024-04-panoptic-findings/issues?q=is%3Aissue+_validatePositionList%28%29+positionIdList+can+forgery+is%3Aclosed) and it's duplicates

## Recommended Mitigation Steps
Apply the suggested fix from the report, which is †hat the check should be against `255` instead.







## Assessed type

Context
Protocol is vulnerable to SVG JSON injection attacks
Audit: 2024-06-panoptic-findings
Severity: MEDIUM

# Lines of code

 https://github.com/code-423n4/2024-06-panoptic/blob/153f0d82440b7e63075d55b0659706531431145f/contracts/base/FactoryNFT.sol#L58-L118


# Vulnerability details


## Proof of Concept

Take a look at https://github.com/code-423n4/2024-06-panoptic/blob/153f0d82440b7e63075d55b0659706531431145f/contracts/base/FactoryNFT.sol#L58-L118

```solidity
    function constructMetadata(
        address panopticPool,
        string memory symbol0,
        string memory symbol1,
        uint256 fee
    ) public view returns (string memory) {
        uint256 lastCharVal = uint160(panopticPool) & 0xF;
        uint256 rarity = PanopticMath.numberOfLeadingHexZeros(panopticPool);

        string memory svgOut = generateSVGArt(lastCharVal, rarity);

        svgOut = generateSVGInfo(svgOut, panopticPool, rarity, symbol0, symbol1);
        return
            string(
                abi.encodePacked(
                    "data:application/json;base64,",
                    Base64.encode(
                        bytes(
                            abi.encodePacked(
                                '{"name":"',
                                abi.encodePacked(
                                    LibString.toHexString(uint256(uint160(panopticPool)), 20),
                                    "-",
                                    string.concat(
                                        metadata[bytes32("strategies")][lastCharVal].dataStr(),
                                        "-",
                                        LibString.toString(rarity)
                                    )
                                ),
                                '", "description":"',
                                string.concat(
                                    "Panoptic Pool for the ",
                                    symbol0,
                                    "-",
                                    symbol1,
                                    "-",
                                    PanopticMath.uniswapFeeToString(uint24(fee)),
                                    " market"
                                ),
                                '", "attributes": [{',
                                '"trait_type": "Rarity", "value": "',
                                string.concat(
                                    LibString.toString(rarity),
                                    " - ",
                                    metadata[bytes32("rarities")][rarity].dataStr()
                                ),
                                '"}, {"trait_type": "Strategy", "value": "',
                                metadata[bytes32("strategies")][lastCharVal].dataStr(),
                                '"}, {"trait_type": "ChainId", "value": "',
                                getChainName(),
                                '"}]',
                                '", "image": "',
                                "data:image/svg+xml;base64,",
                                B
Approve race condition in Collateral Tracker
Audit: 2024-06-panoptic-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-06-panoptic/blob/153f0d82440b7e63075d55b0659706531431145f/contracts/tokens/ERC20Minimal.sol#L49-L55


# Vulnerability details

## Impact

The function `approve(address spender, uint256 amount)` allows `spender` to withdraw from your account, multiple times, up to the `amount`. If this function is called again it overwrites the current allowance with the new `amount`.

Front running this approval can cause a owner to approve a different amount of shares than the one they intended to approve.

## Proof of Concept

Attack scenario:

1. Alice allows Bob to transfer N of Alice's tokens (N>0) by calling the approve method on a Token smart contract, passing the Bob's address and N as the method arguments
2. After some time, Alice decides to change from N to M (M>0) the number of Alice's tokens Bob is allowed to transfer, so she calls the approve method again, this time passing the Bob's address and M as the method arguments
3. Bob notices the Alice's second transaction before it was mined and quickly sends another transaction that calls the transferFrom method to transfer N Alice's tokens somewhere
4. If the Bob's transaction will be executed before the Alice's transaction, then Bob will successfully transfer N Alice's tokens and will gain an ability to transfer another M tokens
5. Before Alice noticed that something went wrong, Bob calls the transferFrom method again, this time to transfer M Alice's tokens.
6. So, an Alice's attempt to change the Bob's allowance from N to M (N>0 and M>0) made it possible for Bob to transfer N+M of Alice's tokens, while Alice never wanted to allow so many of her tokens to be transferred by Bob.

**Proof Of Concept**

```javascript
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.18;

import {Test, console} from "forge-std/Test.sol";
import {FactoryNFT} from "@base/FactoryNFT.sol";
import {CollateralTrackerTest} from "./core/CollateralTracker.t.sol";
import {CollateralTracker} from "@contracts/CollateralTracker.sol";
import {IERC20Partial} from "@tokens/interfaces/IERC20Partial.sol";
import {ERC20Mock} from "@openzeppelin/contracts/mocks/ERC20Mock.sol";
import {PanopticFactoryTest} from "./core/PanopticFactory.t.sol";
import {PanopticPool} from "@contracts/PanopticPool.sol";
import {PanopticPoolTest} from "./core/PanopticPool.t.sol";

contract BugTest_CollateralTracker is CollateralTrackerTest {
    FactoryNFT public factory;
    ERC20Mock public mock;
    address public mockAddr;
    address public ctAddr;
    CollateralTracker public ct;

    function setUp() public override(CollateralTrackerTest) {
        super.setUp();
        mock = new ERC20Mock("Mock", "MCK", address(this), 1e18);
        ct = new CollateralTracker(10, 2_000, 1_000, -1_024, 5_000, 9_000, 20_000);
        mock.approveInternal(address(this), address(ct), type(uint256).max);
        mockAddr = address(mock);
        ctAddr = address(ct);
    }

    function test_ApproveRaceCondition() public {
safeERC20Symbol() function will always revert when interating with tokens that returns bytes32 as Symbol
Audit: 2024-06-panoptic-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-06-panoptic/blob/main/contracts/libraries/PanopticMath.sol#L85-L93


# Vulnerability details

## Impact
Tokens that return bytes32 as a Symbol are incompatible with the FactoryNFT.

## Proof of Concept
Some tokens (e.g. [MKR](https://etherscan.io/address/0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2#readContract#F7)) have metadata fields (name / symbol) encoded as bytes32 instead of a string. Such tokens will cause that all calls to [`PanopticMath.safeERC20Symbol()`](https://github.com/code-423n4/2024-06-panoptic/blob/main/contracts/libraries/PanopticMath.sol#L85-L93) ends up reverting the whole tx.

```
function safeERC20Symbol(address token) external view returns (string memory) {

    //@audit-issue => Any token that returns bytes32 will cause the tx to revert!

    // not guaranteed that token supports metadata extension
    // so we need to let call fail and return placeholder if not
    try IERC20Metadata(token).symbol() returns (string memory symbol) {
        return symbol;
    } catch {
        return "???";
    }
}
```

### Coded PoC
<details>
<summary><b>Expand to see PoC</b></summary>
<br>

Add the below file under the folder [test/foundry](https://github.com/code-423n4/2024-06-panoptic/tree/main/test/foundry).

```
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

// Foundry
import "forge-std/Test.sol";

import {PanopticMath} from "@libraries/PanopticMath.sol";
import {FactoryNFT} from "../../contracts/base/FactoryNFT.sol";

import {Pointer} from "@types/Pointer.sol";

import {IUniswapV3Pool} from "v3-core/interfaces/IUniswapV3Pool.sol";


//@audit => Short implementation of the Maker Token
//@audit => https://etherscan.io/address/0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2#readContract#F7
contract DSToken {
    bytes32 public symbol;

    constructor(bytes32 symbol_) public {
        symbol = symbol_;
    }
}

contract TetherToken {
    string public symbol;
    
    constructor(string memory symbol_) public {
        symbol = symbol_;
    }

}

contract UniPool {
  address public token0;
  address public token1;

  constructor(address _token0, address _token1) {
    token0 = _token0;
    token1 = _token1;
  }
}

contract PanopticPool {
  IUniswapV3Pool pool;

  constructor(address _uniPool) {
    pool = IUniswapV3Pool(_uniPool);
  }
}

contract FactoryNFTMock is FactoryNFT {
  constructor(
      bytes32[] memory properties,
      uint256[][] memory indices,
      Pointer[][] memory pointers
  )
      FactoryNFT(properties, indices, pointers){}
    
    function callsafeERC20Symbol(address token) external view returns (string memory) {
        PanopticMath.safeERC20Symbol(token);
    }
}


contract SafeERC20SymbolTest is Test {
    bytes32[] properties;
    uint256[][] indices;
    Pointer[][] pointers;

    function test_safeERC20Symbol() external {
      DSToken makerToken = new DSToken(bytes32(0x4d4b520000000000000000000000000000000000000000000000000000000000));
The `tokenURI` function doesn't verify if a token ID is valid before returning its metadata. This means it could return data for a fake or non existent NFT.
Audit: 2024-06-panoptic-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-06-panoptic/blob/153f0d82440b7e63075d55b0659706531431145f/contracts/base/FactoryNFT.sol#L40


# Vulnerability details

## Impact

Anyone can exploit the `tokenURI` function with a fake tokenID. The function doesn't check if the tokenId is real and returns data that makes it look like a real factory NFT. The returned data can be used to deceive potential users (especially in integrations and on the marketplace), as the function will return data for a non-existent NFT id that appears to be a genuine factory NFT. This will lead to a poor user experience or financial loss for users.

This also violates the [ERC721](https://eips.ethereum.org/EIPS/eip-721) standard.

## Proof of Concept

The `tokenURI` methods lack any requirements stating that the provided NFT id must be created. We can also see that in the standard implementation by [OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/de4154710bcc7c6ca5417097f34ce14e9205c3ac/contracts/token/ERC721/ERC721.sol#L89), this check is present.

```solidity
    function tokenURI(uint256 tokenId) public view override returns (string memory) {
        address panopticPool = address(uint160(tokenId));

        return
            constructMetadata(
                panopticPool,
                PanopticMath.safeERC20Symbol(PanopticPool(panopticPool).univ3pool().token0()),
                PanopticMath.safeERC20Symbol(PanopticPool(panopticPool).univ3pool().token1()),
                PanopticPool(panopticPool).univ3pool().fee()
            );
    }

```

And as for the [EIP compliance](https://eips.ethereum.org/EIPS/eip-721)

> Throws if _tokenId is not a valid NFT

An attacker can simply create a basic NFT, preferably impersonating a Factory NFT;
He deploys the contract and offers it on marketplace for sale;
Unsuspecting users query the tokenURI of the NFT;
The function works normally as it doesn't check if its a real one;
Users get decieved into thinking the NFT is a real one, which can lead to potential loss of funds.

## Tools Used
Manual code review

## Recommended Mitigation Steps

Consider checking that the NFT exists.



## Assessed type

ERC721