xanoutas/solidity-security-findings huggingface dataset:
In `DelayedAdmin.sol` , `auth` access functions will always revert
Audit: 2023-09-centrifuge-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/admins/DelayedAdmin.sol#L26-L38


# Vulnerability details

## Impact
In `DelayedAdmin.sol`, Any ward can trigger instantaneous pausing and unpausing on the Root, as well as schedule and cancel new relys through the timelock. `pause()` function is used to pause the root. Similarly unpause(), scheduleRely() and cancelRely() functions have their use in contract.

```Solidity
File: src/admins/DelayedAdmin.sol

    function pause() public auth {
        root.pause();
    }

    function unpause() public auth {
        root.unpause();
    }

    function scheduleRely(address target) public auth {
        root.scheduleRely(target);
    }

    function cancelRely(address target) public auth {
        root.cancelRely(target);
    }
```

Let's take `pause()` for issue explanation,

The issue here is in `pause()` function which will always revert and therefore the root wont be paused instantaneouly.

I have tried this issue in RemixIDE and found to be true and the issue exist and throws an error 

> transact to DelayedAdmin.pause errored: VM error: revert.

> revert
> 	The transaction has been reverted to the initial state.
Reason provided by the contract: "Auth/not-authorized"

This is due to wrong access control which is expected by Root.pause().

The flow of calling pause() function as follows,

1) The DelayedAdmin.pause() is called by EOA wallet.......This is msg.sender as EOA wallet address
2) Now, root.pause() will be called by `DelayedAdmin.sol` contract address as msg.sender
3) root.pause() expects the `auth` which is nothing but root core contract deployer which can be seen as below,

```Soldity
File: src/Root.sol

    function pause() external auth {
        paused = true;
        emit Pause();
    }
```
[Reference link](https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/Root.sol#L54)

Here pause() has `auth` modifier which is nothing but contract deployer, which can be seen below,

```Solidity
File: src/Root.sol

        wards[msg.sender] = 1;
```

4) Therefore, the `DelayedAdmin.pause()` will always revert due to different addresses as msg.sender is accessing 
the `pause()` function.

## Proof of Concept
https://github.com/code-423n4/2023-09-centrifuge/blob/512e7a71ebd9ae76384f837204216f26380c9f91/src/admins/DelayedAdmin.sol#L26-L38

## Tools Used
Manaul review

## Recommended Mitigation Steps
Rework on `DelayedAdmin.sol` contract functions using `auth` access modifier so that functions should not revert.
pause(), unpause(), scheduleRely() and cancelRely() functionality should work as intended.


## Assessed type

Access Control
Signature Verification for _verifyVoteSignature Function
Audit: 2023-12-revolutionprotocol-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-revolutionprotocol/blob/d42cc62b873a1b2b44f57310f9d4bbfdd875e8d6/packages/revolution/src/CultureIndex.sol#L419


# Vulnerability details

Potential Risk:
The `_verifyVoteSignature` function in the `CultureIndex` contract is responsible for verifying signatures for specific votes. While it attempts to verify signatures, there are potential risks associated with signature verification.

Proof of Concept (PoC):
Signature verification can be challenging and may lead to vulnerabilities if not implemented correctly. Attackers could potentially exploit weaknesses in the verification process. For example, an attacker might craft signatures that appear valid but contain malicious data or provide invalid parameters to bypass the verification.

Recommended Mitigation Steps:
To mitigate the risk associated with signature verification, ensure that the `_verifyVoteSignature` function correctly and securely verifies the provided signatures. Verify the following:

1. Ensure that the `deadline`, `v`, `r`, and `s` parameters are validated to prevent the use of expired signatures or invalid components.
2. Verify that the recovered address from the signature (`recoveredAddress`) matches the `from` address.
3. Check if the `from` address is not the zero address (`address(0)`) to prevent potential issues related to invalid addresses.

It's essential to use a well-established and secure signature verification library or function to perform these checks. Additionally, consider consulting with security experts to review and test the signature verification implementation thoroughly.

Here's a general outline of how you might implement the signature verification in the `_verifyVoteSignature` function, and you should adapt it to your specific requirements and security considerations:

function _verifyVoteSignature(
    address from,
    uint256[] calldata pieceIds,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
) internal view returns (bool) {
    // Ensure the deadline is not expired
    require(block.timestamp <= deadline, "Signature has expired");

    // Construct the message hash to be signed
    bytes32 voteHash = keccak256(abi.encode(VOTE_TYPEHASH, from, pieceIds, nonces[from], deadline));

    // Recover the signer's address from the signature
    address recoveredAddress = ecrecover(voteHash, v, r, s);

    // Ensure to address is not 0
    require(from != address(0), "Invalid 'from' address");

    // Ensure signature is valid
    require(recoveredAddress != address(0) && recoveredAddress == from, "Invalid signature");

    return true;
}

