xanoutas/solidity-security-findings huggingface dataset:
Upgraded Q -> 2 from #540 [1718031699595]
Audit: 2024-05-munchables-findings
Severity: MEDIUM

Judge has assessed an item in Issue #540 as 2 risk. The relevant finding follows:

 ## 5. Pricefeeds that had disapproved a price proposal can also approve it

Links to affected code *

https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L192-L197

https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L224-L230

### Impact

The `approveUSDPrice` function doesn't check that the caller had not previously disapproved the price. This can lead to prices being updated without the actual needed consensus as it seems like the same price feed is double voting. If a contentious price is set and two price feed disapprove it, the same two can later on approve it, causing the price to reach the desired threshold, and for the update to be executed. Important to note that this also goes against the protcol's intended design as if a pricefeed had previously approved a price, they're [unable](https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L225) to disapprove it.
```solidity
    function approveUSDPrice(
        uint256 _price
    )
        external
        onlyOneOfRoles(
            [
                Role.PriceFeed_1,
                Role.PriceFeed_2,
                Role.PriceFeed_3,
                Role.PriceFeed_4,
                Role.PriceFeed_5
            ]
        )
    {
        if (usdUpdateProposal.proposer == address(0)) revert NoProposalError();
        if (usdUpdateProposal.proposer == msg.sender)
            revert ProposerCannotApproveError();
        if (usdUpdateProposal.approvals[msg.sender] == _usdProposalId)
            revert ProposalAlreadyApprovedError();
        if (usdUpdateProposal.proposedPrice != _price)
            revert ProposalPriceNotMatchedError();

        usdUpdateProposal.approvals[msg.sender] = _usdProposalId;
        usdUpdateProposal.approvalsCount++;
      ...
    }
```
Upgraded Q -> 2 from #552 [1717590895402]
Audit: 2024-05-munchables-findings
Severity: MEDIUM

Judge has assessed an item in Issue #552 as 2 risk. The relevant finding follows:

 ## QA-03 Current voting logic is flawed as it allows for double votes for the same proposal

### Proof of Concept

Take a look at [LockManager.\_execUSDPriceUpdate()](https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L506-L527)

```solidity
    function _execUSDPriceUpdate() internal {
        if (
            usdUpdateProposal.approvalsCount >= APPROVE_THRESHOLD &&
            usdUpdateProposal.disapprovalsCount < DISAPPROVE_THRESHOLD
        ) {
            uint256 updateTokensLength = usdUpdateProposal.contracts.length;
            for (uint256 i; i < updateTokensLength; i++) {
                address tokenContract = usdUpdateProposal.contracts[i];
                if (configuredTokens[tokenContract].nftCost != 0) {
                    configuredTokens[tokenContract].usdPrice = usdUpdateProposal
                        .proposedPrice;

                    emit USDPriceUpdated(
                        tokenContract,
                        usdUpdateProposal.proposedPrice
                    );
                }
            }

            delete usdUpdateProposal;
        }
    }
```

This function is called when updating the price of a token if the proposal meets the `APPROVE_THRESHOLD` and has fewer than `DISAPPROVE_THRESHOLD` disapprovals. However, the logic for counting votes has a flaw that allows a voter to vote both for and against the same proposal. This occurs because while there is a check to prevent disapproving a proposal that has already been approved, there is no similar check to prevent approving a proposal that has already been disapproved.

### Approve Function

In `approveUSDPrice()`, the checks are as follows:
[approveUSDPrice()](https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L191-L198)

```solidity
        if (usdUpdateProposal.proposer == address(0)) revert NoProposalError();
        if (usdUpdateProposal.proposer == msg.sender)
            revert ProposerCannotApproveError();
        if (usdUpdateProposal.approvals[msg.sender] == _usdProposalId)
            revert ProposalAlreadyApprovedError();
        if (usdUpdateProposal.proposedPrice != _price)
            revert ProposalPriceNotMatchedError();
```

### Disapprove Function

In `disapproveUSDPrice()`, the checks are as follows:
[disapproveUSDPrice()](https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L224-L231)

