xanoutas/solidity-security-findings huggingface dataset:
Malicious pausing the contract
Audit: 2022-09-nouns-builder-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/auction/Auction.sol#L204
https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/auction/Auction.sol#L206
https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/auction/Auction.sol#L235


# Vulnerability details

# Vulnerability details

## Description

There is a function `_createAuction` in `Auction` contract.

It consist the following logic:

```
/// @dev Creates an auction for the next token
function _createAuction() private {
    // Get the next token available for bidding
    try token.mint() returns (uint256 tokenId) {
        **creating of the auction for token with id equal to tokenId**

        // Pause the contract if token minting failed
    } catch Error(string memory) {
        _pause();
    }
}
```

According to the [EIP-150](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-150.md) `call` opcode can consume as most `63/64` of parrent calls' gas. That means `token.mint()` can fail since there will be no gas. 

All in all, if `token.mint()` fail on gas and the rest gas is enough for pausing the contract by calling `_pause` in `catch` statement the contract will be paused.

Please note, that a bug can be exploitable if the token.mint() consume more than 1.500.000 of gas, because 1.500.000 / 64 > 20.000 that need to pause the contract. Also, the logic of `token.mint()` includes traversing the array up to 100 times, that's heavy enough to reach 1.500.000 gas limit. 

## Impact

Contract can be paused by any user by passing special amount of gas for the call of `settleCurrentAndCreateNewAuction` (which consists of two internal calls of `_settleAuction` and `_createAuction` functions).

## Recommended Mitigation Steps

Add a special check for upper bound of `gasLeft` at start of `_createAuction` function.
baseTokenId variable calculation
Audit: 2022-09-nouns-builder-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/token/Token.sol#L71
https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/token/Token.sol#L118


# Vulnerability details

## Description

There is `_addFounders` function in the `Token` contract. There is the following loop:

```
// Used to store the base token id the founder will recieve
uint256 baseTokenId;

// For each token to vest:
for (uint256 j; j < founderPct; ++j) {
    // Get the available token id
    baseTokenId = _getNextTokenId(baseTokenId);

    // Store the founder as the recipient
    tokenRecipient[baseTokenId] = newFounder;

    emit MintScheduled(baseTokenId, founderId, newFounder);

    // Update the base token id
    (baseTokenId += schedule) % 100;
}
```

As we can see line `(baseTokenId += schedule) % 100;` have a bug, there is no reason to do `% 100` operation over the result value of the incrementing `baseTokenId` by `schedule`.

## Recommended Mitigation Steps

We recommend removal of such modulo operation. If case it is necessary to perform a modulo operation over `baseTokenId` inside the loop, it will be correct to do the following:

```
baseTokenId += schedule;
baseTokenId %= 100;
```
It is possible to add more than 15 properties
Audit: 2022-09-nouns-builder-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/token/metadata/MetadataRenderer.sol#L108-L130


# Vulnerability details

The total number of properties is now limited to be 15 or less with hard code on the storage structures level.

In the same time it is possible to add unlimited number of properties with MetadataRenderer's addProperties().

If this happens, with a malicious intent or as the result of an operational mistake, the whole auctioning will become permanently frozen.

## Proof of Concept

The `attributes` mapping has fixed length of `16` for each token's array of attributes:

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/token/metadata/storage/MetadataRendererStorageV1.sol#L19-L20

```solidity
    /// @notice The attributes generated for a token
    mapping(uint256 => uint16[16]) internal attributes;
```

First element, `tokenAttributes[0]`, is dedicated to the currently existing properties count:

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/token/metadata/MetadataRenderer.sol#L171-L185

```solidity
    function onMinted(uint256 _tokenId) external returns (bool) {
        // Ensure the caller is the token contract
        if (msg.sender != settings.token) revert ONLY_TOKEN();

        // Compute some randomness for the token id
        uint256 seed = _generateSeed(_tokenId);

        // Get the pointer to store generated attributes
        uint16[16] storage tokenAttributes = attributes[_tokenId];

        // Cache the total number of properties available
        uint256 numProperties = properties.length;

        // Store the total as reference in the first slot of the token's array of attributes
        tokenAttributes[0] = uint16(numProperties);
```

So, an NFT can have up to 15 properties set.

However, the number of properties addProperties() introduces isn't controlled anyhow, it is possible to add an arbitrary large `numNewProperties`:

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/token/metadata/MetadataRenderer.sol#L108-L130

