xanoutas/solidity-security-findings huggingface dataset:
`NounsDAOV3Proposals.cancel()` does not cancel signature used to create proposal if `msg.sender` is co-signer. Co-signer cannot cancel his proposal as long as his signature is valid.
Audit: 2023-07-nounsdao-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
Even if a co-signer of a proposal cancels his co-signed proposal, proposer can propose same proposal again with same list of signatures as long as signature timestamp is valid.

## Proof of Concept
`NounsDAOV3Proposals.cancel()` does not cancel signature used to create proposal if `msg.sender` is co-signer.
```solidity
    function cancel(NounsDAOStorageV3.StorageV3 storage ds, uint256 proposalId) external {
...
        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);
        }
...        
```
```
    function cancelSig(NounsDAOStorageV3.StorageV3 storage ds, bytes calldata sig) external {
        bytes32 sigHash = keccak256(sig);
        ds.cancelledSigs[msg.sender][sigHash] = true;

        emit SignatureCancelled(msg.sender, sig);
    }
```

## Tools Used

## Recommended Mitigation Steps
1. `struct Proposal` should have mapping with co-signer and their signatures.
2. `cancel()` should call `cancelSig()` signature associated with `msg.sender` if `msg.sender` is co-signer.

```solidity
    struct Proposal {
        /// @notice Unique id for looking up a proposal
        uint256 id;
        /// @notice Creator of the proposal
        address proposer;
        ...
        /// @notice The signers of a proposal, when using proposeBySigs
        address[] signers;
+       mapping(address => bytes) signerSignatures;
        ...
    }
```
```solidity
    function cancel(NounsDAOStorageV3.StorageV3 storage ds, uint256 proposalId) external {
...
        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);
        }
        
+       cancelSig(signerSignatures[msg.sender]);
```



## Assessed type

DoS
In NounsAuctionHouseFork.sol, Fixed Amount of Gas Sent in Call May Be Insufficient
Audit: 2023-07-nounsdao-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
In NounsAuctionHouseFork.sol, _safeTransferETH() function is used to transfer the ETH

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

