xanoutas/solidity-security-findings huggingface dataset:
The fork escrow voting should use the snapshot mechanism to save whether the current DAO state reaches the fork threshold
Audit: 2023-07-nounsdao-findings
Severity: HIGH

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOV3Fork.sol#L119


# Vulnerability details

## Impact
The fork escrow vote does not use the snapshot mechanism (or checkpoint mechanism) to save whether the current DAO state reaches the fork threshold, which may cause the timing of the fork to be missed.

## Proof of Concept
Suppose the following scenario:
1. When BlockNumber= N, Alice calls the `escrowToFork` function to contribute to the fork threshold, assuming that the system just meets the fork threshold at this time. But no one calls the `executeFork` function to execute the fork at this time, and the system is temporarily in the `Escrow Period` stage.

2. Since the fork operation needs to consume a relatively large gas cost, we assume that there will be DAO members calling the `executeFork` function to execute the fork after **1 minute**.

3. The system will auction off a new Noun NFT every day. If a new Noun NFT is generated exactly in the **blank 1 minute**, the total supply of Noun will increase, making the fork threshold higher, which should be able to execute The wrong execution timing of the fork.

## Tools Used
manual

## Recommended Mitigation Steps

Use the snapshot mechanism to save whether the current DAO has reached the fork threshold, and consider adding a new structure:
```solidity
struct SnapshotFork {
     /// @notice Is the system at this time reaching ForkThreshold?
    bool isReachForkThreshold;
     /// @notice expiration time
    uint256 expireTime;
}
```
For example, when the user calls the `escrowToFork` function to contribute to the fork threshold, if the fork threshold is reached at this time to generate a checkpoint record, note: `expireTime = block.timestamp + N;` The value of N determines the snapshot Survival time, at the same time, the judgment logic of `SnapshotFork` structure should be added in `executeFork`, if
 `isReachForkThreshold` is 1 and the current time has not expired, then there is no need to check the fork threshold.





## Assessed type

Other
Same proposer can make duplicate proposals 
Audit: 2023-07-nounsdao-findings
Severity: HIGH

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOLogicV1.sol#L174-L273


# Vulnerability details

## Impact
A proposer can continuously create new proposals, even if they are redundant or unnecessary. This can overload the system and make it difficult for other participants to navigate through legitimate proposals.

## Proof of Concept
In the propose function, there is a check to ensure that the proposer has enough votes to meet the proposal threshold, but there is no specific verification to prevent duplicate proposals from the same proposer. The code only checks if the proposer has an already active or pending proposal, but it doesn't check if the proposer has already submitted a proposal with the same details.
## Tools Used
Manual
## Recommended Mitigation Steps
Maintain a mapping of proposers and their latest proposal IDs, and check if a proposer has a live proposal before allowing them to submit a new proposal.




## Assessed type

Access Control
Execution of incomplete or mismatched actions due to arrays of different lengths 
Audit: 2023-07-nounsdao-findings
Severity: HIGH

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOLogicV1.sol#L174-L198


# Vulnerability details

## Impact
an attacker can pass different-sized arrays for these parameters. For example, if the attacker passes an array of length 3 for targets, but arrays of length 2 for values, signatures, and calldatas, the function will still proceed and store the proposal with incorrect data.
As a result, when the proposal is executed later on, the contract will attempt to execute actions using mismatched arrays. This can lead to unexpected behavior, such as calling incorrect functions or passing incorrect arguments to functions. It can also potentially cause the contract to enter an inconsistent state or result in failed transactions.
## Proof of Concept
The propose function takes arrays of addresses (targets), uint256 values (values), strings (signatures), and bytes (calldatas) as parameters, representing the actions to be executed in the proposal. It requires that the lengths of all these arrays are equal. However, it does not verify this condition. If the lengths of these arrays do not match, it can lead to incorrect function calls and unexpected behavior.
For example, if the targets array has fewer elements than the other arrays, the proposal will only execute the actions corresponding to the common elements in all arrays. This can result in incomplete or invalid proposals. On the other hand, if the targets array has more elements than the other arrays, the proposal will try to execute actions with empty or uninitialized values, which can lead to errors or unexpected behavior.
## Tools Used

## Recommended Mitigation Steps
add requirements the function that will revert if the lengths of the arrays are not equal, preventing incorrect function calls and unintended behavior


## Assessed type

Invalid Validation
`executeTransaction` function allows executing a queued transaction.
Audit: 2023-07-nounsdao-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
The `executeTransaction` function allows executing a queued transaction. It requires the caller to be the admin, verifies the transaction's queue status and time lock, and executes the transaction. The use of target.call this can be exploit it by an attacker if the target contract is not properly secured against them.
 here is the problem were ;
