xanoutas/solidity-security-findings huggingface dataset:
A host can abuse "abdicateHost" to inflate arbitrarily the number of hosts that voted a proposal
Audit: 2023-10-party-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/party/PartyGovernance.sol#L457
https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/party/PartyGovernance.sol#L636
https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/party/PartyGovernance.sol#L1098


# Vulnerability details

PartyGovernance has an expedite lane for proposals that have been approved by all hosts, because once all hosts have approved a proposal, there's nobody left to oppose a veto, and the proposal can be executed without waiting for the `executionDelay` time reserved for hosts to express a veto.

The way this works is as follows: whenever a proposal is accepted by a member, if this member is also a host, a counter of the hosts that approved this proposal is increased:

```Solidity
        // PartyGovernance.sol:630

        // Mark the caller as having voted.
        info.hasVoted[msg.sender] = true;

        // Increase the total votes that have been cast on this proposal.
        uint96 votingPower = getVotingPowerAt(msg.sender, values.proposedTime - 1, snapIndex);
        values.votes += votingPower;
        if (isHost[msg.sender]) {
            ++values.numHostsAccepted;
        }
        info.values = values;
```

this counter is later used to check if all hosts voted and in case expedite the `Ready` status for the proposal:

```Solidity
            // PartyGovernance.sol:1097

            // If all hosts voted, skip execution delay
            if (_hostsAccepted(pv.numHosts, pv.numHostsAccepted)) {
                return ProposalStatus.Ready;
            }
            // Passed.
            return ProposalStatus.Passed;
```

This logic can be easily manipulated, because unlike voting power, the "host status" of a voter is evaluated when the vote is cast and not at a fixed point in time, so different voters acting together can share the "host status" just for the time of voting, as passing over the "host status" is quick and easy through the `abdicateHost` function:

```Solidity
    // PartyGovernance.sol:455
    
    /// @notice Transfer party host status to another.
    /// @param newPartyHost The address of the new host.
    function abdicateHost(address newPartyHost) external {
        _assertHost();
        // 0 is a special case burn address.
        if (newPartyHost != address(0)) {
            // Cannot transfer host status to an existing host.
            if (isHost[newPartyHost]) {
                revert InvalidNewHostError();
            }
            isHost[newPartyHost] = true;
        } else {
            // Burned the host status
            --numHosts;
        }
        isHost[msg.sender] = false;
        emit HostStatusTransferred(msg.sender, newPartyHost);
    }
```

Abusing this function to repeatedly `accept` a proposal with an exchanged host status effectively games the system to prevent other l
Implementation child contracts can be destructed though a direct initialization call
Audit: 2023-10-party-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/utils/Implementation.sol#L29


# Vulnerability details

The `Implementation` contract is designed to be a base class for contracts that are deployed for later use behind a proxy. They provide an `onlyDelegateCall` and an `onlyInitialize` modifier to decorate the implementation functions to make sure they are called properly, that is behind a proxy for the former, and only once for the latter.

The `onlyInitialize` modifier used by the contracts in scope to decorate the initializer functions is however insufficient to secure them, because the implementation contracts can be initialized by anyone without passing by a proxy. While this often does not pose a threat, it sometimes does, especially for contracts whose initializers contain a `delegatecall`. This is the case for `PartyGovernanceNFT` and its children, where risk is however mitigated by the fact for this attack to work, the caller needs to have authority on the `Globals` contract.