This code demonstrates a simplified signature verification process for a single signature, and you should adapt it to your specific requirements.

By following best practices for signature verification and thoroughly testing the functionality, you can reduce the risk associated with the `_verifyVoteSignature` function and enhance the security of your contrac
Signature Verification for batchVoteForManyWithSig Function
Audit: 2023-12-revolutionprotocol-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-revolutionprotocol/blob/d42cc62b873a1b2b44f57310f9d4bbfdd875e8d6/packages/revolution/src/CultureIndex.sol#L389


# Vulnerability details

Potential Risk:
The `batchVoteForManyWithSig` function in the `CultureIndex` contract allows multiple users to execute a batch of votes using provided signatures. While it attempts to verify the signatures, there are potential risks associated with signature verification.

Proof of Concept (PoC):
Signature verification can be challenging and may lead to vulnerabilities if not implemented correctly. Attackers could potentially exploit weaknesses in the verification process. For example, an attacker might craft signatures that appear valid but contain malicious data or provide invalid parameters to bypass the verification.

Recommended Mitigation Steps:
To mitigate the risk associated with signature verification, ensure that the `_verifyVoteSignature` function correctly and securely verifies the provided signatures. Verify the following:

1. Ensure that the `_verifyVoteSignature` function checks that the provided `from` address has the authority to vote on the specified `pieceIds`.
2. Confirm that the `deadline`, `v`, `r`, and `s` parameters are validated to prevent the use of expired signatures or invalid components.
3. Verify that the provided arrays (`from`, `pieceIds`, `deadline`, `v`, `r`, and `s`) have matching lengths and that each signature is correctly associated with its respective address and pieceIds.

It's essential to use a well-established and secure signature verification library or function to perform these checks. Additionally, consider consulting with security experts to review and test the signature verification implementation thoroughly.

Here's a general outline of how you might implement the signature verification for a single signature, and you should adapt it to your specific requirements and security considerations:

function _verifyVoteSignature(
    address from,
    uint256[] memory pieceIds,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
) internal view returns (bool) {
    // Ensure the deadline is not expired
    require(block.timestamp <= deadline, "Signature has expired");

    // Construct the message hash to be signed
    bytes32 messageHash = keccak256(abi.encodePacked(from, pieceIds, deadline));

    // Recover the signer's address from the signature
    address signer = ecrecover(messageHash, v, r, s);

    // Ensure the recovered signer matches the 'from' address
    return signer == from;
}

This code demonstrates a simplified signature verification process for a single signature, and you should adapt it to your specific requirements.

By following best practices for signature verification and thoroughly testing the functionality, you can reduce the risk associated with the `batchVoteForManyWithSig` function and enhance the security of your contract.




## Assessed type

Invalid Validation
Signature Verification for voteForManyWithSig Function
Audit: 2023-12-revolutionprotocol-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-revolutionprotocol/blob/d42cc62b873a1b2b44f57310f9d4bbfdd875e8d6/packages/revolution/src/CultureIndex.sol#L419


# Vulnerability details

Potential Risk:
The `voteForManyWithSig` function in the `CultureIndex` contract allows users to vote on multiple `pieceIds` using a provided signature. While it attempts to verify the signature, there are some potential risks associated with signature verification.

Proof of Concept (PoC):
In some cases, signature verification can be tricky and may lead to vulnerabilities if not implemented correctly. Attackers could potentially exploit weaknesses in the verification process. For example, an attacker might craft a signature that appears valid but contains malicious data, or they could provide invalid parameters to bypass the verification.

Recommended Mitigation Steps:
To mitigate the risk associated with signature verification, ensure that the `_verifyVoteSignature` function correctly and securely verifies the provided signature. Verify the following:

1. Ensure that the `_verifyVoteSignature` function checks that the provided `from` address has the authority to vote on the specified `pieceIds`.
2. Confirm that the `deadline` parameter is validated to prevent the use of expired signatures.
3. Validate that the `v`, `r`, and `s` components are correctly formatted and used in the signature verification process.

It's important to use a well-established and secure signature verification library or function to perform these checks. Additionally, consider consulting with security experts to review and test the signature verification implementation thoroughly.

Here's a general outline of how you might implement the signature verification:

function _verifyVoteSignature(
    address from,
    uint256[] memory pieceIds,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
) internal view returns (bool) {
    // Ensure the deadline is not expired
    require(block.timestamp <= deadline, "Signature has expired");

    // Construct the message hash to be signed
    bytes32 messageHash = keccak256(abi.encodePacked(from, pieceIds, deadline));

    // Recover the signer's address from the signature
    address signer = ecrecover(messageHash, v, r, s);

    // Ensure the recovered signer matches the 'from' address
    return signer == from;
}