```solidity
    function executeTransaction(
        address target,
        uint256 value,
        string memory signature,
        bytes memory data,
        uint256 eta
    ) public returns (bytes memory) {
        require(msg.sender == admin, 'NounsDAOExecutor::executeTransaction: Call must come from admin.');

        bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
        require(queuedTransactions[txHash], "NounsDAOExecutor::executeTransaction: Transaction hasn't been queued.");
        require(
            getBlockTimestamp() >= eta,
            "NounsDAOExecutor::executeTransaction: Transaction hasn't surpassed time lock."
        );
        require(
            getBlockTimestamp() <= eta + GRACE_PERIOD,
            'NounsDAOExecutor::executeTransaction: Transaction is stale.'
        );

        queuedTransactions[txHash] = false;

        bytes memory callData;

        if (bytes(signature).length == 0) {
            callData = data;
        } else {
            callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);
        }

        // solium-disable-next-line security/no-call-value
        (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;
    }
```

## Proof of Concept
so this vulnerability is causing financial lose for the contract let's say the contract has a balance of
 `100 ETH`. The attacker could send a transaction to the `executeTransaction` function with a value of `0`. The function would then call a function in the target contract that withdraws `100 ETH` from the first contract.
The first contract would then call back to the target contract, and the attacker would be able to withdraw another `100 ETH` from the first contract. This process could be repeated as many times as the attacker wants, resulting in a significant financial loss for the contract.
## Tools Used
manual review line by line 
## Recommended Mitigation Steps
To solve this problem it is recommended to follow the checks-effects-interactions pattern and perform any state updates before making external calls.



## Assessed type

Reentrancy
ABI encodePacked Collision
Audit: 2023-07-nounsdao-findings
Severity: HIGH

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOExecutorV2.sol#L192
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOLogicV2.sol#L598


# Vulnerability details

## Impact
Collision occurs

## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.
https://github.com/crytic/slither/wiki/Detector-Documentation#abi-encodepacked-collision

## Tools Used