```solidity
        if (usdUpdateProposal.proposer == address(0)) revert NoProposalError();
        if (usdUpdateProposal.approvals[msg.sender] == _usdProposalId)
            revert ProposalAlreadyApprovedError();
        if (usdUpdateProposal.disapprovals[msg.sender] == _usdProposalId)
            rever
Upgraded Q -> 2 from #550 [1717590861116]
Audit: 2024-05-munchables-findings
Severity: MEDIUM

Judge has assessed an item in Issue #550 as 2 risk. The relevant finding follows:

 # L4. A feeder who has already disapproved the USD price can approve it in the same proposal.

## Impact
There are no checks to ensure that msg.sender has not already disapproved the price in the approveUSDPrice function. This means a feeder who has previously disapproved the same price can also approve it.

## Proof of Concept

https://github.com/code-423n4/2024-05-munchables/blob/main/src/managers/LockManager.sol#L177
```solidity
File: src/managers/LockManager.sol#L177
    function approveUSDPrice(
        uint256 _price
    )
        external
        onlyOneOfRoles(
            [
                Role.PriceFeed_1,
                Role.PriceFeed_2,
                Role.PriceFeed_3,
                Role.PriceFeed_4,
                Role.PriceFeed_5
            ]
        )
    {
        if (usdUpdateProposal.proposer == address(0)) revert NoProposalError();
        if (usdUpdateProposal.proposer == msg.sender) // @audit missed disapprovals checks
            revert ProposerCannotApproveError();
        if (usdUpdateProposal.approvals[msg.sender] == _usdProposalId)
            revert ProposalAlreadyApprovedError();
        if (usdUpdateProposal.proposedPrice != _price)
            revert ProposalPriceNotMatchedError();
        ...
    }
```

## Tools Used
Manual review

## Recommended Mitigation Steps
It is advised to include validation to check if it has already been disapproved.

```diff
File: src/managers/LockManager.sol#L177
    function approveUSDPrice(
        uint256 _price
    )
        external
        onlyOneOfRoles(
            [
                Role.PriceFeed_1,
                Role.PriceFeed_2,
                Role.PriceFeed_3,
                Role.PriceFeed_4,
                Role.PriceFeed_5
            ]
        )
    {
        if (usdUpdateProposal.proposer == address(0)) revert NoProposalError();
        if (usdUpdateProposal.proposer == msg.sender)
            revert ProposerCannotApproveError();
        if (usdUpdateProposal.approvals[msg.sender] == _usdProposalId)
            revert ProposalAlreadyApprovedError();
+        if (usdUpdateProposal.disapprovals[msg.sender] == _usdProposalId)
+            revert ProposalAlreadyDisapprovedError();
        if (usdUpdateProposal.proposedPrice != _price)
            revert ProposalPriceNotMatchedError();
        ...
    }