This code demonstrates a simplified signature verification process, and you should adapt it to your specific requirements and security considerations.

By following best practices for signature verification and thoroughly testing the functionality, you can reduce the risk associated with the `voteForManyWithSig` function and enhance the security of your contract.



## Assessed type

Invalid Validation
No access control on critical functions
Audit: 2023-12-revolutionprotocol-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-revolutionprotocol/blob/main/packages/revolution/src/NontransferableERC20Votes.sol#L127


# Vulnerability details

The contract lacks proper access control on critical functions, allowing unauthorized parties to execute them.

Recommendation: Use OpenZeppelin Ownable control for minting/burning, etc.


**Before:**
```solidity
// No access control checks in critical functions
function _mint(address account, uint256 value) internal override {
    if (account == address(0)) {
        revert ERC20InvalidReceiver(address(0));
    }
    _update(address(0), account, value);
}
```

**After:**
```solidity
// Add access control to critical functions
function _mint(address account, uint256 value) internal onlyOwner override {
    require(account != address(0), "ERC20: mint to the zero address");
    _update(address(0), account, value);
}
```



## Assessed type

Access Control
Overflow potential
Audit: 2023-12-revolutionprotocol-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-revolutionprotocol/blob/d42cc62b873a1b2b44f57310f9d4bbfdd875e8d6/packages/revolution/src/MaxHeap.sol#L120


# Vulnerability details

Overflow:
Be cautious about integer overflow when incrementing size. 
Depending on how the size variable is used in your contract, it might be beneficial to check for potential overflow conditions.

require(size < type(uint256).max, "Heap size exceeds maximum");



## Assessed type

Invalid Validation
Error prone and complex code 
Audit: 2023-12-revolutionprotocol-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-revolutionprotocol/blob/d42cc62b873a1b2b44f57310f9d4bbfdd875e8d6/packages/revolution/src/MaxHeap.sol#L94


# Vulnerability details

Used function is error prone and complex that leads to incorrect asset handling so have suggested below maxHeapify implementation.


function maxHeapify(uint256 pos) internal {
    uint256 left = 2 * pos + 1;
    uint256 right = 2 * pos + 2;
    uint256 largest = pos;

    if (left < heap.length && valueMapping[heap[left]] > valueMapping[heap[largest]]) {
        largest = left;
    }

    if (right < heap.length && valueMapping[heap[right]] > valueMapping[heap[largest]]) {
        largest = right;
    }

    if (largest != pos) {
        swap(pos, largest);
        maxHeapify(largest);
    }
}


## Assessed type

Other
Missing access control on critical functions
Audit: 2023-12-revolutionprotocol-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-revolutionprotocol/blob/main/packages/revolution/src/MaxHeap.sol#L136


# Vulnerability details

The broad admin role enables arbitrary manipulation of the heap without restrictions.

Recommendation: Implement granular access control and privilege separation. Implement an access control system such as OpenZeppelin AccessControl to restrict access to these functions. For example, create separate admin roles for each function and assign them granular privileges.

https://github.com/code-423n4/2023-12-revolutionprotocol/blob/main/packages/revolution/src/MaxHeap.sol#L136

The `insert`, `updateValue`, and `extractMax` functions in the MaxHeap contract allow the admin to insert, update, and extract values from the heap. However, there is no access control on these critical functions besides a basic `onlyAdmin` modifier. This allows the admin account to arbitrarily modify the heap without restrictions.

```diff
- modifier onlyAdmin() {
+ using AccessControl for AccessControl.AccessControl; 

+ bytes32 public constant INSERT_ROLE = keccak256("INSERT_ROLE");
+ bytes32 public constant UPDATE_ROLE = keccak256("UPDATE_ROLE");
+ bytes32 public constant EXTRACT_ROLE = keccak256("EXTRACT_ROLE");

+ modifier onlyInserter() {
+   require(hasRole(INSERT_ROLE, msg.sender), "Sender missing INSERT_ROLE");
   _;  
 }

+ function insert(uint256 itemId, uint256 value) public onlyInserter {
+   // ...
+ }

+ function updateValue(uint256 itemId, uint256 newValue) public onlyHolder(UPDATE_ROLE) {
+   // ...  
+ }

+ function extractMax() public onlyHolder(EXTRACT_ROLE) returns (uint256, uint256) {
+   // ...
+ }
```



## Assessed type

Access Control
Incorrect Termination Condition
Audit: 2023-12-revolutionprotocol-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-revolutionprotocol/blob/d42cc62b873a1b2b44f57310f9d4bbfdd875e8d6/packages/revolution/src/MaxHeap.sol#L102


# Vulnerability details

