xanoutas/solidity-security-findings huggingface dataset:
Integer Overflow/Underflow and Manipulation of `quorumVotes`.
Audit: 2023-07-nounsdao-findings
Severity: HIGH

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOV3DynamicQuorum.sol#L32-L44


# Vulnerability details

## Impact
If the `totalSupply` value is manipulated, the quorum calculation can be distorted, leading to proposals either becoming impossible to pass or passing too easily, depending on the manipulated value. This can undermine the integrity and fairness of the governance process. If an attacker gains control over the `quorumVotes` value, they can reduce the required quorum votes for a proposal. This enables the attacker to easily pass malicious proposals, potentially causing harm to the contract or its users.

## Proof of Concept
[NounsDAOV3DynamicQuorum.sol#L32-L42](https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOV3DynamicQuorum.sol#L32-L44)
```solidity
    function quorumVotes(NounsDAOStorageV3.StorageV3 storage ds, uint256 proposalId) internal view returns (uint256) {
        NounsDAOStorageV3.Proposal storage proposal = ds._proposals[proposalId];
        if (proposal.totalSupply == 0) {
            return proposal.quorumVotes;
        }


        return
            dynamicQuorumVotes(
                proposal.againstVotes,
                proposal.totalSupply,
                getDynamicQuorumParamsAt(ds, proposal.creationBlock)
            );
    }
```
The `quorumVotes` function in the `NounsDAOV3DynamicQuorum` library calculates the required quorum votes for a specific proposal to succeed, here are the potential vulnerabilities:

1. Integer Overflow/Underflow:
   - Vulnerable code block: `if (proposal.totalSupply == 0) { return proposal.quorumVotes; }`
   - Explanation: If the `proposal.totalSupply` value is manipulated to overflow or underflow, it may lead to incorrect quorum calculations. An attacker could intentionally modify the `totalSupply` value to manipulate the required quorum votes for a proposal.

2. Manipulating `proposal.quorumVotes`:
   - Vulnerable code block: `return dynamicQuorumVotes(proposal.againstVotes, proposal.totalSupply, getDynamicQuorumParamsAt(ds, proposal.creationBlock));`
   - Explanation: If an attacker gains control over the `proposal.quorumVotes` value, they can manipulate the required quorum votes for a proposal. This could potentially be done by exploiting vulnerabilities in other parts of the contract that modify the `quorumVotes` value.

If an attacker gains control over the `proposal.quorumVotes` value and manipulates it to exceed the maximum value of `params.minQuorumVotesBPS,` an integer overflow can occur in the above line of code. This overflow may lead to incorrect quorum calculations and potentially enable a successful proposal that should have failed.

## Tools Used
Manual Review

## Recommended Mitigation Steps
The following measures are recommended:

1. Integer Overflow/Underflow:
   - Imple
Phishing with `tx.origin`
Audit: 2023-07-nounsdao-findings
Severity: HIGH

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOLogicV2.sol#L1033-L1046


# Vulnerability details

### Impact
Phishing with tx.origin

The external function `castRefundableVoteWithReason` that can be called directly by a user or by another contract, to internally call the `castRefundableVoteInternal` function, which could potentially lead to a call to the `_refundGas` function. This function sends some funds to `tx.origin`.

The use of `tx.origin` carries risks. In this case, the risks primarily manifest in the following ways:
1. Front-end Phishing Attacks: A malicious front-end app could let a user vote on a proposal and then call castRefundableVoteWithReason in the background. Since this function sends funds to `tx.origin`, and `tx.origin` would be the address of the malicious front-end app in this case, the app could steal the user's funds.
2. Contract Phishing Attacks: A malicious contract could lure a user into calling one of its functions that then calls castRefundableVoteWithReason in the background. Since this function sends funds to `tx.origin`, and `tx.origin` would be the address of the malicious contract in this case, the contract could steal the user's funds.

## Findings
Total: 2

[nouns-monorepo/packages/nouns-contracts/contracts/governance/NounsDAOLogicV2.sol#L1033-L1046](https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOLogicV2.sol#L1033-L1046)
```solidity
    function _refundGas(uint256 startGas) internal {
        unchecked {
            uint256 balance = address(this).balance;
            if (balance == 0) {
                return;
            }
            uint256 basefee = min(block.basefee, MAX_REFUND_BASE_FEE);
            uint256 gasPrice = min(tx.gasprice, basefee + MAX_REFUND_PRIORITY_FEE);
            uint256 gasUsed = min(startGas - gasleft() + REFUND_BASE_GAS, MAX_REFUND_GAS_USED);
            uint256 refundAmount = min(gasPrice * gasUsed, balance);
            (bool refundSent, ) = tx.origin.call{ value: refundAmount }('');
            emit RefundableVote(tx.origin, refundAmount, refundSent);
        }
    }
```


## Recommendation
A possible solution would be to replace tx.origin with msg.sender.



## Assessed type

Payable
In NounsDAOLogicV1Fork.sol, proposal execution will be failed by execute() function
Audit: 2023-07-nounsdao-findings
Severity: HIGH

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/newdao/governance/NounsDAOLogicV1Fork.sol#L428


# Vulnerability details

## Impact
In NounsDAOLogicV1Fork.sol, execute() function is used to execute a queued proposal if eta has passed in contract.

```Solidity
File: nouns-contracts/contracts/governance/fork/newdao/governance/NounsDAOLogicV1Fork.sol

428    function execute(uint256 proposalId) external {
429        require(
430            state(proposalId) == ProposalState.Queued,
431            'NounsDAO::execute: proposal can only be executed if it is queued'
432        );
433        Proposal storage proposal = _proposals[proposalId];
434        proposal.executed = true;
435        for (uint256 i = 0; i < proposal.targets.length; i++) {
436            timelock.executeTransaction(
437                proposal.targets[i],
438                proposal.values[i],
439                proposal.signatures[i],
440                proposal.calldatas[i],
441                proposal.eta
442            );
443        }
444        emit ProposalExecuted(proposalId);
445    }
```

The issue is execute() function is not payable which means it will not be able to spend the eth for proposals. see below function used from timelock contract here, timelockContract is NounsDAOExecutorV2.sol which has function executeTransaction() as below,

```Solidity
File: nouns-contracts/contracts/governance/NounsDAOExecutorV2.sol

165    function executeTransaction(
        address target,
        uint256 value,
        string memory signature,
        bytes memory data,
        uint256 eta
    ) public returns (bytes memory) {




196        (bool success, bytes memory returnData) = target.call{ value: value }(callData);
        require(success, 'NounsDAOExecutor::executeTransaction: Transaction execution reverted.');

        emit ExecuteTransaction(txHash, target, value, signature, data, eta);

        return returnData;
202    }
```

At L-196, the low level call function takes value param from input i.e eth value and to accept eth by this function it needs to be payable. 

Now, coming back to at NounsDAOLogicV1Fork.sol L-428, execute() function will fail as eth value along the call wont be able to transfer to successfully execute the proposal.

To mitigate this issue, add payable modifier on execute().

## References
Per the NounsDAOLogicV1Fork.sol,
```Solidity
// NounsDAOLogicV1Fork.sol is a modified version of NounsDAOLogicV1.sol.
// NounsDAOLogicV1.sol is a modified version of Compound Lab's GovernorBravoDelegate.sol:
```
Therefore, Let's take an example of compound GovernorBravoDelegate.sol and see the execute() function,

```Solidity
File: contracts/Governance/GovernorBravoDelegate.sol

    function execute(uint proposalId) external payable {
        require(state(proposalId) == ProposalState.Queued, "GovernorBravo::execute: proposal can only be executed if it is queued
DAO fork voter can drain fork ERC20 funds by calling quit function with providing same ERC20 address inside `erc20TokensToInclude` array
Audit: 2023-07-nounsdao-findings
Severity: HIGH

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/newdao/governance/NounsDAOLogicV1Fork.sol#L206-L216
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/newdao/governance/NounsDAOLogicV1Fork.sol#L231-L234
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/newdao/governance/NounsDAOLogicV1Fork.sol#L238-L242


# Vulnerability details

## Impact
DAO Fork voter can drain ERC20 funds by calling `quit` function, providing the same ERC20 inside `erc20TokensToInclude` array multiple times, using minimal nouns token amount.

## Proof of Concept
If nouns token holders contribute to the fork or join the fork, they can claim the nouns fork token and directly quit the fork to get the eth and ERC20 funds. However, `quit` function inside `NounsDAOLogicV1Fork` that provide this mechanism is problematic.

This function let user to choose which ERC20 that they want to get as long as it registered inside the fork token, and they will get the amount based on theirs nouns token amount proportional to total supply. But it doesn't check if ERC20 tokens inside `erc20TokensToInclude` are unique. Malicious user can provide the same ERC20 token multiple inside the array and get more funds than he supposed to get as long as `erc20TokensToInclude` inside `erc20TokensToIncludeInQuit`.

https://github.com/nounsDAO/nouns-monorepo/tree/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/newdao/governance#L206-L216

```solidity
    function quit(uint256[] calldata tokenIds, address[] memory erc20TokensToInclude) external nonReentrant {
        // check that erc20TokensToInclude is a subset of `erc20TokensToIncludeInQuit`
        address[] memory erc20TokensToIncludeInQuit_ = erc20TokensToIncludeInQuit;
        for (uint256 i = 0; i < erc20TokensToInclude.length; i++) {
            // @audit - only check if it is inside erc20TokensToIncludeInQuit_, not checking duplicate
            if (!isAddressIn(erc20TokensToInclude[i], erc20TokensToIncludeInQuit_)) {
                revert TokensMustBeASubsetOfWhitelistedTokens();
            }
        }

        quitInternal(tokenIds, erc20TokensToInclude);
    }
```

Inside the `quitInternal`, the calculation of this arrays of ERC20 token is done, still no check if there is no duplicate.

https://github.com/nounsDAO/nouns-monorepo/tree/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/newdao/governance#L218-L245

```solidity
    function quitInternal(uint256[] calldata tokenIds, address[] memory erc20TokensToInclude) internal {
        checkGovernanceActive();

        uint256 totalSupply = adjustedTotalSupply();

        for (uint256 i = 0; i < tokenIds.length; i++) {
quorumVotesBPS property of StorageV3 is never set, allowing first proposals always to be accepted
Audit: 2023-07-nounsdao-findings
Severity: HIGH

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOInterfaces.sol#L671


# Vulnerability details

## Impact
`quorumVotesBPS` property of the StorageV3 ds; struct is never set which would leave it initialized with default value of zero. This would break contract's function and would cause proposals to be accepted without people voting **YES**.

First proposals of V3 always will be accepted.

## Proof of Concept
The property of `StorageV3 ds` in `NounsDAOStorageV3` contract - `quorumVotesBPS` is never set to an actual value.

- The value is used in `NounsDAOV3DynamicQuorum.sol#getDynamicQuorumParamsAt()` calculation which would make the function return `minQuorumVotesBPS` and `maxQuorumVotesBPS` as **Zero** if the conditions on lines `85` or `98` are met.
- Later this value is used in calculation of the minimum nouns voted **YES** for certain proposal using the `bps2Uint(bps, number)` function which would also return zero if one of its arguments are set to 0
- This would result the condition of minimum number of people voting **YES** on certain proposal to be 0 which would always be met.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Set a default value to `ds.quorumVotesBPS`


## Assessed type

Other
Auction house can break or users can lose their NFT
Audit: 2023-07-nounsdao-findings
Severity: HIGH

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/newdao/token/NounsTokenFork.sol#L148


# Vulnerability details

## Impact
The claimFromEscrow shows that those who have escrowed their token to before the fork can claim their token. The problem is that claimDuringForkPeriod() increments the currentId which can be equel to an id that hava not been claimed from escrow. if a user claims such id at point and the current id is not changed, then auction would fail trying to mint an id that already exists or if auctions mints first then the usser wont be able to mint such id again and have lost the token

## Proof of Concept
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/test/foundry/governance/fork/NounsTokenFork.t.sol#L89C1-L100C6

```solidity
     function test_mintsTokensSameIDsSameSeeds() public {
        vm.prank(originalDAO);
        token.claimDuringForkPeriod(recipient, tokenIds);

        assertEq(token.balanceOf(recipient), 3);
        assertEq(token.ownerOf(1), recipient);
        assertEq(token.ownerOf(4), recipient);
        assertEq(token.ownerOf(2), recipient);
        assertEq(token.tokenURI(1), originalToken.tokenURI(1));
        assertEq(token.tokenURI(4), originalToken.tokenURI(4));
        assertEq(token.tokenURI(2), originalToken.tokenURI(2));
        console.log(token._currentNounId(),"current ID nuons after claimDuringForkPeriod");

        uint256[] memory ids = new uint256[](1);
        ids[0] = 5;

        escrow.setOwnerOfTokens(nouner, ids);


        vm.prank(nouner);
        token.claimFromEscrow(ids);

        console.log(token._currentNounId(),"current ID nuons after claimFromEscrow");
        vm.prank(minter);
        token.mint();
    }
}
``` 
The token mint fails because the id has already been minted


## Tools Used
manuel review

## Recommended Mitigation Steps
Auction should only begin after token remaining have been claimed or given time


## Assessed type

DoS
The admin can initialize any number of times
Audit: 2023-07-nounsdao-findings
Severity: HIGH

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOLogicV3.sol#L141


# Vulnerability details

## Impact
Changing the values of variables such as :timelock_, nouns_, forkEscrow_, forkDAODeployer_, vetoer_, daoParams_, and dynamicQuorumParams_ 
can have significant impacts on the contract's governance process, token functionality, fund management, veto power, and overall decision-making dynamics. 
These changes can alter the executor, token contract, escrow functionality, DAO deployment mechanism, vetoing authority, governance parameters, and quorum requirements. 
Modifying these variables without careful consideration can lead to unintended consequences, including disruptions in governance, voting irregularities and financial implications.

## Proof of Concept

    function initialize( address timelock_,  /../  ) public virtual {
        /../
        if (timelock_ == address(0)) revert InvalidTimelockAddress();
        /../
        ds.timelock = INounsDAOExecutorV2(timelock_);
        /../
    }

The admin can break the if condition by calling _setTimelocksAndAdmin() and set the timelock_ value to 0.

    function _setTimelocksAndAdmin(
        address newTimelock,
        address newTimelockV1,
        address newAdmin
    ) external {
        ds._setTimelocksAndAdmin(newTimelock, newTimelockV1, newAdmin);
    }

Thus, the admin can bypass the if condition and reinitialize the contract.

## Tools Used
Manual review

## Recommended Mitigation Steps
add the openzeppelin initializer modifier


## Assessed type

Invalid Validation
Tokens left in the escrow get locked once the escrow is closed
Audit: 2023-07-nounsdao-findings
Severity: HIGH

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOForkEscrow.sol#L149
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOForkEscrow.sol#L133


# Vulnerability details

## Impact
Tokens remaining in the escrow after it has been closed will become inaccessible to the DAO.
 The ownership check in the withdrawTokens function restricts token withdrawal to only the owner of the tokens, which in this case are the users.
 However, due to the ownership check not being modified after closing the escrow, the DAO will be unable to withdraw the tokens from the closed escrow.
 As a result, the locked tokens will be effectively inaccessible and unusable by the DAO. 
This will lead to a loss of funds and utility of the tokens held in the escrow.

## Proof of Concept
As the comment states , tokens left in escrow after it is closed can only be withdrawn by the owner

     /** Once the escrow is closed, all the escrowed tokens can no longer be unescrowed by the owner, but can be withdrawn by the DAO. 
    function closeEscrow() external onlyDAO returns (uint32 closedForkId) {
        numTokensInEscrow = 0;
        closedForkId = forkId;
        forkId++;
    }

since currentOwnerOf(tokenIds[i]) will be the owners of the tokens , the function will revert

     * @notice Withdraws nouns tokens to the DAO Can only be called by the DAO contract
    function withdrawTokens(uint256[] calldata tokenIds, address to) external onlyDAO {
        for (uint256 i = 0; i < tokenIds.length; i++) {
            if (currentOwnerOf(tokenIds[i]) != dao) revert NotOwner();

            nounsToken.transferFrom(address(this), to, tokenIds[i]);
        }
    }
    
## Tools Used
Manual review

## Recommended Mitigation Steps
Update the withdrawTokens function to allow the DAO to withdraw tokens from the closed escrow by modifying the ownership check condition


## Assessed type

Invalid Validation
User can steal tokens by using duplicated ERC20 tokens as parameter in NounsDAOLogicV1Fork.quit
Audit: 2023-07-nounsdao-findings
Severity: HIGH

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/newdao/governance/NounsDAOLogicV1Fork.sol#L206-L216


# Vulnerability details

## Impact
Calling [NounsDAOLogicV1Fork.quit](https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/newdao/governance/NounsDAOLogicV1Fork.sol#L206-L216) by using dupliated ERC20 tokens, malicious user can gain more ERC20 tokens than he/she is supposed to, even drain all ERC20 tokens

## Proof of Concept
In function, [NounsDAOLogicV1Fork.quit](https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/newdao/governance/NounsDAOLogicV1Fork.sol#L206-L216), `erc20TokensToInclude` is used to specified tokens a user wants to get, but since the function doesn't verify if `erc20TokensToInclude` contains dupliated tokens, it's possible that a malicious user calls the function by specify the ERC20 more than once to get more share tokens
```solidity
    function quit(uint256[] calldata tokenIds, address[] memory erc20TokensToInclude) external nonReentrant {
        // check that erc20TokensToInclude is a subset of `erc20TokensToIncludeInQuit`
        address[] memory erc20TokensToIncludeInQuit_ = erc20TokensToIncludeInQuit;
        for (uint256 i = 0; i < erc20TokensToInclude.length; i++) {
            if (!isAddressIn(erc20TokensToInclude[i], erc20TokensToIncludeInQuit_)) {
                revert TokensMustBeASubsetOfWhitelistedTokens();
            }
        }

        quitInternal(tokenIds, erc20TokensToInclude);
    }

    function quitInternal(uint256[] calldata tokenIds, address[] memory erc20TokensToInclude) internal {
        checkGovernanceActive();

        uint256 totalSupply = adjustedTotalSupply();

        for (uint256 i = 0; i < tokenIds.length; i++) {
            nouns.transferFrom(msg.sender, address(timelock), tokenIds[i]);
        }

        uint256[] memory balancesToSend = new uint256[](erc20TokensToInclude.length);

        // Capture balances to send before actually sending them, to avoid the risk of external calls changing balances.
        uint256 ethToSend = (address(timelock).balance * tokenIds.length) / totalSupply;
        for (uint256 i = 0; i < erc20TokensToInclude.length; i++) {
            IERC20 erc20token = IERC20(erc20TokensToInclude[i]);
            balancesToSend[i] = (erc20token.balanceOf(address(timelock)) * tokenIds.length) / totalSupply;
        }

        // Send ETH and ERC20 tokens
        timelock.sendETH(payable(msg.sender), ethToSend);
        for (uint256 i = 0; i < erc20TokensToInclude.length; i++) {
            if (balancesToSend[i] > 0) {
                timelock.sendERC20(msg.sender, erc20TokensToInclude[i], balancesToSend[i]);
            }
        }

        emit Quit(msg.sender, tokenIds);
    }
```

Add the