```
Upgraded Q -> 2 from #549 [1717590836296]
Audit: 2024-05-munchables-findings
Severity: MEDIUM

Judge has assessed an item in Issue #549 as 2 risk. The relevant finding follows:

 ## 4
## Missing Check for Prior Disapproval in approveUSDPrice Function
The ``approveUSDPrice`` function allows price feed contracts to vote in favor of a proposed price. However, unlike the ``disapproveUSDPrice`` function, ``approveUSDPrice`` does not verify whether the voter has already voted against the current proposal. A price feed contract should only be allowed to vote on one side (approve or disapprove) of the proposal. This missing check can lead to inconsistent voting behavior and potential conflicts in the proposal process.

### Affected code
```
function approveUSDPrice(
    uint256 _price
)
    external
    onlyOneOfRoles(
        [
            Role.PriceFeed_1,
            Role.PriceFeed_2,
            Role.PriceFeed_3,
            Role.PriceFeed_4,
            Role.PriceFeed_5
        ]
    )
{
    if (usdUpdateProposal.proposer == address(0)) revert NoProposalError();
    if (usdUpdateProposal.proposer == msg.sender)
        revert ProposerCannotApproveError();
    if (usdUpdateProposal.approvals[msg.sender] == _usdProposalId)
        revert ProposalAlreadyApprovedError();
    // @audit - LOW should check even disapprovals
    if (usdUpdateProposal.proposedPrice != _price)
        revert ProposalPriceNotMatchedError();

    usdUpdateProposal.approvals[msg.sender] = _usdProposalId;
    usdUpdateProposal.approvalsCount++;

    if (usdUpdateProposal.approvalsCount >= APPROVE_THRESHOLD) {
        _execUSDPriceUpdate();
    }

    emit ApprovedUSDPrice(msg.sender);
}
```
[LockManager#177-207]("https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L177-L207")

### Potential Impact
Without a check to prevent a voter who has disapproved the proposal from approving it, the voting process can become inconsistent, leading to potential manipulation or invalid results. This could undermine the integrity of the price update mechanism.

### Recommended Fix
Add a check in the ``approveUSDPrice`` function to ensure that a voter who has already disapproved the proposal cannot approve it.
Upgraded Q -> 2 from #548 [1717590798564]
Audit: 2024-05-munchables-findings
Severity: MEDIUM

Judge has assessed an item in Issue #548 as 2 risk. The relevant finding follows:

 # Low 02 User Can Approve And Dissaprove

## Summary
There are two functions responsible for approving and disapproving proposals. Users are expected to either approve or disapprove a proposal, but not both. Currently approve function doesn't check if the user was already disapproved.

## Vulnerability Detail
In the `LockManager` contract, there are two functions for managing proposals: `approveUSDPrice` and `disapproveUSDPrice`. Users are expected to either approve or disapprove a proposal, but not both.

**Disapprove Function:**

The `disapproveUSDPrice` function correctly checks if the user has already either approved or disapproved the proposal. If either condition is true, the function reverts to prevent the user from performing conflicting actions.

**Approve Function:**

The current implementation of the `approveUSDPrice` function only checks if the user has already approved the proposal. It does not verify if the user has previously disapproved the proposal, creating an inconsistency.

This omission allows a user who has disapproved a proposal to later approve it, which contradicts the intended logic and can lead to inconsistent proposal states.

https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L177-L207

https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L210-L242

## Impact
The lack of a disapproval check in the `approveUSDPrice` function can result in a proposal being both approved and disapproved by the same user, leading to inconsistencies.

## Code Snippet
None

## Tool used
Manual Review

## Recommendation
Add this line of code tot `LockManager:approveUSDPrice` function:
```solidity
if (usdUpdateProposal.disapprovals[msg.sender] == _usdProposalId) 
    revert ProposalAlreadyDisapprovedError();