# Impact
- Contracts inheriting from `Implementation` that include a `delegatecall` in their initialization, like `PartyGovernanceNFT`, or that can be initialized to a state that allows `delegatecalls` to arbitrary addresses, are at risk of being deleted; all assets managed or held by the affected contracts are then at risk of being lost. 
- If these contracts were created with a `CREATE2` opcode, they can subsequently be re-deployed in a [metamorphic setting](https://medium.com/coinmonks/metamorphic-contracts-5e6a022914e5) with arbitrary code, in the worst case exposing held assets to rug-pull theft.

# Proof of Concept
The following PoC shows how a `Party` (`PartyGovernanceNFT`, and `Implementation` child) implementation can be destroyed through a direct initializer call.
```Solidity
pragma solidity >=0.8.20;

import "forge-std/Test.sol";
import {Party} from "contracts/party/Party.sol";
import {Globals} from "contracts/globals/Globals.sol";
import {LibGlobals} from "contracts/globals/LibGlobals.sol";

contract H2 is Test {
    Globals globals;
    Party party;

    function setUp() public {
        globals = new Globals(address(this));
        party = new Party(globals);

        globals.setAddress(LibGlobals.GLOBAL_PROPOSAL_ENGINE_IMPL, address(new Attack()));

        Party.PartyInitData memory pid;

        party.initialize(
            pid
        );
    }

    function testContractWasDeleted() public {
        require(address(party).code.length == 0);
    }
}

contract Attack {
    function initialize(address, bytes memory) external {
        selfdestruct(payable(address(0)));
    }
}
```

# Tools Used
Code review, Foundry

# Recommended Mitigation Steps
Consider disabling initializers for implementation contracts, which is the [safety practice recommended by the OpenZeppelin team](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/94697be8a3f0dfcd95dfb13ffbd39b5973f5c65d/contracts/
accept - By only 1 Host, it can make the Host unanimity and run the proposal immediately
Audit: 2023-10-party-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/party/PartyGovernance.sol#L636-L638
https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/party/PartyGovernance.sol#L457-L472


# Vulnerability details

## Impact

A single Host can make a Host unanimity situation and execute the proposal immediately.

## Proof of Concept 

The `abdicateHost` function allows you to delegate Host to another account.

```solidity
function abdicateHost(address newPartyHost) external {
    _assertHost();
    // 0 is a special case burn address.
    if (newPartyHost != address(0)) {
        // Cannot transfer host status to an existing host.
        if (isHost[newPartyHost]) {
            revert InvalidNewHostError();
        }
@>      isHost[newPartyHost] = true;
    } else {
        // Burned the host status
        --numHosts;
    }
@>  isHost[msg.sender] = false;
    emit HostStatusTransferred(msg.sender, newPartyHost);
}
```

[https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/party/PartyGovernance.sol#L457-L472](https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/party/PartyGovernance.sol#L457-L472)

User1 is Host. If User1 votes and then moves Host to User2, it can be manipulated to appear that multiple hosts voted. In other words, a single host can make a host unanimity situation.

```solidity
function accept(uint256 proposalId, uint256 snapIndex) public returns (uint256 totalVotes) {
    ...

@>  if (isHost[msg.sender]) {
@>      ++values.numHostsAccepted;
    }
    info.values = values;
```

[https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/party/PartyGovernance.sol#L636-L638](https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/party/PartyGovernance.sol#L636-L638)

This means that the proposal can be executed immediately, even if not all hosts have agreed to it.

This is PoC testcode. You can put this at PartyGovernance.t.sol file and run it. 

```solidity
function testGovernance_allHostsAccept_PoC() public {
  (
      Party party,
      IERC721[] memory preciousTokens,
      uint256[] memory preciousTokenIds
  ) = partyAdmin.createParty(
          partyImpl,
          PartyAdmin.PartyCreationMinimalOptions({
              host1: address(danny),
              host2: address(this),
              passThresholdBps: 5000,
              totalVotingPower: 60,
              preciousTokenAddress: address(toadz),
              preciousTokenId: 1,
              rageQuitTimestamp: 0,
              feeBps: 0,
              feeRecipient: payable(0)
          })
      );
  DummySimpleProposalEngineImpl engInstance = DummySimpleProposalEngineImpl(address(party));

  // Mint first governance NFT
  partyAdmin.mintGovNft(party, address(john), 20, address(john));
  assertEq(
_hostsAccepted - If more than numHosts of Hosts vote, it will not be executed immediately
Audit: 2023-10-party-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/party/PartyGovernance.sol#L1126
https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/party/PartyGovernance.sol#L457-L472
https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/party/PartyGovernance.sol#L636-L638


# Vulnerability details

## Impact

The proposal is not executed immediately, even though the hosts have unanimously agreed to it.

## Proof of Concept

The `abdicateHost` function allows you to move Host to another account. This means that the number of Hosts that can exist at the same time is up to `numHosts`, but the number of accounts that have actually been Hosts can be higher.

```solidity
function abdicateHost(address newPartyHost) external {
    _assertHost();
    // 0 is a special case burn address.
    if (newPartyHost != address(0)) {
        // Cannot transfer host status to an existing host.
        if (isHost[newPartyHost]) {
            revert InvalidNewHostError();
        }
@>      isHost[newPartyHost] = true;
    } else {
        // Burned the host status
        --numHosts;
    }
@>  isHost[msg.sender] = false;
    emit HostStatusTransferred(msg.sender, newPartyHost);
}
```

[https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/party/PartyGovernance.sol#L457](https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/party/PartyGovernance.sol#L457)

User1 is the Host. User1 votes and then moves Host to User2. After pass Host role to User2 and User2 votes as Host, the number of `pv.numHostsAccepted` can be greater than `pv.numHosts`.

```solidity
function accept(uint256 proposalId, uint256 snapIndex) public returns (uint256 totalVotes) {
    ...

@>  if (isHost[msg.sender]) {
@>      ++values.numHostsAccepted;
    }
    info.values = values;
```

[https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/party/PartyGovernance.sol#L636-L637](https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/party/PartyGovernance.sol#L636-L637)

If the same number of Hosts voted as `numHosts` at the time the proposal was created (unanimous), the proposal is executed immediately without waiting for a delay. However, if `pv.numHostsAccepted > pv.numHosts` as shown above, the proposal cannot be executed immediately even though a sufficient number of Hosts have agreed.

```solidity
function _getProposalStatus(
    ProposalStateValues memory pv
) private view returns (ProposalStatus status) {
    ...
    if (pv.passedTime != 0) {
        ...
        // If all hosts voted, skip execution delay
@>      if (_hostsAccepted(pv.numHosts, pv.numHostsAccepted)) {
            return ProposalStatus.Ready;
        }
        // Passed.
        return ProposalStatus.Passed;
One host can skip execution delay instead of all hosts
Audit: 2023-10-party-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/06e6271b75a30f00ae2674e8c26f3eb9e89e75e5/contracts/party/PartyGovernance.sol#L1097-L1100


# Vulnerability details

## Impact

One host can allow a proposal to skip execution delay without all host accepted. The host role responsibility is defined below, it should now allow to bypass without full host consensus.

        Hosts: A role in the Party for trusted addresses that grants the ability to unilaterally veto proposals in the Party and configure Rage Quit. Each Host may or may not be a member (i.e. have non-zero voting power in the Party).

## Proof of Concept

When getting the status of a proposal, there is a case when if all hosts vote for a proposal, the propsal can skip the execution delay and can be called right away.

        // If all hosts voted, skip execution delay
        if (_hostsAccepted(pv.numHosts, pv.numHostsAccepted)) {
            return ProposalStatus.Ready;
        }

https://github.com/code-423n4/2023-10-party/blob/06e6271b75a30f00ae2674e8c26f3eb9e89e75e5/contracts/party/PartyGovernance.sol#L1097-L1100

However, the check can be bypass and one host can allow the skip of execution instead of all hosts.
In `accept` function, when an account vote for a proposal, the msg.sender is checked if it is a host, then it increases the number of host accepted by one.

        if (isHost[msg.sender]) {
            ++values.numHostsAccepted;
        }

There is function `abdicateHost` that allow transfering host status to another account. A host can just transfer to another if its address and call `accept` again, artificially inflating the count of accepting hosts, and bypass the check.

        /// @notice Transfer party host status to another.
        /// @param newPartyHost The address of the new host.
        function abdicateHost(address newPartyHost) external {
            _assertHost();
            // 0 is a special case burn address.
            if (newPartyHost != address(0)) {
                // Cannot transfer host status to an existing host.
                if (isHost[newPartyHost]) {
                    revert InvalidNewHostError();
                }
                isHost[newPartyHost] = true;
            } else {
                // Burned the host status
                --numHosts;
            }
            isHost[msg.sender] = false;
            emit HostStatusTransferred(msg.sender, newPartyHost);
        }

## Tools Used

Manual

## Recommended Mitigation Steps

Implement a snapshot mechanism of host status at the time of proposal creation to ensure that only the hosts at the time can contribute to the acceptance count, preventing manipulation by host role transfer.


## Assessed type

Other
A token contract has a PartyGovernance.fallback() which might be dangerous to implement
Audit: 2023-10-party-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/party/PartyGovernance.sol#L312-L327


# Vulnerability details

## Impact
It may lead to unintended behavior, potentially allowing an attacker to manipulate the contract's state or functionality.

It also could be susceptible to reentrancy attacks, where an attacker repeatedly calls back into the contract during the token transfer process.

If the token fallback function does not handle the received tokens appropriately, there's a risk that the tokens sent to the contract might be stuck, becoming inaccessible or unmanageable.

## Proof of Concept
A token contract has a PartyGovernance.fallback() (contracts/party/PartyGovernance.sol#312-327) which might be dangerous to implement

## Tools Used
Slither, Manual Review

## Recommended Mitigation Steps
Use reentrancy guards, ensure that external calls are handled safely, and implement proper validation and processing of incoming tokens.


## Assessed type

Token-Transfer
Potential DDOS
Audit: 2023-10-party-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernanceNFT.sol#L344


# Vulnerability details

## Impact
With three loops, and an O(n)+ 0(n2) complexity this could cause DOS with users not being able to call this functions

## Proof of Concept
https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernanceNFT.sol#L344

## Tools Used

## Recommended Mitigation Steps


## Assessed type

DoS
In `_executeSetSignatureValidator`, validate `data.signatureHash` and `data.signatureValidator` for non-zero values.
Audit: 2023-10-party-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/main/contracts/signature-validators/OffChainSignatureValidator.sol#L84


# Vulnerability details

M5 - In `_executeSetSignatureValidator`, validate `data.signatureHash` and `data.signatureValidator` for non-zero values.


    **poc**:
    ```solidity
    function _executeSetSignatureValidator() internal returns (bytes memory nextProgressData) {
        require(data.signatureHash != bytes32(0), "Signature hash is zero");
        require(address(data.signatureValidator) != address(0), "Validator address is zero");
        
    }
    ```


## Assessed type

Other
Hosts should agree before performing operations like setting ragequit, vetoing
Audit: 2023-10-party-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernance.sol#L665
https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernanceNFT.sol#L318


# Vulnerability details

## Impact
Since a Party can have multiple hosts, they should agree before vetoing or setting rageQuit, to prevent a single compromised host from having absolute decision, just as is done when hosts accept a proposal
## Proof of Concept
In PartyGovernance#veto and PartyGovernanceNFT#setRageQuit, a single host is allowed to call the function, and directly modify state, even without an agreement from other hosts.

Since accepting a proposal to push it to "ready" state requires agreement from all the hosts, these functions should as well.

This will make Parties more scalable and governance will be made easier, eliminating most need for trusts

## Tools Used
manual
## Recommended Mitigation Steps
There should be a voting mechanism among hosts when setting ragequit or vetoing


## Assessed type

Other
PartyGovernance contract cannot accept Eth
Audit: 2023-10-party-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernance.sol#L312


# Vulnerability details



## Impact
The ```PartyGovernance``` contract does not have payable in it fallback back function which is the main context of allowing a contract to accept Ether or else it will revert or lost forever if receive.


## Proof of Concept
Lack of payable fallback function is useless making contract unable to accept ether.
```
        fallback() external {
        bytes4 functionSelector = bytes4(msg.data[0:4]);
        if (
            functionSelector == ERC1155TokenReceiverBase.onERC1155BatchReceived.selector ||
            functionSelector == ERC1155TokenReceiverBase.onERC1155Received.selector ||
            functionSelector == IERC721Receiver.onERC721Received.selector
        ) {
            assembly {
                let freeMem := mload(0x40)
                mstore(freeMem, functionSelector)
                mstore(0x40, add(freeMem, 0x20))
                return(freeMem, 0x20)
            }
        }
        _readOnlyDelegateCall(address(_getSharedProposalStorage().engineImpl), msg.data);
    }
```

## Tools Used
Manual Review

## Recommended Mitigation Steps
The recommendation is made to either make ```fallback``` function payable or add payable ```receive``` function in contract to enable it for accepting eth.



## Assessed type

ETH-Transfer
Signature malleability can occur by using EVM's `ecrecover`
Audit: 2023-10-party-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/main/contracts/signature-validators/OffChainSignatureValidator.sol#L58


# Vulnerability details


## Impact
EVM's ecrecover is susceptible to signature malleability which allows replay attacks, check [this]( https://swcregistry.io/docs/SWC-117/).


## Proof of Concept
```OffChainSignatureValidator``` in its function ```isValidSignature``` checks for signature validity by using vulnerable method of ```ecrecover```.

```
        address signer = ecrecover(hash, v, r, s);
```


## Tools Used
Manual Review

## Recommended Mitigation Steps
The recommendation is made to utilise the [ECDSCA](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol) library of OpenZeppelin.



## Assessed type

Invalid Validation
The function `_initProposalImpl` should have access control checks to restrict who can initialize or change the implementation.
Audit: 2023-10-party-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/main/contracts/crowdfund/InitialETHCrowdfund.sol#L350


# Vulnerability details

M4 - The function `_initProposalImpl` should have access control checks to restrict who can initialize or change the implementation.

    **poc**:
    ```solidity
    function _initProposalImpl(IProposalExecutionEngine impl, bytes memory initData) internal onlyAuthorized {
        
    }
    ```


## Assessed type

Other
Potential drain of EthCrowdFund contract
Audit: 2023-10-party-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-party/blob/main/contracts/crowdfund/ETHCrowdfundBase.sol#L366


# Vulnerability details

## Impact
In the `emergencyExecute` function in the EthCrowdFundBase contract, the external call msg.value is decided by the user input which could lead to draining of funds by compromised wallet or human error.
Also there is no check if the value of amoutnEth is at least the value of `address(this).balance`

## Proof of Concept
Allowing the msg.value to be user inputted poses some risk, which are potential draining of funds.

If a DAO wallet calls a function and inputs a wrong amount of eth to be used, this could lead to 
- loss of funds 
- rug pull vulnerability
- loss of integrity
This same goes for a compromised wallet. Having an input value for msg.value poses a great risk for the crowdfund contract

## Tools Used
Manual Review
## Recommended Mitigation Steps
- Use msg.value instead
- Update the CrowdfundLifecycle if a certain amount of value is used
- Check the value of `amoutnEth` vs `address(this).balance


## Assessed type

Rug-Pull