```solidity
        // Cache the number of existing properties
        uint256 numStoredProperties = properties.length;

        // Cache the number of new properties
        uint256 numNewProperties = _names.length;

        // Cache the number of new items
        uint256 numNewItems = _items.length;

        unchecked {
            // For each new property:
            for (uint256 i = 0; i < numNewProperties; ++i) {
                // Append storage space
                properties.push();

                // Get the new property id
                uint256 propertyId = numStoredProperties + i;

                // Store the property name
                properties[propertyId].name = _names[i];

                emit PropertyAdded(propertyId, _names[i]);
            }
Use safeTransfer to send ERC721 tokens
Audit: 2022-09-nouns-builder-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/auction/Auction.sol#L192


# Vulnerability details

## Impact

Winner of the auction can lose his NFT

## Proof of Concept

When you settle and auction you transfer the NFT using transferFrom

            token.transferFrom(address(this), _auction.highestBidder, _auction.tokenId);

Maybe this is just an intended behaviour and is the responsability of the bidder but maybe you could use a withdraw pattern


## Recommended Mitigation Steps

A solution could be to use a withdraw pattern and check if the receiver cannot handle nft after the send fails
 allow him to `claim` in another functions.    I believe that this change would be consistent and similar to the funciton 
 `_handleOutgoingTransfer`.
Founders may not receive their vesting allocations
Audit: 2022-09-nouns-builder-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/token/Token.sol#L71-L126


# Vulnerability details

## Impact
In [`_addFounders`](https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/token/Token.sol#L71-L126) the vesting allocations are distributed among the first one hundred nouns according to their ownership percentage. These first one hundred represent the token ID:s modulo 100 that should be minted to the nounders, which is how it is calculated in other functions (i.e. `_tokenId % 100`).
However, this modulation is calculated in line 118 as [`(baseTokenId += schedule) % 100;`](https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/token/Token.sol#L118), which does not apply the modulus to `baseTokenId`. This may result in a an allocation being set at a higher index than the 100th.

## Proof of Concept
Suppose six founders each have 1%. They will be allocated indices 0, 1, 2, 3, 4, and 5. And suppose another founder has 20%. He will then recieve indices 5, 10, ..., 95, 100, 105 (a total of 20 indices). But indices 100 and 105 are useless as they are not remainders of 100, and he will only actually receive 18%.

## Tools Used
Code inspection

## Recommended Mitigation Steps
Change line 118 to `baseTokenId = (baseTokenId + schedule) % 100;`
founderPct variable cast
Audit: 2022-09-nouns-builder-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/token/Token.sol#L71
https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/token/Token.sol#L102
https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/token/Token.sol#L108


# Vulnerability details

## Description

There is a function `_addFounders` in `Token` contract. It accepts array of `FounderParams` as an input. For each of founders it uses `founderPct` as an variable to store percent ownership for such founder.

It is unsafe to cast it to from `uint256` to `uint8` in some of places, but not all:

```
...
if ((totalOwnership += uint8(founderPct)) > 100) revert INVALID_FOUNDER_OWNERSHIP();
...
newFounder.ownershipPct = uint8(founderPct);
...
uint256 schedule = 100 / founderPct;
...
// For each token to vest:
for (uint256 j; j < founderPct; ++j) {
    ...
}
...
```

## Impact

Lack of casting `founderPct` to `uint8` type can lead to incorrect calculation of `schedule` variable and incorrect number of iterations in the loop that goes for each token to vest for such founder.

## Recommended Mitigation Steps

Use `uint8` type for `ownershipPct` in `FounderParams` struct or cast `founderPct` to `uint8` type in all possible places in `_addFounders` function.
Contracts that lock Ether
Audit: 2022-09-nouns-builder-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/governance/treasury/Treasury.sol#L269


# Vulnerability details

## Impact
When receive function, hasn't a revert() all ethers send contracts will be locked forever.


## Proof of Concept
Bob send ether to contract, funds were locked forever and Bob lost money.

## Tools Used
manually 

## Recommended Mitigation Steps
Remove the payable attribute or add a withdraw function.
Initial spam of proposals
Audit: 2022-09-nouns-builder-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/main/src/governance/governor/Governor.sol#L111-L175


# Vulnerability details

## Impact
In the initial phase, when not many tokens are minted, a malicious actor can start submitting proposals and later execute them. E.g. when the first token is minted, this first owner can instantly submit proposals to retrieve all the eth back from the treasury. ```proposalThreshold``` and ```quorum``` will likely be low (0 or 1 depending on ```proposalThresholdBps``` and ```quorumThresholdBps```).

Others would not have a chance to block this proposal later because their past votes were 0. The admins can jump in and veto such proposals but this does not prevent an attacker from spamming multiple proposals simultaneously making it infeasible to fight if the attacker is willing to spend enough ETH for gas.

A similar issue is when the total supply is 0. Then anyone can submit proposals because the threshold and quorum are basically 0. Admins will not have enough supplies to combat the aggregated power of all the spammers.

Thus, this initial spam of proposals can become insurmountable even with the veto function.

## Recommended Mitigation Steps
I believe veto is not enough to combat this issue and there should be more on-chain precautions. Consider something like introducing a minimum token supply when the proposals can start or limiting the number of active proposals per token.
# _safeMint() should be used rather than _mint() wherever possible
Audit: 2022-09-nouns-builder-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/debe9b792cc70510eadf9b3728cde5b0f2ec9a1f/src/token/Token.sol#L161
https://github.com/code-423n4/2022-09-nouns-builder/blob/debe9b792cc70510eadf9b3728cde5b0f2ec9a1f/src/token/Token.sol#L169
https://github.com/code-423n4/2022-09-nouns-builder/blob/debe9b792cc70510eadf9b3728cde5b0f2ec9a1f/src/token/Token.sol#L188


# Vulnerability details

# _safeMint() should be used rather than _mint() wherever possible
### Impact 
In `Token.sol.sol`, eventually it is called ERC721 `_mint()`. Calling `_mint()` this way does not ensure that the receiver of the NFT is able to accept them, making possible to lose them. 

`_safeMint()` should be used with as it checks to see if a user can properly accept an NFT and reverts otherwise.

There is no check of the address provided by the mintNFT when creating the project that it implements ERC721Receiver. 

#### Details 
`_mint()` is discouraged in favor of `_safeMint()` which ensures that the recipient is either an EOA or implements IERC721Receiver. 

Both open OpenZeppelin and solmate have versions of this function so that NFTs aren’t lost if they’re minted to contracts that cannot transfer them back out.

References:
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol#L277

#### References
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/d4d8d2ed9798cc3383912a23b5e8d5cb602f7d4b/contracts/token/ERC721/ERC721.sol#L271

### Github Permalinks
https://github.com/code-423n4/2022-09-nouns-builder/blob/debe9b792cc70510eadf9b3728cde5b0f2ec9a1f/src/token/Token.sol#L161
https://github.com/code-423n4/2022-09-nouns-builder/blob/debe9b792cc70510eadf9b3728cde5b0f2ec9a1f/src/token/Token.sol#L169
https://github.com/code-423n4/2022-09-nouns-builder/blob/debe9b792cc70510eadf9b3728cde5b0f2ec9a1f/src/token/Token.sol#L188

### Mitigation
Use `_safeMint()` as suggested by OpenZeppelin or include the check before minting.
Auction.sol : Treasury(payable) typecasting is missed before setting the treasury address
Audit: 2022-09-nouns-builder-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/auction/Auction.sol#L79


# Vulnerability details

## Impact
The contract address will not be set with relevant properties, like payable and contract type.

## Proof of Concept
https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/auction/Auction.sol#L79

`Auction.sol#L79 : settings.treasury = _treasury;`

