xanoutas/solidity-security-findings huggingface dataset:
`NextGenCore::tokenURI` is vulnerable to JSON injection
Audit: 2023-10-nextgen-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/hardhat/smart-contracts/NextGenCore.sol#L343-L357
https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/hardhat/smart-contracts/NextGenCore.sol#L131-L138


# Vulnerability details

## Impact

Once NFT is minted anyone can trigger `NextGenCore::tokenURI` to retrieve JSON data with the details and generated svg image. 
The issue is that function `NextGenCore::tokenURI` is vulnerable to JSON injection and it is possible to inject characters into the collection fields that would completely alter the integrity of JSON data.

This could lead to issues like incorrect data display and security issues. Some examples of the attacks that can be carried:

- Attacker might create a GenCore NFT collection that mimics copies the name of different GenCore NFT collection.
- Attacker might change the generated svg image to completely different one.
- Attacker might point image to external URL.
- Attacker might trigger cross-site scripting attacks if the data from JSON is not properly handled.




## Proof of Concept

A scenario where the collection name is submitted as the following :
`Cool Token" }, { "extra": "injected data`

Intended JSON structure :

```JSON
{
  "name": "Cool Token",
}
```

JSON structure with the problematic input

```JSON
{
  "name": "Cool Token" }, { "extra": "injected data",
}
```