```
Upgraded Q -> 2 from #546 [1717590748797]
Audit: 2024-05-munchables-findings
Severity: MEDIUM

Judge has assessed an item in Issue #546 as 2 risk. The relevant finding follows:

 ### [Low-03] Role.PriceFeed can `approve` & `disapprove` same time as there is no check for `disapproval` in `approveUSDPrice()`

A `PriceFeeder` at same time can `disapprove` and `approve` same purposed price cause `absence` of `disapproval` check in `approveUSDPrice()`

`approveUSDPrice()` has following checks
```solidity
    function approveUSDPrice(
        uint256 _price
    )
        external
        onlyOneOfRoles(
            [
                Role.PriceFeed_1,
                Role.PriceFeed_2,
                Role.PriceFeed_3,
                Role.PriceFeed_4,
                Role.PriceFeed_5
            ]
        )
    {
        if (usdUpdateProposal.proposer == address(0)) revert NoProposalError();
        if (usdUpdateProposal.proposer == msg.sender)
            revert ProposerCannotApproveError();
        if (usdUpdateProposal.approvals[msg.sender] == _usdProposalId) 
            revert ProposalAlreadyApprovedError();
        if (usdUpdateProposal.proposedPrice != _price)
            revert ProposalPriceNotMatchedError();
```
https://github.com/code-423n4/2024-05-munchables/blob/main/src/managers/LockManager.sol#L191-L197
It never checks msg.sender already disapproved current purposal or not,
So as a result `priceFeeder` can first call `disapproveUSDPrice()` and then call `approveUSDPrice()` which is not a intended behaviour.

### Mitigation
Implement similar check present in `disapproveUSDPrice()`
```diff
    function approveUSDPrice(
        uint256 _price
    )
        external
        onlyOneOfRoles(
            [
                Role.PriceFeed_1,
                Role.PriceFeed_2,
                Role.PriceFeed_3,
                Role.PriceFeed_4,
                Role.PriceFeed_5
            ]
        )
    {
        if (usdUpdateProposal.proposer == address(0)) revert NoProposalError();
        if (usdUpdateProposal.proposer == msg.sender)
            revert ProposerCannotApproveError();
        if (usdUpdateProposal.approvals[msg.sender] == _usdProposalId) 
            revert ProposalAlreadyApprovedError();
        if (usdUpdateProposal.proposedPrice != _price)
            revert ProposalPriceNotMatchedError();
+       if (usdUpdateProposal.disapprovals[msg.sender] == _usdProposalId)
+           revert ProposalAlreadyDisapprovedError();
```
Upgraded Q -> 2 from #545 [1717590716526]
Audit: 2024-05-munchables-findings
Severity: MEDIUM

Judge has assessed an item in Issue #545 as 2 risk. The relevant finding follows:

 ### [L-08] Incorrect validation in function approveUSDPrice() allows price feed roles to both approve and disapprove for a proposal

[Link to instance](https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L225C1-L228C54)

Unlike the [disapproveUSDPrice()](https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L210) function which validates both the disapproval and approval mapping with [these checks](https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L225C1-L228C54), the [approveUSDPrice()](https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L177) function does not check the disapproval mapping. 

This allows price feed roles to first call [disapproveUSDPrice()](https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L210), which will pass since there has neither been an approval or disapproval from their side on the proposal yet. The function would set the disapproval mapping from price feed to the current `_usdProposalId`. The price feed role can then call [approveUSDPrice()](https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L177) and pass [this check](https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L194) since it only checks for approval mapping and not the disapproval mapping as well.

Due to this, price feed roles can both approve and disapprove for a proposal, thereby double counting their votes.

Solution: Add the below check to the approveUSDPrice() function.

```solidity
File: LockManager.sol
233:         if (usdUpdateProposal.disapprovals[msg.sender] == _usdProposalId)
234:             revert ProposalAlreadyDisapprovedError();
```
Upgraded Q -> 2 from #543 [1717590668705]
Audit: 2024-05-munchables-findings
Severity: MEDIUM

Judge has assessed an item in Issue #543 as 2 risk. The relevant finding follows:

 # [04] Function `approveUSDPrice` does not check whether voter (a PrideFeed) already diapproved the proposal or not

Function `approveUSDPrice` performs the following checks to decide whether an approval is valid/acceptable:

```javascript
        if (usdUpdateProposal.proposer == address(0)) revert NoProposalError();
        if (usdUpdateProposal.proposer == msg.sender)
            revert ProposerCannotApproveError();
        if (usdUpdateProposal.approvals[msg.sender] == _usdProposalId)
            revert ProposalAlreadyApprovedError();
        if (usdUpdateProposal.proposedPrice != _price)
            revert ProposalPriceNotMatchedError();
```

It checks whether the the voter already approved the proposal, but it does not check whether it previously disapproved the same proposal. Hence, it is possible for a PriceFeed to disapprove, and then approve the same proposal.

(In contract, `disapproveUSDPrice` does check whether the PriceFeed that is voting has previously approved and also whether it previously disapproved the proposal.)

To mitigate the issue, add an additional check to function `approveUSDPrice`:

```diff
        if (usdUpdateProposal.proposer == address(0)) revert NoProposalError();
        if (usdUpdateProposal.proposer == msg.sender)
            revert ProposerCannotApproveError();
        if (usdUpdateProposal.approvals[msg.sender] == _usdProposalId)
            revert ProposalAlreadyApprovedError();
+       if (usdUpdateProposal.disapprovals[msg.sender] == _usdProposalId)
+           revert ProposalAlreadyDisapprovedError();
        if (usdUpdateProposal.proposedPrice != _price)
            revert ProposalPriceNotMatchedError();
```
Upgraded Q -> 2 from #536 [1717590635475]
Audit: 2024-05-munchables-findings
Severity: MEDIUM

Judge has assessed an item in Issue #536 as 2 risk. The relevant finding follows:

 ## [L-01] For usd price proposals, the same account can't approve then disapprove, but it's possible to disapprove then approve

Consider disallowing approvals once an account has already disapproved.

https://github.com/code-423n4/2024-05-munchables/blob/main/src/managers/LockManager.sol#L225-L226

https://github.com/code-423n4/2024-05-munchables/blob/main/src/managers/LockManager.sol#L177-L207
Upgraded Q -> 2 from #535 [1717590581109]
Audit: 2024-05-munchables-findings
Severity: MEDIUM

Judge has assessed an item in Issue #535 as 2 risk. The relevant finding follows:

 ### Title
PriceFeed can vote both against and in favor of confirming the new price at the same time 

### Severity
Low

### Target
src/managers/LockManager.sol

## Description
One of the roles can first call `disapproveUSDPrice()` and then call `approveUSDPrice()`, thus increasing the count of those who agree and disagree for the new token price, since the approveUSDPrice function does not check if one of the roles has voted against it. 

```solidity 
function approveUSDPrice(
        uint256 _price
    )
        external
        onlyOneOfRoles(
            [
                Role.PriceFeed_1,
                Role.PriceFeed_2,
                Role.PriceFeed_3,
                Role.PriceFeed_4,
                Role.PriceFeed_5
            ]
        )
    {
        if (usdUpdateProposal.proposer == address(0)) revert NoProposalError(); 
        if (usdUpdateProposal.proposer == msg.sender) 
            revert ProposerCannotApproveError();
        if (usdUpdateProposal.approvals[msg.sender] == _usdProposalId) 
            revert ProposalAlreadyApprovedError();
        if (usdUpdateProposal.proposedPrice != _price)  
            revert ProposalPriceNotMatchedError();

        usdUpdateProposal.approvals[msg.sender] = _usdProposalId;  
        usdUpdateProposal.approvalsCount++; 

        if (usdUpdateProposal.approvalsCount >= APPROVE_THRESHOLD) { 
            _execUSDPriceUpdate();
        }
        emit ApprovedUSDPrice(msg.sender);
    }
```

```solidity 
function disapproveUSDPrice(
        uint256 _price
    )
        external
        onlyOneOfRoles(
            [
                Role.PriceFeed_1,
                Role.PriceFeed_2,
                Role.PriceFeed_3,
                Role.PriceFeed_4,
                Role.PriceFeed_5
            ]
        )
    {
        if (usdUpdateProposal.proposer == address(0)) revert NoProposalError(); 
        if (usdUpdateProposal.approvals[msg.sender] == _usdProposalId) 
            revert ProposalAlreadyApprovedError();
        if (usdUpdateProposal.disapprovals[msg.sender] == _usdProposalId) 
            revert ProposalAlreadyDisapprovedError();
        if (usdUpdateProposal.proposedPrice != _price)
            revert ProposalPriceNotMatchedError();

        usdUpdateProposal.disapprovalsCount++; 
        usdUpdateProposal.disapprovals[msg.sender] = _usdProposalId; 

        emit DisapprovedUSDPrice(msg.sender);

        if (usdUpdateProposal.disapprovalsCount >= DISAPPROVE_THRESHOLD) {  
            delete usdUpdateProposal; 
            emit RemovedUSDProposal();
        }
    }
```


## Recommendations
it is worth adding to the `approveUSDPrice()` function to check `usdUpdateProposal.disapprovals[msg.sender] == _usdProposalId`
```solidity 

function approveUSDPrice(
        uint256 _price
    )
        external
        onlyOneOfRoles