xanoutas/solidity-security-findings huggingface dataset:
Governor proposal creation can be blocked by frontrunning
Audit: 2023-12-autonolas-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/governance/contracts/GovernorOLAS.sol#L14


# Vulnerability details

## Impact

Due to using a vulnerable version in which **ANYONE** can submit **ANY** proposal, it's possible for an attacker to start front-running important proposals and canceling them afterwards, effectively invalidating the governance mechanism.

## Proof of Concept

**NOTE** $\Rightarrow$ As the `GovernorOLAS` is just a vanilla OZ `Governor*` fork and is considered as in-scope for the contest, I believe that although this is a bug that arises from using a flawed dependency, it should be reported and fixed.

The used OZ version for the `GovernorOLAS` contract is flawed as **ANYONE** can front-run **ANY** proposal being made, become the proposer and close it afterwards, efectively preventing the governance mechanism from working. For reference, see [the original report](https://github.com/OpenZeppelin/openzeppelin-contracts/security/advisories/GHSA-5h3x-9wvq-w4m2) .

The `GovernorOLAS` contract inherits from `Governor` and `GovernorCompatibilityBravo`, among others. As Solidity handles inheritance linearly from right to left, the common workflow for `propose` and `cancel` will be as follows:

- `GovernorOLAS::propose` $\Rightarrow$ `GovernorCompatibilityBravo::propose` $\Rightarrow$ `Governor::propose`
- `GovernorCompatibilityBravo::cancel` $\Rightarrow$ `GovernorOLAS::_cancel` $\Rightarrow$ `GovernorTimelockControl::_cancel` $\Rightarrow$ `Governor::_cancel`

The issue arises in

[**GovernorCompatibilityBravo, function propose**](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/0a25c1940ca220686588c4af3ec526f725fe2582/contracts/governance/compatibility/GovernorCompatibilityBravo.sol#L58)

```solidity
    function propose(
        address[] memory targets,
        uint256[] memory values,
        bytes[] memory calldatas,
        string memory description
    ) public virtual override(IGovernor, Governor) returns (uint256) {
        _storeProposal(_msgSender(), targets, values, new string[](calldatas.length), calldatas, description);
        return super.propose(targets, values, calldatas, description);
    }
```

[**GovernorCompatibilityBravo, function _storeProposal**](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/0a25c1940ca220686588c4af3ec526f725fe2582/contracts/governance/compatibility/GovernorCompatibilityBravo.sol#L144C1-L164C6)

```solidity
    function _storeProposal(
        address proposer,
        address[] memory targets,
        uint256[] memory values,
        string[] memory signatures,
        bytes[] memory calldatas,
        string memory description
    ) private {
        bytes32 descriptionHash = keccak256(bytes(description));
        uint256 proposalId = hashProposal(targets, values, _encodeCalldata(signatures, calldatas), descriptionHash);

        ProposalDetails storage details = _proposalDetails[proposalId];
Unrestricted create function in GnosisSafeMultisig
Audit: 2023-12-autonolas-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-12-autonolas/blob/97088cd67930e2d198793f2138f11dee73a68887/registries/audits/internal/analysis/reentrancyPoC/ServiceRegistry.sol#L540
https://github.com/code-423n4/2023-12-autonolas/blob/97088cd67930e2d198793f2138f11dee73a68887/registries/contracts/multisigs/GnosisSafeMultisig.sol#L91-L107


# Vulnerability details

## Impact

This vulnerability could potentially lead to a Denial of Service (DoS) attack, preventing service owners from deploying their services on the platform.

## Proof of Concept

The `ServiceRegistry` contract utilizes the `create` function to set up a Gnosis Safe multisig wallet when a new service is deployed.

        multisig = IMultisig(multisigImplementation).create(agentInstances, service.threshold, data);

https://github.com/code-423n4/2023-12-autonolas/blob/97088cd67930e2d198793f2138f11dee73a68887/registries/audits/internal/analysis/reentrancyPoC/ServiceRegistry.sol#L540

However, the `create` function in the `GnosisSafeMultisig` contract is publicly accessible, allowing any user to call it.

        function create(
            address[] memory owners,
            uint256 threshold,
            bytes memory data
        ) external returns (address multisig)
        {
            // Parse the data into gnosis-specific set of variables
            (address to, address fallbackHandler, address paymentToken, address payable paymentReceiver, uint256 payment,
                uint256 nonce, bytes memory payload) = _parseData(data);

            // Encode the gnosis setup function parameters
            bytes memory safeParams = abi.encodeWithSelector(GNOSIS_SAFE_SETUP_SELECTOR, owners, threshold,
                to, payload, fallbackHandler, paymentToken, payment, paymentReceiver);

            // Create a gnosis safe multisig via the proxy factory
            multisig = IGnosisSafeProxyFactory(gnosisSafeProxyFactory).createProxyWithNonce(gnosisSafe, safeParams, nonce);
        }

https://github.com/code-423n4/2023-12-autonolas/blob/97088cd67930e2d198793f2138f11dee73a68887/registries/contracts/multisigs/GnosisSafeMultisig.sol#L91-L107


An attacker can exploit this by front-running legitimate service deployment transactions with the same parameters. Since the Gnosis Safe Proxy Factory uses the `create2` opcode, which depends on the supplied salt and deployment data, it would fail to create a new multisig wallet if the parameters are reused, resulting in a failed service deployment.

https://etherscan.io/address/0xa6b71e26c5e0845f74c812102ca7114b6a896ab2#code%23F2%23L61

It will stop the service owners from deploying their services.

## Tools Used

Manual

## Recommended Mitigation Steps

Restrict Function Access: Modify the `create` function so that only the `ServiceRegistry` contract (or other authorized entities) can call it. 


## Assessed type

Access Control
`Depository.deposit` can be manipulated
Audit: 2023-12-autonolas-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/tokenomics/contracts/Depository.sol#L291-L346


# Vulnerability details

## Impact
`Depository.deposit` is used as a use can deposit one LP token, and after matured, OLAS token can be withdrawed by calling `Depository.redeem`. The issue is that during calculating the amount of OLAS can be redeemed, there are some factors can be manipulated which will impact the amount of OLAS.

## Proof of Concept
In [Depository.deposit](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/tokenomics/contracts/Depository.sol#L291-L346), the `payout` of OLAS is calculated by `GenericBondCalculator(bondCalculator).calculatePayoutOLAS` at [Depository.sol#L320](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/tokenomics/contracts/Depository.sol#L320)

In [GenericBondCalculator.calculatePayoutOLAS](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/tokenomics/contracts/GenericBondCalculator.sol#L46-L65), the amount of OLAS is calculated as `ITokenomics(tokenomics).getLastIDF() * priceLP * tokenAmount  / 1e36;`
Next, `Tokenomics.getLastIDF()` is [defined as](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/tokenomics/contracts/Tokenomics.sol#L1262-L1268)
```solidity
    function getLastIDF() external view returns (uint256 idf)
    {
        idf = mapEpochTokenomics[epochCounter - 1].epochPoint.idf;
        if (idf == 0) {
            idf = 1e18;
        }
    }
```

Next, lets check how [idf](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/tokenomics/contracts/Tokenomics.sol#L1264) is calculated, as show in [Tokenomics.sol#L1043](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/tokenomics/contracts/Tokenomics.sol#L1043), idf is calculated by [Tokenomics._calculateIDF](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/tokenomics/contracts/Tokenomics.sol#L831-L862).
```solidity
 831     function _calculateIDF(
 832         uint256 treasuryRewards,
 833         uint256 numNewOwners
 834     ) internal view returns (uint256 idf) {
 835         // Calculate the inverse discount factor based on the tokenomics parameters and values of units per epoch
 836         // df = 1 / (1 + iterest_rate), idf = (1 + iterest_rate) >= 1.0
 837         // Calculate IDF from epsilon rate and f(K,D)
 838         // f(K(e), D(e)) = d * k * K(e) + d * D(e),
 839         // where d corresponds to codePerDev and k corresponds to devPerCapital
 840         // codeUnits (codePerDev) is the estimated value of the code produced by a single developer for epoch
 841         UD60x18 codeUnits = UD60x18.wrap(codePerDev);
 842         // fKD = codeUnits * devsPerCapital * treasuryRewards + co
Absence of chain ID verification in HomeMediator contract
Audit: 2023-12-autonolas-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-12-autonolas/blob/667052268700dffe5f813ad65a974dc366dc6beb/governance/contracts/bridges/HomeMediator.sol#L105-L122


# Vulnerability details

## Impact

The lack of chain ID verification in the HomeMediator contract increase a risk of governor message spoofing, potentially allowing unauthorized actions in cross-chain communication.

## Proof of Concept

The HomeMediator contract is designed to process messages from the Ethereum mainnet to the Gnosis chain using the Arbitrary Message Bridge (AMB).

From the AMB bridge documents, in addition to checking `msg.sender` and `messageSender()`, it is crucial to verify the `source chainId`

        Since in the future the same arbitrary message bridge contract could handle requests from different chains, it makes sense to check also the source chain id by the method:
            function messageSourceChainId() external view returns (bytes32);

https://docs.tokenbridge.net/amb-bridge/development-of-a-cross-chain-application/how-to-develop-xchain-apps-by-amb#receive-a-method-call-from-the-amb-bridge

The existing `processMessageFromForeign` function in `HomeMediator` lacks this crucial check:

        function processMessageFromForeign(bytes memory data) external {
            // Check for the AMB Contract Proxy (Home) address
            if (msg.sender != AMBContractProxyHome) {
                revert AMBContractProxyHomeOnly(msg.sender, AMBContractProxyHome);
            }

            // Check for the Foreign Governor address
            address governor = foreignGovernor;
            address bridgeGovernor = IAMB(AMBContractProxyHome).messageSender();
            if (bridgeGovernor != governor) {
                revert ForeignGovernorOnly(bridgeGovernor, governor);
            }

            // Check for the correct data length
            uint256 dataLength = data.length;
            if (dataLength < DEFAULT_DATA_LENGTH) {
                revert IncorrectDataLength(DEFAULT_DATA_LENGTH, data.length);
            }

https://github.com/code-423n4/2023-12-autonolas/blob/667052268700dffe5f813ad65a974dc366dc6beb/governance/contracts/bridges/HomeMediator.sol#L105-L122

If a governor contract is deployed with the same address as the governor in mainnet. It can spoof a call from the governor contract in mainnet, since there is no check where the chainId the message comes from.

## Tools Used

Manual

## Recommended Mitigation Steps

It is recommended to add a check for the source chain ID in the `processMessageFromForeign` function.

```diff
function processMessageFromForeign(bytes memory data) external {
    ...
+   uint256 sourceChainId = IAMB(AMBContractProxyHome).messageSourceChainId();
+   if (sourceChainId != 1) {
+       revert ForeignGovernorOnly(bridgeGovernor, governor);
+   }
    ...
```


## Assessed type

Context
Donator blacklist can be circumvent
Audit: 2023-12-autonolas-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-12-autonolas/blob/a7d2d6c575307356a478b7afc3778a00ef10f1db/tokenomics/contracts/Tokenomics.sol#L788-L803
https://github.com/code-423n4/2023-12-autonolas/blob/a7d2d6c575307356a478b7afc3778a00ef10f1db/tokenomics/contracts/Tokenomics.sol#L706-L711


# Vulnerability details

## Impact

The OLAS tokenomics system includes a blacklist function to prevent malicious actors from donating to their services and earning OLAS tokens through inflation. However, this mechanism can be circumvented, allowing blacklisted donators to still benefit from OLAS inflation rewards.

## Proof of Concept

The existing code checks if a donator is blacklisted when they attempt to donate ETH to services. If the donator is blacklisted, the donation will revert. This function is designed to block the malicious donators. Because their veOLAS is locked and cannot be delegate or transfer to another account, blocking malicious donators mean preventing them from further participation and earn the OLAS tokens araising from inflation.

        function trackServiceDonations(
            address donator,
            uint256[] memory serviceIds,
            uint256[] memory amounts,
            uint256 donationETH
        ) external {
            // Check for the treasury access
            if (treasury != msg.sender) {
                revert ManagerOnly(msg.sender, treasury);
            }

            // Check if the donator blacklist is enabled, and the status of the donator address
            address bList = donatorBlacklist;
            if (bList != address(0) && IDonatorBlacklist(bList).isDonatorBlacklisted(donator)) {
                revert DonatorBlacklisted(donator);
            ...
        }

https://github.com/code-423n4/2023-12-autonolas/blob/a7d2d6c575307356a478b7afc3778a00ef10f1db/tokenomics/contracts/Tokenomics.sol#L788-L803

However, the `topUpEligible` mechanism in the same contract allows a loophole. As it currently stands, a service owner with sufficient votes to meet the `veOLASThreshold` can be eligible for inflation rewards. This eligibility does not depend on the voting power of the donator.

        bool topUpEligible;
        if (incentiveFlags[2] || incentiveFlags[3]) {
            address serviceOwner = IToken(serviceRegistry).ownerOf(serviceIds[i]);
            topUpEligible = (IVotingEscrow(ve).getVotes(serviceOwner) >= veOLASThreshold  ||
                IVotingEscrow(ve).getVotes(donator) >= veOLASThreshold) ? true : false;
        }

https://github.com/code-423n4/2023-12-autonolas/blob/a7d2d6c575307356a478b7afc3778a00ef10f1db/tokenomics/contracts/Tokenomics.sol#L706-L711

So a malicious service owner could accumulate enough votes to meet the threshold, and then use different accounts to donate, it ensure receiving OLAS tokens, and the blacklist does not work since the malicious actors can changes account every time it donates.

## Tools Used

Manual

## Recommended Mitigation Steps

There are some optio
Missing checks for failed calls to the token program will corrupt user's positions
Audit: 2023-12-autonolas-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/lockbox-solana/solidity/library/spl_token.sol#L63
https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/lockbox-solana/solidity/library/spl_token.sol#L83
https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/lockbox-solana/solidity/library/spl_token.sol#L104


# Vulnerability details

## Impact

If a call to the token program does revert, user's positions will be updated even though the changes at the environment level were null. For example, when doing a `deposit`, the contract will go on and mint the bridged tokens to the user regardless of whether the NFT transfer did succeed or not.

## Proof of Concept

Direct calls to the token program can revert as seen [here](https://solscan.io/account/TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA). Because of that, like in Ethereum, it must be checked whether the call did succeed or not as seen in the Solang docs [here](https://solang.readthedocs.io/en/v0.3.3/language/functions.html#calling-an-external-function-using-call). If it is not checked, then, user's positions will be updated regardless of whether the call did succeed or not. To show an example, imagine the next situation:

1. A deposit call is done with a valid NFT.
2. The NFT transfer in [line 164](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/lockbox-solana/solidity/liquidity_lockbox.sol#L164) reverts and the NFT remains under the caller control.
3. The code goes on and mints the bridged token, as well as record a new position, even though the NFT was not transferred at all.

```mermaid
flowchart TB
	USER(((USER)))--1. Call-->deposit
	subgraph liquidity_lockbox
	deposit
	end

	subgraph SplToken
	transfer
	mint
	end

	deposit--2. Pull NFT from USER--xtransfer
	deposit--3. Mint bridged tokens-->mint
	mint--4. Send minted tokens to USER-->USER
	deposit--5. Record position-->deposit
```

The flawed code is in

[**SplToken library**](https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/lockbox-solana/solidity/library/spl_token.sol#L52C1-L105C3)

```solidity
    ...

	function pda_mint_to(address mint, address account, address authority, uint64 amount, bytes seed, bytes bump) internal {
		bytes instr = new bytes(9);
		instr[0] = uint8(TokenInstruction.MintTo);
		instr.writeUint64LE(amount, 1);

		AccountMeta[3] metas = [
			AccountMeta({pubkey: mint, is_writable: true, is_signer: false}),
			AccountMeta({pubkey: account, is_writable: true, is_signer: false}),
			AccountMeta({pubkey: authority, is_writable: false, is_signer: true})
		];

		tokenProgramId.call{accounts: metas, seeds: [[seed, bump]]}(instr); // @audit what if the call reverts??
	}

	...

	function transfer(address from, address to, address owner, uint64 amount) internal {
		bytes instr = new bytes(9);
		instr[0]
privileged address can be transferred without recipient’s confirmation
Audit: 2023-12-autonolas-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/governance/contracts/OLAS.sol#L43


# Vulnerability details

The owner of many important protocol contracts (`OLAS`, `GenericManager`, `GenericRegistry`, `Depository`, `Dispenser`, `DonatorBlacklist`, `Tokenomics` and `Treasury`) can transfer ownership of the contract to a different address. However, such an operation does not require a confirmation from the new owner’s address. Incorrect use of such functionality can lead to losing control over contracts, which could not be undone.

## Impact

Should the ownership be transfered to the wrong address this can lead to lose important control over the protocol.

## Proof of Concept

Over the entire protocol is used the same pattern of ownership transfer using the `changeOver` function:

https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/governance/contracts/OLAS.sol#L43
https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/registries/contracts/GenericManager.sol#L20
https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/registries/contracts/GenericRegistry.sol#L37
https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/tokenomics/contracts/Depository.sol#L123
https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/tokenomics/contracts/Dispenser.sol#L46
https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/tokenomics/contracts/DonatorBlacklist.sol#L36
https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/tokenomics/contracts/Tokenomics.sol#L404
https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/tokenomics/contracts/Treasury.sol#L137

## Tools Used
Manual review

## Recommended Mitigation Steps

Implement two-step ownership transfer.


## Assessed type

Access Control
getUnit and tokenURI can return stale value
Audit: 2023-12-autonolas-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/registries/contracts/UnitRegistry.sol#L121


# Vulnerability details

When a service or component `unitHash` is updated its value is not reflected in the `getUnit` and `tokenURI` method. So when called a stale value is returned.

## Impact

When a hash is updated trough `UnitRegistry#updateHash` function then function like `getUnit` and `tokenURI` return a stale value.

## Proof of Concept

Inside `UnitRegistry#updateHash` the hash is only appended to an array representing the history of hash changes but the main value is left intact/original.

https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/registries/contracts/UnitRegistry.sol#L121

In contrast when a service updates its `configHash` trough `ServiceRegistry#update` the method stores the new hash and the history of the updated hash. Then a function like `getServiceInfo` returns the correct hash value. 

https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/registries/audits/internal/analysis/reentrancyPoC/ServiceRegistry.sol#L345-L348

`tokeURI` is using the stale `unitHash` becouse the call to `_getUnitHash` function :

https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/registries/contracts/GenericRegistry.sol#L135-L142

also `getUnit` just return the wrong value associated with the unit in case of a hash update:

https://github.com/code-423n4/2023-12-autonolas/blob/2a095eb1f8359be349d23af67089795fb0be4ed1/registries/contracts/UnitRegistry.sol#L152-L154

## Tools Used
Manual review

## Recommended Mitigation Steps

One possible solution :

```diff
-   function getUnit(uint256 unitId) external view virtual returns (Unit memory unit) {
+   function getUnit(uint256 unitId) public view virtual returns (Unit memory unit) {
        unit = mapUnits[unitId];
+       if (mapUnitIdHashes[unitId].length() > 0) {    
+           unit.unitHash = mapUnitIdHashes[unitId][mapUnitIdHashes[unitId].length()-1];
+       }
    }
```

and

```diff
    function _getUnitHash(uint256 unitId) internal view override returns (bytes32) {
+       return getUnit(unitId).unitHash;
-       return mapUnits[unitId].unitHash;
    }
```


## Assessed type

Other