## Recommended Mitigation Steps
Do not use more than one dynamic type in abi.encodePacked() (see the Solidity documentation https://docs.soliditylang.org/en/v0.5.10/abi-spec.html?highlight=abi.encodePacked#non-standard-packed-modeDynamic). Use abi.encode(), preferably.


## Assessed type

Other
Anyone can cancel a future proposal
Audit: 2023-07-nounsdao-findings
Severity: HIGH

# Lines of code

https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOV3Proposals.sol#L571-L613


# Vulnerability details

## Impact
Anyone can call `NounsDAOLogicV3#cancel` with a future proposalId(proposalId>ds.proposalCount), which would prevent the proposal with that proposalId from gaining votes, getting queued, or getting executed.
So adversary can easily prevent any proposal which does not favour him from getting executed.

## Proof of Concept
The `NounsDAOV3Proposals#cancel` function allows anyone to cancel a proposal whose proposer's voting power has gone below threshold.

Here is the function:
```solidity
    function cancel(NounsDAOStorageV3.StorageV3 storage ds, uint256 proposalId) external {
        NounsDAOStorageV3.ProposalState proposalState = stateInternal(ds, proposalId);
        if (
            proposalState == NounsDAOStorageV3.ProposalState.Canceled ||
            proposalState == NounsDAOStorageV3.ProposalState.Defeated ||
            proposalState == NounsDAOStorageV3.ProposalState.Expired ||
            proposalState == NounsDAOStorageV3.ProposalState.Executed ||
            proposalState == NounsDAOStorageV3.ProposalState.Vetoed
        ) {
            revert CantCancelProposalAtFinalState();
        }
        NounsDAOStorageV3.Proposal storage proposal = ds._proposals[proposalId];
        address proposer = proposal.proposer;
        NounsTokenLike nouns = ds.nouns;

        uint256 votes = nouns.getPriorVotes(proposer, block.number - 1);
        bool msgSenderIsProposer = proposer == msg.sender;
        address[] memory signers = proposal.signers;
        for (uint256 i = 0; i < signers.length; ++i) {
            msgSenderIsProposer = msgSenderIsProposer || msg.sender == signers[i];
            votes += nouns.getPriorVotes(signers[i], block.number - 1);
        }

        require(
            msgSenderIsProposer || votes <= proposal.proposalThreshold,
            'NounsDAO::cancel: proposer above threshold'
        );

        proposal.canceled = true;
        INounsDAOExecutor timelock = getProposalTimelock(ds, proposal);
        for (uint256 i = 0; i < proposal.targets.length; i++) {
            timelock.cancelTransaction(
                proposal.targets[i],
                proposal.values[i],
                proposal.signatures[i],
                proposal.calldatas[i],
                proposal.eta
            );
        }

        emit ProposalCanceled(proposalId);
    }
```

The problem is that the function allows cancellation of a proposal with proposalId greater than global proposalId, which would allow the following attack scenario:
- ds.proposalCount is 10
- Adversary calls `NounsDAOLogicV3#cancel` with a proposalId of 15 which will call `NounsDAOV3Proposal#cancel` which does the following:
    - proposalState of proposalId 15 is fetched, which will return "Pending"(that is the default state for the ProposalState enum)
dynamicQuorumVotes calculation has accuracy error resulting in the less required quorum
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#L58-L68
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOLogicV2.sol#L962-L972


# Vulnerability details

## Impact

`dynamicQuorumVotes` is divided by `totalSupply`, multiplied by `quorumCoefficient`, divided by `1e6` and then multiplied by `totalSupply`. There are precision errors in division before multiply. 
For `quorumAdjustmentBPS`, the division precision error is 1 and multiplied by `quorumCoefficient` will magnify the error by the corresponding multiple. And because `quorumCoefficient` is a fixed point integer with 6 decimals, the error is large.

## Proof of Concept

```solidity
    function dynamicQuorumVotes(
        uint256 againstVotes,
        uint256 totalSupply,
        NounsDAOStorageV3.DynamicQuorumParams memory params
    ) public pure returns (uint256) {
        uint256 againstVotesBPS = (10000 * againstVotes) / totalSupply;
        uint256 quorumAdjustmentBPS = (params.quorumCoefficient * againstVotesBPS) / 1e6;
        uint256 adjustedQuorumBPS = params.minQuorumVotesBPS + quorumAdjustmentBPS;
        uint256 quorumBPS = min(params.maxQuorumVotesBPS, adjustedQuorumBPS);
        return bps2Uint(quorumBPS, totalSupply);
    }
```

In general, as mentioned above, for `quorumAdjustmentBPS`, the accuracy error is `params.quorumCoefficient`, and the final calculated quorumVotes is smaller than the actual value.
There are also a special boundary cases: when `totalSupply > 10000 * againstVotes`, `againstVotesBPS = 0`, the final calculation is always `params.minQuorumVotesBPS`, regardless of `params.quorumCoefficient`. This is not in line with expectations, because when `params.quorumCoefficient` is large, `quorumAdjustmentBPS` should not be 0, but this problem occurs due to the accuracy error.
Later divide by `1e6` and then multiply `totalSupply` is same problem.

## Tools Used

Manual review

## Recommended Mitigation Steps

Multiply before divide





## Assessed type

Math
Admin user has an absolute power to withdraw all contract balance, which may raise red flags for investors 
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#L818-L829


# Vulnerability details

## Impact

Having rug-pull related code is always considered as a red flag for new investors. An admin, who's a single point of failure has access to `_withdraw` function, which allows to withdraw the whole contract balance. Even if the owner is genuine the rug pull vector available might negatively impact the protocol’s reputation.

The very similar issues have been already reported at Code4rena, e.g.: 
* https://github.com/code-423n4/2021-11-malt-findings/issues/125
* https://github.com/code-423n4/2021-08-realitycards-findings/issues/73


## Proof of Concept


```
File: contracts/governance/NounsDAOLogicV2.sol

    function _withdraw() external returns (uint256, bool) {
        if (msg.sender != admin) {
            revert AdminOnly();
        }

        uint256 amount = address(this).balance;
        (bool sent, ) = msg.sender.call{ value: amount }('');

        emit Withdraw(amount, sent);

        return (amount, sent);
    }
```

`_withdraw` can be called by admin. It allows him/her to withdraw the whole contract balance. 

It's also important to notice, that:

* admin is a single point of failure - this increases a risk, since even with genuine admin, his/her keys might be stolen or account compromised. 
* admin address can be changed: functions `_setPendingAdmin` and `_acceptAdmin` allows admin to change his/her wallet address and withdraw funds there - this decreases the trust in the contract's transparency, since the admin can change his/her address and withdraw funds there


## Tools Used

Manual code review

## Recommended Mitigation Steps

Use multisig address and hardcode it into contract. Users have to know, where the contract's balance may eventually go.


## Assessed type

Rug-Pull
DoS by cancelling newly created proposals
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#L367


# Vulnerability details

## Impact

Malicious user can `cancel` any freshly created proposal, thus making the goverence not usuable to anyone. Whenever new proposal is being created, a malicious actor can `cancel` it.

## Proof of Concept

Function `cancel` is external, thus it can be called by anyone. There's a single check which defines who can execute this function:

```
File: contracts/governance/NounsDAOLogicV2.sol

require(
            msg.sender == proposal.proposer ||
                nouns.getPriorVotes(proposal.proposer, block.number - 1) <= proposal.proposalThreshold,
            'NounsDAO::cancel: proposer above threshold'
        );
```

According to above - proposal can be cancelled either by a proposal creator, OR, when `getPriorVotes` is smaller than `proposalThreshold`.
Since the attacking scenario affects freshly created proposal, their `getPriorVotes` is always smaller than `proposalThreshold`. This implies, that the condition is always true (false OR true == true).

Malicious user may scan blockchain for new proposals and call `cancel` on them. This will cancel every freshly created proposal.



## Tools Used

Manual code review

## Recommended Mitigation Steps

Only proposal creator and admin should be able to cancel proposal.


## Assessed type

DoS
Vetoed proposals still can be execute or queued
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#L389-L415
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOLogicV2.sol#L298-L316
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/NounsDAOLogicV2.sol#L336-L352


# Vulnerability details

## Impact

Function `veto` does not change the state of the proposal. This means, that when a proposal is in a `ProposalState.Queued` state and then vetoed - it, still can be executed.
The same occurs for proposals in `ProposalState.Succeeded` state - even after being vetoed - they can still be queued and then executed.

## Proof of Concept

Function `veto` sets `vetoed` parameter to true: `proposal.vetoed = true;`, however, it does not modify proposal's state.
Whenever we enter function `queue` or `execute` - there's no check for `proposal.vetoed` parameter. Since `veto` did not modify proposal's state - we can still execute `queue` or `execute` on the vetoed proposals.



## Tools Used

Manual code review

## Recommended Mitigation Steps

Add additional check in `execute` and `queue` functions to make sure, that proposal had not been vetoed.


## Assessed type

Other
Attacker can drain the forked DAO's ERC20s by supplying a list with dupes to `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#L202-L204


# Vulnerability details

The `quit()` function is used to allow members of the forked DAO to ragequit the dao and receive a pro-rata share of the ERC20 tokens that the DAO holds.
One version of this functions allows the user to supply their own list of ERC20 tokens for the function to iterate over.
The issue is that the function doesn't check for duplicates, meaning that if a user supplies a list with the same token repeated 10 times - they'd get a x10 of their pro rata share.

## Impact
Forked DAO tokens can be drained by a single member.



## Proof of Concept
The following PoC demonstrates the issue - the user has only 1/5 share in the DAO but is able to drain the full balance of `token2`.

```diff
diff --git a/packages/nouns-contracts/test/foundry/governance/fork/NounsDAOLogicV1Fork.t.sol b/packages/nouns-contracts/test/foundry/governance/fork/NounsDAOLogicV1Fork.t.sol
index e7e03767..17179177 100644
--- a/packages/nouns-contracts/test/foundry/governance/fork/NounsDAOLogicV1Fork.t.sol
+++ b/packages/nouns-contracts/test/foundry/governance/fork/NounsDAOLogicV1Fork.t.sol
@@ -469,8 +469,11 @@ contract NounsDAOLogicV1Fork_Quit_Test is NounsDAOLogicV1ForkBase {
 
     function test_quit_allowsChoosingErc20TokensToInclude() public {
         vm.prank(quitter);
-        address[] memory tokensToInclude = new address[](1);
-        tokensToInclude[0] = address(token2);
+        address[] memory tokensToInclude = new address[](5);
+        for(uint i =0; i < 5; i++){
+            tokensToInclude[i] = address(token2);
+        }
+
         dao.quit(quitterTokens, tokensToInclude);
 
         assertEq(quitter.balance, 24 ether);

```


Output:
```
Running 1 test for test/foundry/governance/fork/NounsDAOLogicV1Fork.t.sol:NounsDAOLogicV1Fork_Quit_Test
[FAIL. Reason: Assertion failed.] test_quit_allowsChoosingErc20TokensToInclude() (gas: 435452)
Logs:
  Error: a == b not satisfied [uint]
    Expected: 1753
      Actual: 8765
```


## Recommended Mitigation Steps
Check the list for duplicates


## Assessed type

Invalid Validation