272    function _safeTransferETH(address to, uint256 value) internal returns (bool) {
273        (bool success, ) = to.call{ value: value, gas: 30_000 }(new bytes(0));
274        return success;
275    }
```

The issue here is at L-273, _safeTransferETH() makes a call with a fixed amount of gas, 30,000. If the receiver is a contract this may be insufficient to process the receive() function. As a result the user will not be able to receive funds from this function. Due to this issue, createBid() and _settleAuction() functions will largely suffer. Therefore it is recommended to remove the gas field at L-273.

## Proof of Concept
https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/newdao/NounsAuctionHouseFork.sol#L272-L275

## References
There is an exactly similar Medium severity found in Joyn audit: [Reference link](https://github.com/code-423n4/2022-03-joyn-findings/issues/8)

## Tools Used
Manual review

## Recommended Mitigation Steps
Consider removing the gas field to use the default amount.


## Assessed type

Other
[M-04] `NounsDAOV3Fork.adjustedTotalSupply()`: Forking can affect existing adjusted total supply for proposing proposals
Audit: 2023-07-nounsdao-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
In `NounsDAOV3Fork.executeFork()`, during a governance proposal, any token holder can call `executeFork()` to fork a new Noun DAO provided the fork threshold is met, which stands at 20% of adjustedTotalSupply of tokens (total supply of tokens excluding Noun tokens owned by treasury and DAO). 

To simplify calculations, here is a equation to calculate adjusted total supply with the same meaning as `adjustedTotalSupply()`.
```solidity
adjustedTotalSupply = totalSupply - supplyInTreasury - (supplyInDAO - numTokensInEscrow)
```

Within the `executeFork()` function, it internally calls the `closeEscrow()` function, which in turn sets `numTokensInEscrow = 0` to signify closing of escrow for forking. The `numTokensInEscrow` turning zero directly decreases the `adjustedTotalSupply()` calculated.

[NounsDAOForkEscrow.sol#L133-L134](https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOForkEscrow.sol#L133-L134)
```solidity
function closeEscrow() external onlyDAO returns (uint32 closedForkId) {
    /// @audit numTokensInEscrow zeroed out
 @> numTokensInEscrow = 0;

    closedForkId = forkId;

    forkId++;
}
```

Similarly, nouner's joining new NounDao via `joinFork()` increases supply of Noun tokens in treasury and further decreases `adjustedTotalSupply()`.

[NounsDAOV3Fork.sol#L154](https://github.com/nounsDAO/nouns-monorepo/blob/718211e063d511eeda1084710f6a682955e80dcb/packages/nouns-contracts/contracts/governance/fork/NounsDAOV3Fork.sol#L154)
```solidity
function joinFork(
    NounsDAOStorageV3.StorageV3 storage ds,
    uint256[] calldata tokenIds,
    uint256[] calldata proposalIds,
    string calldata reason
) external {
    if (!isForkPeriodActive(ds)) revert ForkPeriodNotActive();

    INounsDAOForkEscrow forkEscrow = ds.forkEscrow;
    address timelock = address(ds.timelock);
    sendProRataTreasury(ds, ds.forkDAOTreasury, tokenIds.length, adjustedTotalSupply(ds));

    for (uint256 i = 0; i < tokenIds.length; i++) {
        /// @audit treasury balance of Noun tokens increases
@>      ds.nouns.transferFrom(msg.sender, timelock, tokenIds[i]);
    }

    NounsTokenFork(ds.forkDAOToken).claimDuringForkPeriod(msg.sender, tokenIds);

    emit JoinFork(forkEscrow.forkId() - 1, msg.sender, tokenIds, proposalIds, reason);
}
```


The decrease in `adjustedTotalSupply()` during forking period can cause several issues:

1. It opens up the possibility of proposers successfully proposing proposal without meeting proposal threshold since proposal threshold is temporarily decreased.

<br
Escrowed Tokens Not Returned to Owners in closeEscrow Function
Audit: 2023-07-nounsdao-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
The current implementation of the closeEscrow() function in the contract poses a risk to token holders as it allows the DAO to withdraw the escrowed tokens, rather than returning them to their rightful owners. 
This behavior introduces the potential for a rug pull, where locked tokens are taken by the DAO without being returned to the users who own them. Such an action could lead to a loss of trust and negatively impact the token holders' assets.

## Proof of Concept  

     /**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++;
    }

## Tools Used
Manual review

## Recommended Mitigation Steps
To mitigate the risk associated with this issue, it is recommended to modify the closeEscrow() function to transfer the escrowed tokens back to their respective owners. 


## Assessed type

Rug-Pull
NO ACCESS CONTROL IN THE FORK-DAO-DEPLOYER CONTRACT
Audit: 2023-07-nounsdao-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
Detailed description of the impact of this finding.
The function deploy-fork-dao in the DAO-DEPLOYER contract responsible for deploying a group of vital contracts, including Nouns DAO fork, a new Noun token, auction house, governor, and treasury, lacks proper access control. This omission presents a significant security vulnerability.
It is crucial to add access control mechanisms to the deploy-fork-dao() to mitigate potential risks. Since only the NounDao contract should have the authority to call the function, access to the function should be restricted to the NounDao contract exclusively. This measure will prevent unauthorized callers from initializing the contracts and tampering with essential parameters.

Implementing access control will ensure that only trusted entities within the protocol have the privilege to interact with the deploy-fork-dao(). By restricting access, the protocol can maintain the integrity of its core mechanisms and prevent malicious actors from exploiting vulnerabilities.



## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.
 function deployForkDAO(uint256 forkingPeriodEndTimestamp, INounsDAOForkEscrow forkEscrow)
        external
        returns (address treasury, address token)
    {

## Tools Used

## Recommended Mitigation Steps:The absence of access control in the Fork-DAO-Deployer contract poses a significant security risk. It is recommended to implement access control measures to restrict the function's invocation to the NounDao contract. This enhancement will enhance the overall security and robustness of the protocol, safeguarding it against potential attacks and unauthorized modifications.







## Assessed type

Access Control
Anyone can open any position with no init period
Audit: 2023-04-frankencoin-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-04-frankencoin/blob/main/contracts/MintingHub.sol#L88


# Vulnerability details

## Impact
Anyone can immediately open a malicious position by calling openPosition(...) and passing 0 as _initPeriodSeconds.

## Proof of Concept
The function on line https://github.com/code-423n4/2023-04-frankencoin/blob/main/contracts/MintingHub.sol#L88 has public visibility, whereas I suspect it should be private.
This effectively means anyone can mint an arbitrary amount of ZCHF by opening a position with a worthless ERC20 as collateral and calling mint() on it.

## Tools Used
This may even be exploitable without writing a custom ERC20.

## Recommended Mitigation Steps
Change function visibility to private.
Attacker can extract unlimited ZCHF by setting a high price for a position and challenging it
Audit: 2023-04-frankencoin-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-04-frankencoin/blob/1022cb106919fba963a89205d3b90bf62543f68f/contracts/MintingHub.sol#L272


# Vulnerability details

An attacker can act as both minter and challenger, and profit by setting an arbitrarily high price for a position (way higher than what the collateral really is worth), and then immediately challenging the position. After the challenge succeeds, the attacker will obtain 2% of the arbitrarily high amount of ZCHF that could be minted from the collateral at the attacker-controlled price. As the reserve will mint new tokens should it not have enough to cover the reward, the attacker can not only drain the reserve this way, but obtain an arbitrary amount of ZCHF.


## Detailed Explanation

The whitepaper considers what happens with incentives in the liquidation game, should the identites of bidder and one of the other actors overlap, but fails to consider an overlap of the identities of the minter and the challenger.

Suppose the attacker owns a position of little value that is currently not challenged. The attacker can then increase the claimed price of the collateral to a value as high as they wish:
```Solidity
function adjustPrice(uint256 newPrice) public onlyOwner noChallenge {
    if (newPrice > price) {
        restrictMinting(3 days);
    } else {
        checkCollateral(collateralBalance(), newPrice);
    }
    price = newPrice;
    emitUpdate();
}
```
While the attacker won't be able to use the position to *mint* ZCHF at the new, absurdly high, valuation of the collateral, they are able to immediately open a challenge on the position (that there is a period after increasing the price where the minter can not mint yet, but the position can be challenged is exactly the point of restricting minting for 3 days). As we assumed the amount of collateral in the position is in reality not worth much, it is no problem for the attacker to provide that collateral again for the challenge. After some time the challenge will succeed. The reward for the challenger (=the attacker) is then calculated as follows. In the `notifyChallengeSucceeded` function of the `Position` contract `volumeZCHF` is calculated as follows:
```Solidity
uint256 volumeZCHF = _mulD18(price, _size); // How much could have minted with the challenged amount of the collateral
```
As the attacker controls both `price` and `_size` and can in particular set `price` to an arbitrarily large value, the attacker can obtain an arbitrarily large value for `volumeZCHF`.
In the `end` function of the `MintingHub`, the challenger (so the attacker) is then rewarded with `2%` of `volumeZCHF` (here called `volume`).
```Solidity
uint256 reward = (volume * CHALLENGER_REWARD) / 1000_000;
uint256 fundsNeeded = reward + repayment;
if (effectiveBid > fundsNeeded){
    zchf.transfer(owner, effectiveBid - fundsNeeded);
} else if (effectiveBid < fundsNeeded){
    zchf.notifyLoss(fundsNeeded - effectiveBid); // ensure we have enough to pay everyth
ERC777 Re-entrancy Risk
Audit: 2023-04-frankencoin-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-04-frankencoin/blob/1022cb106919fba963a89205d3b90bf62543f68f/contracts/MintingHub.sol#L142


# Vulnerability details

## Impact
The code does not screen out for tokens that use the erc777 standard and therefore presents a re-entrancy risk via the token's callback function

## Proof of Concept
https://github.com/code-423n4/2023-04-frankencoin/blob/1022cb106919fba963a89205d3b90bf62543f68f/contracts/MintingHub.sol#L142

## Tools Used
Manual

## Recommended Mitigation Steps
Add in re-entrancy lock to function
Lack of validation in opening positions parameters can lead to critical vulnerabilities at protocol level
Audit: 2023-04-frankencoin-findings
Severity: HIGH

# Lines of code

https://github.com/Frankencoin-ZCHF/FrankenCoin/blob/main/contracts/MintingHub.sol#L88-L113


# Vulnerability details

Suspicious positions may be denied by voters if they don't seem legit, but over time it is very possible that one of them lands in the protocol, which can involve serious risks.

Some attributes may not seem harmful with certain values at first sight, and can lead to permanent damage to the protocol if a position becomes active.

## Impact

Malicious positions can lead to damage at protocol level, including stealing assets from the reserve, or unrestricted minting of tokens for example.

I will provide two attack vectors to illustrate this. Buy many other can be performed with different `Position` attributes.

## Proof of concept - Attack vector 1

Take for example `challengePeriod = 0`.

There is no restriction for creating positions with `challengePeriod = 0`.

Positions with `challengePeriod = 0` can launch a challenge and end it immediately on the same transaction.

Position owners can launch challenges against their own positions, and as there will be no `bid` period, the protocol will have to pay for the unaverted challenge. It can be done even on the same transaction as launching the challenge.

This leads to stealing reserves from the protocol, or unrestricting minting of ZCHF tokens when reserves are gone.

Add this test to `test/GeneralTest.t.sol` prove how tokens can be minted without restriction. Values can be tweaked to mint bigger amounts:

```solidity
function testZeroChallengePeriod() public {
    // Obtain some tokens to be able to open a position
    alice.obtainFrankencoins(swap, 1000 ether);
    col.mint(address(alice), 100);

    // Open the position with a challenge period of 0
    vm.startPrank(address(alice));
    uint256 challengePeriod = 0;
    col.approve(address(hub), 100);
    address posAddr = hub.openPosition(address(col), 0, 100, 2 ** 255, 3 days, 1, challengePeriod, 0, 10 ether, 0);
    vm.stopPrank();

    // Alice balance is 0
    assertEq(zchf.balanceOf(address(alice)), 0);
    assertEq(col.balanceOf(address(alice)), 0);

    // Wait until the cooldown is off
    skip(3 days + 1);
    
    // Perform the attack
    // Alice launches a challenge with 100 collateral tokens
    col.mint(address(alice), 100);
    vm.startPrank(address(alice));
    col.approve(address(hub), 100);
    Position(posAddr).adjustPrice(Position(posAddr).price() * 100);
    uint256 challengeNumber = hub.launchChallenge(posAddr, 100);
    hub.end(challengeNumber);
    vm.stopPrank();

    // Alice gets her collateral back
    assertEq(col.balanceOf(address(alice)), 200);
    
    // Alice prints ZCHF token out of thin air
    assertEq(zchf.balanceOf(address(alice)), 2000);
}
```

Positions have many values, and few of them are properly validated:

```solidity
function openPosition(
    address _collateralAddress, uint256 _minCollateral, uint256 _initialCollateral,
    uint256 _mintingMaximum, uint256 _initP
Challenges can be frontrun with de-leveraging to cause lossses for challengers
Audit: 2023-04-frankencoin-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-04-frankencoin/blob/1022cb106919fba963a89205d3b90bf62543f68f/contracts/MintingHub.sol#L140-L148


# Vulnerability details

## Impact
Challenges, once created, cannot be closed. Thus once a challenge is created, the challenger has already transferred in a collateral amount and is thus open for losing their collateral to a bidding war which will most likely close below market price, since otherwise buying from the market would be cheaper for bidders.

Position owners can take advantage of this fact and frontrun a `launchChallenge` transaction with an `adjustPrice` transaction. The `adjustPrice` function lets the user lower the price of the position, and can pass the collateral check by sending collateral tokens externally.

As a worst case scenario, consider a case where a position is open with 1 ETH collateral and 1500 ZCHF minted. A challenger challenges the position and the owner frontruns the challenger by sending the contract 1500 ZCHF and calling `repay()` and then calling `adjustPrice` with value 0, all in one transaction with a contract. Now, the price in the contract is set to 0, and the collateral check passes since the outstanding minted amount is 0. The challenger's transaction gets included next, and they are now bidding away their collateral, since any amount of bid will pass the avert collateral check.

The position owner themselves can backrun the same transaction with a bid of 1 wei and take all the challenger's collateral, since every bid checks for the `tryAvertChallenge` condition.
```solidity
if (_bidAmountZCHF * ONE_DEC18 >= price * _collateralAmount)
```

Since price is set to 0, any bid passes this check. This sandwich attack causes immense losses to all challengers in the system, baiting them with bad positions and then sandwiching their challenges.

Since sandwich attacks are extremely commonplace, this is classified as high severity.
## Proof of Concept
The attack can be performed the following steps.

1. Have an undercollateralized position. This can be caused naturally due to market movements.
2. Frontrun challenger's transaction with a repayment and `adjustPrice` call lowering the price.
3. Challenger's call gets included, where they now put up collateral for bids.
4. Backrun challenger's call with a bid such that it triggers the avert.
5. Attacker just claimed the challenger's collateral at their specified bid price, which can be as little as 1 wei if price is 0.

## Tools Used
Manual Review
## Recommended Mitigation Steps
When launching a challenge, ask for a `expectedPrice` argument. If the actual price does not match this expected price, that means that transaction was frontrun and should be reverted. This acts like a slippage check for challenges.
Minter and position privileges allow unrestricted minting and burning of arbitrary tokens
Audit: 2023-04-frankencoin-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-04-frankencoin/blob/1022cb106919fba963a89205d3b90bf62543f68f/contracts/Frankencoin.sol#L266-L269
https://github.com/code-423n4/2023-04-frankencoin/blob/1022cb106919fba963a89205d3b90bf62543f68f/contracts/Frankencoin.sol#L172-L174
https://github.com/code-423n4/2023-04-frankencoin/blob/1022cb106919fba963a89205d3b90bf62543f68f/contracts/Frankencoin.sol#L194-L197


# Vulnerability details

Any registered minter and a position belonging to him have absolute control of minting and burning tokens on all accounts.
## Impact
New minting mechanisms are subject to governance approval (i.e. can be veto’ed).
These, however, will be arbitrary contracts with absolute control over the token. Any vulnerability in these contracts puts the whole system at risk, furthermore any “position” registered by them can be an arbitrary contract as well multiplying the attack surface. 
Also EOA can still technically be registered as minters.

## Proof of Concept
Consider a “position” registered by a new minter allowing for arbitrary external calls.
```
   function burn(address _owner, uint256 _amount) override external minterOnly {
      _burn(_owner, _amount);
   }


   function mint(address _target, uint256 _amount) override external minterOnly {
      _mint(_target, _amount);
   }


   modifier minterOnly() {
      if (!isMinter(msg.sender) && !isMinter(positions[msg.sender])) revert NotMinter();
      _;
   }

```
It can then mint arbitrary amount or burn from any account.

## Tools Used
VS code, pen and paper
## Recommended Mitigation Steps
Limit positions minting capabilities by:
a) keep account of positions minting limits in `frankencoin.sol` contract by adding `limits` `address=>uint` map keeping track of remaining limit
b) add minting function only specifically for positions which checks and updates available limit

Differentiate minters and positions access:
a) `onlyMinter` should only allow registered minter contracts not positions
b) `onlyPosition`  should be created

Add minter minting limits and perhaps restrict burning capabilities.

Restrict minters to contract accounts