The provided termination condition if (pos >= (size / 2) && pos <= size) is incorrect. 
This condition is not suitable for terminating the maxHeapify function. 
It should instead be based on comparing values in the heap to ensure the max heap property.

The condition if (pos >= (size / 2) && pos <= size) return; in the code  is meant to optimize the maxHeapify function. However, it's not the correct condition for controlling the termination of the function. This condition is attempting to check if pos is a leaf node in the heap and, if so, return early because leaf nodes in a heap are already considered to be max heaps by definition.

I am assuming that the idea behind this optimization is that in a binary heap, elements starting from size / 2 (where size is the number of elements in the heap) are leaf nodes, and leaf nodes can be considered as already satisfying the max heap property because they have no children.

However, the condition itself is not entirely correct. The termination condition for maxHeapify in a correct max-heapify algorithm should be based on the comparison of the values of the current node with its children. It should continue recursively calling maxHeapify as long as there are children that violate the max heap property (i.e., if the current node is smaller than either of its children).

So, in summary, you should replace the condition if (pos >= (size / 2) && pos <= size) with the standard termination condition that checks the values of the current node and its children to maintain the max heap property.


## Assessed type

Error
MaxHeapify: find children with large value and swap
Audit: 2023-12-revolutionprotocol-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-revolutionprotocol/blob/d42cc62b873a1b2b44f57310f9d4bbfdd875e8d6/packages/revolution/src/MaxHeap.sol#L94


# Vulnerability details

Input Validation: 
    - The maxHeapify function assumes that pos is a valid position within the heap. 
    - Similar to the swap function, you should consider adding a check to verify that pos is within the bounds of your heap.

require(pos < size, "Invalid position provided");
Ensuring that pos is within bounds can help prevent potential errors or unintended behavior.



## Assessed type

Invalid Validation
Votes delegated to the zero address will be stuck forever and user can no longer delegate votes
Audit: 2023-12-revolutionprotocol-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-12-revolutionprotocol/blob/main/packages/revolution/src/base/VotesUpgradeable.sol#L164-L167


# Vulnerability details

## Bug Description

VotesUpgradeable automatically delegates a user's votes to themselves if a delegatee is not set.

[VotesUpgradeable.sol#L165-168](https://github.com/code-423n4/2023-12-revolutionprotocol/blob/main/packages/revolution/src/base/VotesUpgradeable.sol#L164-L167)
```solidity
function delegates(address account) public view virtual returns (address) {
    VotesStorage storage $ = _getVotesStorage();
    return $._delegatee[account] == address(0) ? account : $._delegatee[account];
}
```

If a user delegates their votes to the zero address, the above logic will still return that a user has delegated their votes to themselves.

This will mess up the logic in `_moveDelegateVotes()` [VotesUpgradeable.sol#L233-L253](https://github.com/code-423n4/2023-12-revolutionprotocol/blob/main/packages/revolution/src/base/VotesUpgradeable.sol#L233-L253)
```solidity
function _moveDelegateVotes(address from, address to, uint256 amount) private {
    VotesStorage storage $ = _getVotesStorage();
    if (from != to && amount > 0) {
        if (from != address(0)) {
            (uint256 oldValue, uint256 newValue) = _push(
                $._delegateCheckpoints[from],
                _subtract,
                SafeCast.toUint208(amount)
            );
            emit DelegateVotesChanged(from, oldValue, newValue);
        }
        if (to != address(0)) {
            (uint256 oldValue, uint256 newValue) = _push(
                $._delegateCheckpoints[to],
                _add,
                SafeCast.toUint208(amount)
            );
            emit DelegateVotesChanged(to, oldValue, newValue);
        }
    }
}
```

If a user tries to redelegate to themselves, `_moveDelegateVotes()` will receive the same address for `from` and `to` and skip the delegation.

If a user tries to delegate to another address, it will view `from` as having zero delegates (or not enough delegates) and will revert due to underflow. 

## Impact

If a user delegates their votes to the zero address, those votes will be locked forever. Additionally, that user will no longer be able to delegate their votes to another address.

This also makes misuse and phishing attacks more feasible because it involves interacting directly with the trusted contract.

## Proof of Concept

```solidity
function testVotesDelegatedToAddressZeroLostForever() public {
    // Mint tokens to the user
    uint256 mintAmount = 1000 * 1e18;
    vm.prank(address(erc20TokenEmitter));
    erc20Token.mint(address(this), mintAmount);
    assertEq(erc20Token.getVotes(address(this)), mintAmount);

    // Delegate voting power to address 0
    erc20Token.delegate(address(0));
    assertEq(erc20Token.getVotes(address(this)), 0);

    // Try to delegate back to user and also other address
    erc20Token.delegate(address(this));
    assertEq(erc20Token.ge