Whereas, in Governor.sol, it is set properly. refer the line,
https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/governance/governor/Governor.sol#L74

`Governor.sol#L74 :   settings.treasury = Treasury(payable(_treasury));`

## Tools Used

Manual code review

## Recommended Mitigation Steps

Type cast the `_treasury` while assigning as shown below.
`settings.treasury = Treasury(payable(_treasury));`
Ddos in Governor.sol
Audit: 2022-09-nouns-builder-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/governance/governor/Governor.sol#L145
https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/governance/governor/Governor.sol#L128
https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/governance/governor/Governor.sol#L353
https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/governance/governor/Governor.sol#L363


# Vulnerability details

## Impact

An attacker can cancel proposals.


## Proof of Concept


The proposal ID depends on  4 variables:_targets, _values, _calldatas, and descriptionHash.
https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/governance/governor/Governor.sol#L145

The Problem is that once a proposal is proposed and the proposal ID is stored, no matter if the proposal is executed or canceled or whatever, that proposal is never going to be proposable again and never going to be executable again. At most, it is executed 1 time.

A  malicious attacker having proposalThreshold() of voting power can always front-run a proposal, propose it and then cancel it.
That proposal is never going to be proposable again.

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/governance/governor/Governor.sol#L128

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/governance/governor/Governor.sol#L353

https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/governance/governor/Governor.sol#L363

## Tools Used
Manual review

## Recommended Mitigation Steps