In this altered JSON, the input has broken out of the name field and added an entirely new object ({ "extra": "injected data") to the JSON. 

## Tools Used

Manual review

## Recommended Mitigation Steps

It is recommended to properly encode collections data to make sure it cannot alter returned JSON data.


## Assessed type

Other
The getPrice function returns an incorrect price
Audit: 2023-10-nextgen-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/MinterContract.sol#L540


# Vulnerability details

## Bug Description
When a user calls one of the following functions: `mint, burnToMint, or burnOrSwapExternalToMint`, the current price for the NFT is calculated, depending on the collection type. The issue arises in sale model 2, specifically when a user mints an NFT in the last second, resulting in the acquisition of the NFT at an incorrect amount.

All the above functions have a time check implemented as follows:
```solidity
block.timestamp >= collectionPhases[col].publicStartTime &&
            block.timestamp <= collectionPhases[col].publicEndTime
```

The getPrice function for sale model 2 checks as follows:
```solidity
block.timestamp >
            collectionPhases[_collectionId].allowlistStartTime &&
            block.timestamp < collectionPhases[_collectionId].publicEndTime
```

As a result, when purchasing an NFT in the last second before the `publicEndTime` using sale model 2, the user is charged an incorrect price, and the transaction fails.

## Impact
If the collection has a sales option of 2, the user must buy the NFT at the wrong price.

## Tools Used
Manual

## Recommended Mitigation Steps
Consider changing `getPrice` as follows:
```diff		
else if (
            collectionPhases[_collectionId].salesOption == 2 &&
-            block.timestamp > collectionPhases[_collectionId].allowlistStartTime &&
-            block.timestamp < collectionPhases[_collectionId].publicEndTime

+            block.timestamp >= collectionPhases[_collectionId].allowlistStartTime &&
+            block.timestamp <= collectionPhases[_collectionId].publicEndTime
        )
```


## Assessed type

Invalid Validation
User can overpay for NFT
Audit: 2023-10-nextgen-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-nextgen/blob/main/smart-contracts/MinterContract.sol#L196
https://github.com/code-423n4/2023-10-nextgen/blob/main/smart-contracts/MinterContract.sol#L258
https://github.com/code-423n4/2023-10-nextgen/blob/main/smart-contracts/MinterContract.sol#L326


# Vulnerability details

## Impact
Because of the way the check `require(msg.value >= getPrice(_mintCollectionID), "Wrong ETH");` is structured, users can end up paying more than the actual value of NFT, causing bad user experience for user. There is also no refund for extra value sent to help the UX.

## Proof of Concept
In the burnToMint function as shown below
```solidity
function burnToMint(uint256 _burnCollectionID, uint256 _tokenId, uint256 _mintCollectionID, uint256 _saltfun_o) public payable {
        require(burnToMintCollections[_burnCollectionID][_mintCollectionID] == true, "Initialize burn");
        require(block.timestamp >= collectionPhases[_mintCollectionID].publicStartTime && block.timestamp<=collectionPhases[_mintCollectionID].publicEndTime,"No minting");
        require ((_tokenId >= gencore.viewTokensIndexMin(_burnCollectionID)) && (_tokenId <= gencore.viewTokensIndexMax(_burnCollectionID)), "col/token id error");
        // minting new token
        uint256 collectionTokenMintIndex;
        collectionTokenMintIndex = gencore.viewTokensIndexMin(_mintCollectionID) + gencore.viewCirSupply(_mintCollectionID);
        require(collectionTokenMintIndex <= gencore.viewTokensIndexMax(_mintCollectionID), "No supply");
        //@audit-check: avoid overpaying for nft: use ==: duplicate medium
        require(msg.value >= getPrice(_mintCollectionID), "Wrong ETH");
        uint256 mintIndex = gencore.viewTokensIndexMin(_mintCollectionID) + gencore.viewCirSupply(_mintCollectionID);
        // burn and mint token
        address burner = msg.sender;
        gencore.burnToMint(mintIndex, _burnCollectionID, _tokenId, _mintCollectionID, _saltfun_o, burner);
        collectionTotalAmount[_mintCollectionID] = collectionTotalAmount[_mintCollectionID] + msg.value;
    }
```
The price check does not put an upper bound, means User can pay 1000ETH for a 1ETH NFT with no refund sent

## Tools Used
Manual Review

## Recommended Mitigation Steps
- use `require(msg.value == getPrice(_mintCollectionID), "Wrong ETH");` instead or
- add a refund function for excess ETH sent


## Assessed type

ETH-Transfer
Multiplication before division can cause  loss in precision
Audit: 2023-10-nextgen-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/MinterContract.sol#L429-L433


# Vulnerability details

## Impact
 Multiplication before division can cause loss in precision
 
## Proof of Concept
https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/MinterContract.sol#L429-L433

In Solidity, integer division might truncate. Performing multiplication before division can sometimes avoid a loss of precision. In this audit, multiple instances were found where division is performed before multiplication in the contract

## Tools Used

## Recommended Mitigation Steps
Perform multiplication operations before division to preserve the precision of values for non-floating data types.


## Assessed type

Math
`returnHighestBidder` of `Auction.sol` will return wrong bidder rather than highest due to missing logic in for loop.
Audit: 2023-10-nextgen-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-nextgen/blob/main/hardhat/smart-contracts/AuctionDemo.sol#L87-L100


# Vulnerability details



## Summary

When `returnHighestBidder` is called. To find highest bidder for `_tokenid` loop runs on `auctionInfoStru[]` array for that `_tokenid`. But in for loop one line is missing to update `highBid` o each iteration. Because of it `highBid` remains 0 for all loop iterations. And each bid value will show higher than highBid so it will return last positive bid's bidder which status is true rather than returning highest bid bidder.

## Vulnerability Details

In this function's for loop highBid updating line is missing so it will remain 0 for all iterations. Because of it this function will not return highest bid bidder. Since each time condition `auctionInfoData[_tokenid][i].bid > highBid` will be true even if bid is very little. And index will be updated even if it bid is lesser than previous with true status. it will update the index even if bid is lesser than previous it just should be above 0 with true status and index will be updated. Resulting in wrong bidder returning for that index.

[/hardhat/smart-contracts/AuctionDemo.sol#L87-L100](https://github.com/code-423n4/2023-10-nextgen/blob/main/hardhat/smart-contracts/AuctionDemo.sol#L87-L100)

```solidity
87:    function returnHighestBidder(uint256 _tokenid) public view returns (address) {
        uint256 highBid = 0;
        uint256 index;
        for (uint256 i=0; i< auctionInfoData[_tokenid].length; i++) {
91:            if (auctionInfoData[_tokenid][i].bid > highBid && auctionInfoData[_tokenid][i].status == true) {
92:                index = i;
              //@audit highBid = auctionInfoData[_tokenid][i].bid  line is missing in this loop so highBid always remains 0
93:            }
        }
        if (auctionInfoData[_tokenid][index].status == true) {
                return auctionInfoData[_tokenid][index].bidder;
            } else {
                revert("No Active Bidder");
        }
    }
```

## Impact

Due to returning wrong bidder. Bidder will not match with the highest bid because `returnHighestBid` function is working fine and returning highest bid amount. `returnHighestBidder` returning wrong bidder. Due to this mismatch `claimAuction` can never transfer NFT to highest bidder. Also the bid value for all these bidders will be transferred to them. It makes the whole `AuctionDemo.sol` contract useless.

In `claimAuction` function if `highestBid` is correct and `highestBidder` is wrong than both will mismatches. And due to this both won't be found simultaneously at same index.

Then `highestBid` will be of different bidder. And current bidder's bid is not highest due to this mismatch , both condition at line 111 in `if(auctionInfoData[_tokenid][i].bidder == highestBidder && auctionInfoData[_tokenid][i].bid == highestBid` will never be simultaneously true at same index resulting in that if condition at line 111 will always be fal
THE RANDOM HASH GENERATED VIA THE `RandomizerNXT.calculateTokenHash` FUNCTION IS NOT TRULY RANDOM
Audit: 2023-10-nextgen-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-nextgen/blob/main/smart-contracts/RandomizerNXT.sol#L55-L59
https://github.com/code-423n4/2023-10-nextgen/blob/main/smart-contracts/XRandoms.sol#L36
https://github.com/code-423n4/2023-10-nextgen/blob/main/smart-contracts/XRandoms.sol#L41


# Vulnerability details

## Impact

The `RandomizerNXT.calculateTokenHash` function is used to calculate the random hash and returns it to the `gencore` contract. The `random hash` is calculated as shown below:

        bytes32 hash = keccak256(abi.encodePacked(_mintIndex, blockhash(block.number - 1), randoms.randomNumber(), randoms.randomWord()));

Here the `randoms.randomNumber()` and `randoms.randomWord())` (here randoms is an instance of the XRandoms contract) functions are called to obtain the respective randomness to the calculation of the random hash.

The `XRandoms.randomNumber` function and `XRandoms.randomWord` function uses the following computations to calculate the `randomNum` respectively, as shown below:

        uint256 randomNum = uint(keccak256(abi.encodePacked(block.prevrandao, blockhash(block.number - 1), block.timestamp))) % 1000;

        uint256 randomNum = uint(keccak256(abi.encodePacked(block.prevrandao, blockhash(block.number - 1), block.timestamp))) % 100;        

But the issue is that the solidity documentation says `block.number` and `block.timestamp` should not be relied upon for `randomness` as shown below:

```solidity
Do not rely on block.timestamp or blockhash as a source of randomness, unless you know what you are doing.
```

Further `block.prevrandao` is also manipulatable since the `randomness from the past is known and predictable`, as explained in the following article:
https://soliditydeveloper.com/prevrandao

As a result the `random hash` value calculated in the `RandomizerNXT.calculateTokenHash` is not truly random since its randomness is predictable and can be manipulated by the validators. Hence the `NextGenCore.tokenToHash` value for a specific `TokenId` can be manipulated as a result. Since the `tokenToHash[tokenId]` is used in the `NextGenCore.retrieveGenerativeScript` function to create the `Generative Script` of a token and this generative script is used to calculate the `tokenURI` of a token when the `onchainMetadata[tokenIdsToCollectionIds[tokenId]] == true`, it can be concluded that the `tokenURI` of a ERC721 token is manipulatable when the `RandomizerNXT` is used as the `randomizer` contract of a specific collection (Since the returned random hash via the `calculateTokenHash` function is not truly random).

## Proof of Concept

```solidity
    function calculateTokenHash(uint256 _collectionID, uint256 _mintIndex, uint256 _saltfun_o) public {
        require(msg.sender == gencore);
        bytes32 hash = keccak256(abi.encodePacked(_mintIndex, blockhash(block.number - 1), randoms.randomNumber(), randoms.randomWord()));
        gencoreContract.setTokenHash(_collectionID, _mintIndex, hash);
    }
```

https:
RandomizerNXT contract's result can be predicted by caller
Audit: 2023-10-nextgen-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-nextgen/blob/main/smart-contracts/RandomizerNXT.sol#L58


# Vulnerability details

## Impact
When minting with the RandomizerNXT contract, the resulting tokenhash is
computed from publicly available information, so an attacker can predict its
output and choose to revert the transaction if they don't deem the tokenhash to
generate an NFT of their liking / valuable enough, defeating the purpose of
randomness.

## Proof of Concept
A malicious user could have a contract execute the mint transaction on their
behalf, and check the resulting tokenhash immediately after execution returns
from the project's contract, similar to the solution of the CoinFlip Ethernaut
challenge

## Tools Used
Manual Review

## Recommended Mitigation Steps
Discard the RandomizerNXT contract and use the other Randomizers instead, which
are asyncronous and make the minter commit to a mint before being able to know
the tokenhash it'll receive.



## Assessed type

Oracle
Unchecked low-level call transfers can cause loss of ETH locked in `claimAuction()`
Audit: 2023-10-nextgen-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-nextgen/blob/main/smart-contracts/AuctionDemo.sol#L113;https://github.com/code-423n4/2023-10-nextgen/blob/main/smart-contracts/AuctionDemo.sol#L116


# Vulnerability details

## Impact
Unchecked low-level call transfers can cause loss of ETH locked in contract for failed 
transfers. 


## Proof of Concept
Once `AuctionDemo.sol::claimAuction()` is called, it will flag the `tokenId` as 
claimed with `auctionClaim[_tokenid] = true;`. Because the low-level call transfers
are unchecked, if a transfer fails that eth will be lost locked in the contract. As,
that function will not be callable anymore due to the require check and there's no
other way to recover it. 


## Tools Used
Manual Analysis

## Recommended Mitigation Steps
Please make sure to check all calls with `require(success)`.$


## Assessed type

ETH-Transfer
dubious typecast from bytes to bytes32.
Audit: 2023-10-nextgen-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/RandomizerVRF.sol#L65-L68


# Vulnerability details

## Impact
not all types can be converted to each other, and some conversions may result in data loss or unexpected behavior. For example, converting from bytes to bytes32 may truncate the data if the bytes array is longer than 32 bytes, or pad the data with zeros if the bytes array is shorter than 32 bytes. This can affect the logic and security of the smart contract.

## Proof of Concept
you are using the abi.encodePacked function to pack the _randomWords array and the _requestId into a bytes array, and then casting it to bytes32. This may not be a safe or clear way to do the conversion, as the packed bytes array may not fit into 32 bytes, or may lose the original structure of the data.

Dubious typecast in NextGenRandomizerVRF.fulfillRandomWords(uint256,uint256[]) (hardhat/smart-contracts/RandomizerVRF.sol#65-68):
bytes => bytes32 casting occurs in gencoreContract.setTokenHash(tokenIdToCollection[requestToToken[_requestId]],requestToToken[_requestId],bytes32(abi.encodePacked(_randomWords,requestToToken[_requestId]))) (hardhat/smart-contracts/RandomizerVRF.sol#66)

## Tools Used
- Manual

## Recommended Mitigation Steps
One way to avoid this dubious typecast is to use a clear and consistent way to encode and decode the data. For example, you can use the abi.encode and abi.decode functions, which preserve the types and lengths of the data. For example:

// Encode the data as bytes
bytes memory data = abi.encode (_randomWords, _requestId);

// Decode the data as uint256[] and uint256
(uint256 [] memory randomWords, uint256 requestId) = abi.decode (data, (uint256 [], uint256));


## Assessed type

Context
'getPrice()' returns unreasonably high prices for ' Periodic sales' (sales model 3)  if the collection has pre-existing supply.
Audit: 2023-10-nextgen-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-nextgen/blob/main/smart-contracts/MinterContract.sol#L535-L536


# Vulnerability details

## Impact

' Periodic sales' model cannot be effectively used in a collection with  Pre-existing supply,
due to the unreasonably high prices reported by the currently employed price formula in 'getPrice()'.

This prevents the system from flexibly combining different sales models in a single collection 
as intended by the business model. 

## Proof of Concept

https://seize-io.gitbook.io/nextgen/nextgen-smart-contracts/features#minting-process

The docs of the NextGen Protocol (linked above) states the following, 
  
```

* A collection can have an arbitrary number of phases (starting and ending times), 
each with their own allowlist.

* The sales models can be combined with phased allowlists to execute very complex drops.

```

For Illustration, this means

A collection with a supply of 10,000 NFTs can be split into different phases,
where each phase can have it own supply and sales model.

Lets consider a common scenario followed in the NFT community, where 
collectionMintCost = 1 ETH 
100 % of the total supply is allocate to allowlisted addresses, and 
any unminted NFTs go over to public sale phase.

Here, the collection's admin wants to utilize ' Periodic sales' (sales model 3)  for public sale phase.
where he wants the price to increase by 0.005 ETH ( 1.005 ETH, 1.01, 1.015 ETH and so on)
for every 30 second 'time period'  
after the ` publicStartTime `.

```
if (collectionPhases[_collectionId].salesOption == 3) {

            if (collectionPhases[_collectionId].rate > 0) {
                return collectionPhases[_collectionId].collectionMintCost + 
                      ((collectionPhases[_collectionId].collectionMintCost / 
                      collectionPhases[_collectionId].rate) * gencore.viewCirSupply(_collectionId));

```

Lets say 9000 tokens are minted in the allowlist phase and 
1000 tokens are left unminted.
So it is time for public sale phase under' sales model 3'.

The potential buyer queries `getPrice()` for this collection and gets an outrageous price of 451 ETH.

Its because the current formula calculates price using the circulating supply of the collection 
which is curently sitting at 9000.

This prevents the NextGen system from performing complex drops 
by effectively combine different sales models in a single collection 
as intended by the business model.


## Tools Used

Manual Review, Foundry.

## Recommended Mitigation Steps

Remodel the way to calculate ' Periodic sales model '  prices 
by tracking the number of NFTs minted through `sales model 3` using a separate counter
without taking the collection's current ciculating supply into account.


## Assessed type

Math
Impossible to execute `mintAndAuction` due to division by zero error
Audit: 2023-10-nextgen-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/MinterContract.sol#L276-L298


# Vulnerability details

## Impact
The admins would not be able to use the function `mintAndAuction` in case either the `timePeriod` equals zero as the function would always revert with `panic code 0x12 (Division or modulo division by zero)`

The scenario is very likely to happen when the sale model (`collectionPhases[_collectionID].salesOption`) is set to other than 3 (periodic sale), as all other sale models do not utilize `timePeriod` values in their logic. 

## Proof of Concept
The `mintAndAuction` function has a check if a period has passed in order to allow minting, which should only be checked if the collection is using a periodic sales model. However, the check is enforced on all models instead:
```solidity
    function mintAndAuction(address _recipient, string memory _tokenData, uint256 _saltfun_o, uint256 _collectionID, uint _auctionEndTime) public FunctionAdminRequired(this.mintAndAuction.selector) {
        require(gencore.retrievewereDataAdded(_collectionID) == true, "Add data");
        uint256 collectionTokenMintIndex;
        collectionTokenMintIndex = gencore.viewTokensIndexMin(_collectionID) + gencore.viewCirSupply(_collectionID);
        require(collectionTokenMintIndex <= gencore.viewTokensIndexMax(_collectionID), "No supply");
        uint256 mintIndex = gencore.viewTokensIndexMin(_collectionID) + gencore.viewCirSupply(_collectionID);
        gencore.airDropTokens(mintIndex, _recipient, _tokenData, _saltfun_o, _collectionID);
        uint timeOfLastMint;
        // check 1 per period
        if (lastMintDate[_collectionID] == 0) {
        // for public sale set the allowlist the same time as publicsale
            timeOfLastMint = collectionPhases[_collectionID].allowlistStartTime - collectionPhases[_collectionID].timePeriod;
        } else {
            timeOfLastMint =  lastMintDate[_collectionID];
        }
        // uint calculates if period has passed in order to allow minting
>>      uint tDiff = (block.timestamp - timeOfLastMint) / collectionPhases[_collectionID].timePeriod;
        // users are able to mint after a day passes
>>      require(tDiff>=1, "1 mint/period");
        lastMintDate[_collectionID] = collectionPhases[_collectionID].allowlistStartTime + (collectionPhases[_collectionID].timePeriod * (gencore.viewCirSupply(_collectionID) - 1));
        mintToAuctionData[mintIndex] = _auctionEndTime;
        mintToAuctionStatus[mintIndex] = true;
    }
```
[276-298](https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/MinterContract.sol#L276-L298)
Every time `mintAndAuction` is called when `collectionPhases[_collectionID].timePeriod` is zero, the transaction will revert with `division by zero` error.

To replicate the error:
1. Create a separate `POCs.test.js` file in a test folder, copy and paste the code b