Add more parameters to the proposal ID to differentiate two proposals with the same structure but proposed by different persons and you can add a nonce to differentiate the same proposal made by the same person at different times.
Use of transfer() instead of call() to send eth
Audit: 2022-09-nouns-builder-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/debe9b792cc70510eadf9b3728cde5b0f2ec9a1f/src/auction/Auction.sol#L363


# Vulnerability details

# Use of `transfer()` instead of `call()` to send eth
## Impact
Use of `transfer()` might render ETH impossible to withdraw because after istanbul hardfork, there is increases in the gas cost of the `SLOAD` operation and therefore breaks some existing smart contracts.Those contracts will break because their fallback functions used to consume less than `2300` gas, and they’ll now consume more, since `2300` the amount of gas a contract’s fallback function receives if it’s called via Solidity’s `transfer()` or `send()` methods.

Any smart contract that uses `transfer()` or `send()` is taking a hard dependency on gas costs by forwarding a fixed amount of gas: `2300`.

### References
https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/
https://blog.openzeppelin.com/opyn-gamma-protocol-audit/

## Proof of Concept
https://github.com/code-423n4/2022-09-nouns-builder/blob/debe9b792cc70510eadf9b3728cde5b0f2ec9a1f/src/auction/Auction.sol#L363



### Recommended Mitigation Steps
Use `call()` to send eth
An owner can accidentally (or not?) delete the first auction
Audit: 2022-09-nouns-builder-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/main/src/auction/Auction.sol#L247-L254
https://github.com/code-423n4/2022-09-nouns-builder/blob/main/src/auction/Auction.sol#L206-L208
https://github.com/code-423n4/2022-09-nouns-builder/blob/main/src/token/Token.sol#L154


# Vulnerability details

## Impact
Token ids start from 0, however, this value is also checked against when unpausing the contract: 
```solidity
  function unpause() external onlyOwner {
    _unpause();

    // If this is the first auction:
    if (auction.tokenId == 0) {
        // Transfer ownership of the contract to the DAO
        transferOwnership(settings.treasury);

        // Start the first auction
        _createAuction();
    }
    ...
  }
```
If the owner unpauses the auction twice, the second time the value of ```auction.tokenId``` is again 0 meaning the check will pass and re-create the auction again even though the current auction might have active bids.
It is highly likely because I think the highest chances of unpausing are initially if something does not go according to the plan or an owner wants to adjust some parameters before continuing the sale. This could happen accidentally and the current highest bid and NFT #0 will be lost or this could be a griefing attack by the malicious owner which I suppose is very unlikely because the owner has too many privileges to exploit the system anyway.
Yet I think this is a significant flaw in the codebase that should be prevented and deserves a severity of Medium.

## Proof of Concept
PoC of a simplified version:
https://gist.github.com/pauliax/07ba76afb89a431f78eadef488e296e0

Steps to reproduce are in the gist's comments section.

## Recommended Mitigation Steps
Token ids should either not start from 0 or you should enhance this check, e.g. ```if (auction.tokenId == 0 && auction.startTime == 0)```.
A proposal can be canceled when the original proposer has just enough votes
Audit: 2022-09-nouns-builder-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-09-nouns-builder/blob/main/src/governance/governor/Governor.sol#L363-L364


# Vulnerability details

## Impact
This situation happens when the proposer's voting power and ```proposalThreshold``` are equal because the contract does not respect that: 
```solidity
  function cancel(bytes32 _proposalId) external {
    // Ensure the caller is the proposer or the proposer's voting weight has dropped below the proposal threshold
    if (msg.sender != proposal.proposer && getVotes(proposal.proposer, block.timestamp - 1) > proposal.proposalThreshold)
        revert INVALID_CANCEL();
```
The original Nouns contract had these checks in an opposite way:
```solidity
  require(
      msg.sender == proposal.proposer ||
          nouns.getPriorVotes(proposal.proposer, block.number - 1) < proposal.proposalThreshold,
      'NounsDAO::cancel: proposer above threshold'
  );
```
so when inverting, ```<``` should become ```>=```. Now when proposer votes == ```proposal.proposalThreshold``` it allows canceling such a proposal by anyone. This is a huge flow compared to ERC20s-based votes because here this 1 vote means 1 NFT which is supposed to be significant and hard to obtain. E.g. when the ```proposalThreshold``` is 2 NFTs the proposer must have 3 NFTs to avoid being canceled.

## Recommended Mitigation Steps
```getVotes(proposal.proposer, block.timestamp - 1) >= proposal.proposalThreshold```