xanoutas/solidity-security-findings huggingface dataset:
Policy attached to a wallet should apply to all subaccounts likewise. 
Audit: 2023-10-brahma-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-brahma/blob/dd0b41031b199a0aa214e50758943712f9f574a0/contracts/src/core/registries/PolicyRegistry.sol#L52-L54
https://github.com/code-423n4/2023-10-brahma/blob/dd0b41031b199a0aa214e50758943712f9f574a0/contracts/src/core/PolicyValidator.sol#L106-L111


# Vulnerability details

## Impact
The contract policy registry allows you to register policy to a wallet directly as opposed to a sub account. Like [demonstrated](https://github.com/code-423n4/2023-10-brahma/blob/dd0b41031b199a0aa214e50758943712f9f574a0/contracts/src/core/registries/PolicyRegistry.sol#L52-L54) in the line above, but when validating for a wallet sub account policy it only validates policies attached directly to the sub account and not those attached to its parent wallet.

## Proof of Concept
in the function #updatePolicy it allows a wallet to attach a policy to itself. 

     else if (msg.sender == account && walletRegistry.isWallet(account)) {
            // In case invoker is a registered wallet
        } else {
            revert UnauthorizedPolicyUpdate();
        }
 but when a policy is being validated against an account it only checks the sub account and not the wallet, it should rather check the wallet as last resort if the policy does not exist on a sub account.
 
     bytes32 policyHash =
          PolicyRegistry(AddressProviderService._getRegistry(_POLICY_REGISTRY_HASH)).commitments(account);
            // @audit-issue policy can be commited to a wallet directly, skipping the subaccount, 
            // this should check that a permission is either commited to wallet or subaccount.
        if (policyHash == bytes32(0)) {
            revert NoPolicyCommit();
        }

## Tools Used
Manual 

## Recommended Mitigation Steps
It should first check if a policy is attached to a sub account, and if not then check that the policy is attached to the parent wallet, a policy that is attached to a parent wallet should mean that all sub accounts in belonging to the wallet has the permission.


## Assessed type

Invalid Validation
Sub Accounts are never pulled
Audit: 2023-10-brahma-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-brahma/blob/main/contracts/src/core/registries/WalletRegistry.sol#L1-L76


# Vulnerability details


## Impact

The continuous growth of the [`walletToSubAccountList`](https://github.com/code-423n4/2023-10-brahma/blob/main/contracts/src/core/registries/WalletRegistry.sol#L25) mapping in `WalletRegistry` can have several impacts:

1. **Gas Costs:** As the mapping grows, the gas costs associated with iterating through or accessing the mapping's data structures may increase, potentially leading to higher transaction fees.

2. **Contract Efficiency:** Over time, the contract may become less efficient as the size of the mapping grows, potentially impacting contract performance.

3. **Challenges in Management:** Managing a continuously growing mapping may become challenging, especially if there is a need to clear or reduce its size when Sub Accounts are deactivated or unregistered.

## Proof of Concept

The purpose of the `WalletRegistry` contract is to serve as a registry for wallet and sub-account addresses within the Brahma DeFi Protocol.
The `WalletRegistry` contract uses a `subAccountToWallet` and `walletToSubAccountList` state variables to manage registered Wallets and their Sub Accounts. While this pattern enhances control and security, it introduces complexities in the user experience.

The issue arises from the fact that the `walletToSubAccountList` mapping is continually populated with sub-account addresses but lacks a mechanism to remove or reduce the mapping's size when Sub Accounts are no longer active or needed. This continuous growth can lead to unintended consequences over time.

```jsx
    /// @notice subAccount addresses mapped to owner wallet
    mapping(address subAccount => address wallet) public subAccountToWallet;
    /// @notice wallet addresses mapped to list of subAccounts
    mapping(address wallet => address[] subAccountList) public walletToSubAccountList;
```

## Recommended Mitigation Steps

To address the issue of continuous mapping growth in the `WalletRegistry` contract, the following mitigation steps are recommended:

**Implement Data Management:** Consider implementing a mechanism for `Console Accounts` to manage their `Sub Accounts` (Allow the Console Accounts to remove Sub Accounts). This will help maintain contract efficiency and mitigate gas cost increases.

By addressing the issue of continuous mapping growth and implementing proper data management strategies, the `WalletRegistry` contract can maintain efficiency, control gas costs, and ensure smooth contract management over time.



## Assessed type

Context
Anyone can call Register a wallet using the function RegisterWallet().
Audit: 2023-10-brahma-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-brahma/blob/dd0b41031b199a0aa214e50758943712f9f574a0/contracts/src/core/registries/WalletRegistry.sol#L35


# Vulnerability details

## Impact
Anyone(other smartcontracts) can directly call `RegisterWallet()` to register wallets.  

## Proof of Concept
In `WalletRegistry.sol` the function `RegisterWallet()`, is used to register wallets for `SubAccounts` and `consoleAccounts` but this function is supposed to be only callable by the `SafeDeployer.sol` and `wallets`. But this is not ensuerd in the code.
```solidity
* @notice Registers a wallet
     * @dev Can only be called by safe deployer or the wallet itself
     */
    function registerWallet() external {
        if (isWallet[msg.sender]) revert AlreadyRegistered();
        if (subAccountToWallet[msg.sender] != address(0)) revert IsSubAccount();
        isWallet[msg.sender] = true;
        emit RegisterWallet(msg.sender);
    }
```
## Tools Used
Manual review
## Recommended Mitigation Steps
Add function that checks if the caller is a wallet(EOA) if the `msg.sender` is not `SafeDeployer.sol`
```solidity
function isContract(address account) internal view returns (bool) {
    uint256 size;
    assembly {
        size := extcodesize(account)
    }
    return size > 0;
}
```
This function if added to the contract can be used to run an `if/else` statement that ensures that the caller is either a wallet or the SafeDeployer.sol.

















## Assessed type

Access Control
Non Payable function receiving ETH
Audit: 2023-10-brahma-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-brahma/blob/dd0b41031b199a0aa214e50758943712f9f574a0/contracts/src/core/ExecutorPlugin.sol#L68-L77


# Vulnerability details

## Impact
The function 'executeTransaction' expects to make call and delegate requests, and forward the value of the  receiving request to target like state [here #L92](https://github.com/code-423n4/2023-10-brahma/blob/dd0b41031b199a0aa214e50758943712f9f574a0/contracts/src/core/ExecutorPlugin.sol#L92) but the function is not payable. 
 
## Proof of Concept
Transactions which intend to have value would not be processed as intended because the function is not payable.  

## Tools Used
Manual 

## Recommended Mitigation Steps
Make `executeTransaction` function payable. 


## Assessed type

ETH-Transfer
Uncontrolled Registration of Wallet Addresses
Audit: 2023-10-brahma-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-brahma/blob/dd0b41031b199a0aa214e50758943712f9f574a0/contracts/src/core/registries/WalletRegistry.sol#L35-L41


# Vulnerability details

## Impact
The registerWallet function,  allows any Ethereum address to register as a wallet without any validation checks to ensure that the caller is the legitimate owner of the wallet, ans it  uses the msg.sender property to register a wallet. The msg.sender property represents the address of the caller of the function. In this case, it allows any address that calls the function to register itself as a wallet, provided it has not been registered before and it is not a sub-account. if an attacker calls the registerWallet function, their address is registered as a wallet. This can potentially give the attacker access to wallet-related functionalities that are intended only for legitimate wallets. If the contract provides any special permissions or functionalities to registered wallets, the attacker can exploit these, and an attacker can use this vulnerability to register their address as a wallet, and then interact with the contract as a legitimate wallet. This could lead to unauthorized actions being taken on behalf of the impersonated wallet, potentially leading to loss of funds.
## POC
```
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "......../WalletRegistry.sol";

contract TestWalletRegistry {
    WalletRegistry walletRegistry;

    constructor(WalletRegistry _walletRegistry) {
        walletRegistry = _walletRegistry;
    }

    function testRegisterWallet() public {
        walletRegistry.registerWallet();
    }
}

```
## Tools Used
manual review
## Recommended Mitigation Steps
here is an example of fixing :
the  check is to ensure that msg.sender is the same as the address being registered. 
```solidity
function registerWallet(address _wallet) external {
    require(msg.sender == _wallet, "Sender must be the wallet owner");
    if (isWallet[_wallet]) revert AlreadyRegistered();
    if (subAccountToWallet[_wallet] != address(0)) revert IsSubAccount();
    isWallet[_wallet] = true;
    emit RegisterWallet(_wallet);
}
```




## Assessed type

Other
Inadequate Signature Verification Allows Unauthorized Transaction Execution
Audit: 2023-10-brahma-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-brahma/blob/main/contracts/src/core/ExecutorPlugin.sol#L116-L120


# Vulnerability details

## Impact
The current implementation of the `_validateExecutionRequest` function in the `ExecutorPlugin` contract does not adequately handle signature verification for executors that are smart contracts. The vulnerability lies in the fact that the condition only checks for the absence of a signature if the executor is an Externally Owned Account (EOA), but it does not account for executors that are smart contracts, potentially allowing a smart contract to initiate an execution request without a valid signature.

This oversight could lead to unauthorized transactions being executed if a malicious smart contract is designated as an executor. The transactions could involve fund transfers, alterations of contract state, or interactions with external contracts in unintended ways. Depending on the permissions given to the executor and the nature of the transactions, this could compromise the integrity and security of the entire contract system.

The severity of this vulnerability is considered MEDIUM. Although exploiting it requires specific conditions (a smart contract being an executor), the potential consequences are significant, especially in a contract system intended to handle valuable transactions or sensitive logic.

## Proof of Concept
The vulnerability is present in the code snippet below:

https://github.com/code-423n4/2023-10-brahma/blob/main/contracts/src/core/ExecutorPlugin.sol#L116-L120

```solidity
        // Empty Signature check for EOA executor
        if (execRequest.executor.code.length == 0 && execRequest.executorSignature.length == 0) {
            // Executor is an EOA and no executor signature is provided
            revert InvalidSignature();
        }
```
The code checks if the executor is an EOA and if the signature is empty but lacks a corresponding condition for smart contract executors. There's no verification against a standard like EIP-1271 for smart contracts, which should be used to verify signatures provided by smart contract accounts.

Here's also a similar instance found:

https://github.com/code-423n4/2023-10-brahma/blob/main/contracts/src/core/PolicyValidator.sol#L134-L138

```solidity
        // Empty Signature check for EOA signer
        if (trustedValidator.code.length == 0 && validatorSignature.length == 0) {
            // TrustedValidator is an EOA and no trustedValidator signature is provided
            revert InvalidSignature();
        }
```
## Tools Used
Manual

## Recommended Mitigation Steps
1. Update the condition to check for an empty signature regardless of whether the executor is an EOA or a smart contract. The presence of a valid signature should be mandatory for all executors.

```solidity
if (execRequest.executorSignature.length == 0) {
    revert InvalidSignature();
}
```
2. Implement a method to handle signature verification for smart contract ex
Missing a check in `_checkSubAccountSecurityConfig` that no new modules have been introduced to the safe 
Audit: 2023-10-brahma-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-brahma/blob/dd0b41031b199a0aa214e50758943712f9f574a0/contracts/src/core/TransactionValidator.sol#L181-L199


# Vulnerability details

## Impact
Adding a new module opens up a backdoor to the SubAccount, allowing the new module to execute any action on the SubAccount.

## Proof of Concept
The checkAfterExecution function calls validatePostTransaction in the TransactionValidator.sol file, which in turn invokes the _checkSubAccountSecurityConfig function. However, in the _checkSubAccountSecurityConfig function, there is a missing check to ensure that no new modules have been introduced to the safe. When modules execute transactions on a Gnosis safe, the guard safety callbacks are not triggered. Consequently, any new module introduced is free to execute any action it wishes on the safe, creating a serious backdoor threat to the security of the safe.

```
    function _checkSubAccountSecurityConfig(address _subAccount) internal view {
        address guard = SafeHelper._getGuard(_subAccount);
        address fallbackHandler = SafeHelper._getFallbackHandler(_subAccount);

        // Ensure guard has not been disabled
        if (guard != AddressProviderService._getAuthorizedAddress(_SAFE_MODERATOR_HASH)) revert InvalidGuard();

        // Ensure fallback handler has not been altered
        if (fallbackHandler != AddressProviderService._getAuthorizedAddress(_CONSOLE_FALLBACK_HANDLER_HASH)) {
            revert InvalidFallbackHandler();
        }

        address ownerConsole =
            WalletRegistry(AddressProviderService._getRegistry(_WALLET_REGISTRY_HASH)).subAccountToWallet(_subAccount);

        // Ensure owner console as a module has not been disabled
        if (!IGnosisSafe(_subAccount).isModuleEnabled(ownerConsole)) revert InvalidModule();
    }
```

## Tools Used
Manual Review
## Recommended Mitigation Steps
Check that no new modules have been introduced to the safe, using the getModulesPaginated()


## Assessed type

Invalid Validation
ownerSafeCount gets updated while not deploying safe, leading to unexpected behavior
Audit: 2023-10-brahma-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-brahma/blob/main/contracts/src/core/SafeDeployer.sol#L253-L255


# Vulnerability details

### Impact
In `SafeDepoyer`, a consoleAccount and a subAccount can be created using either `deployConsoleAccount` or `deploySubAccount`. Both these functions will make an internal call to `_createSafe`. In `_createSafe` there is a `do-while` loop that tries to deploy a gnosis proxy using  these parameters `gnosisSafeSingleton, _initializer, nonce`.

If this deployment fails with the `_SAFE_CREATION_FAILURE_REASON` error message, the deployment will be tried again by generating a new nonce:
```javascript
function _createSafe(..){
//.. ommitted code
nonce = _genNonce(ownersHash, _salt);
} catch {
//.. ommitted code
}
```

Here lays the problem.
### Proof of Concept
Consider the following:
- Alice tries to create a consoleAccount using `deployConsoleAccount`
- The deployment fails with the `_SAFE_CREATION_FAILURE_REASON`
- Because its a `do-while` loop, the deployment will be tried again by generating a new nonce using `_genNonce`:
```javascript
    function _genNonce(bytes32 _ownersHash, bytes32 _salt) private returns (uint256) {
        return uint256(keccak256(abi.encodePacked(_ownersHash, ownerSafeCount[_ownersHash]++, _salt, VERSION)));
    }
```
- The `ownerSafeCount` will be updated `+1` without having deployed a new safe.
- This can go for multiple times and when the safe deployment actually succeeds, the `ownerSafeCount` will be displaying the wrong data, leading to unexpected behaviour in the future.
### Tools Used
Manual Review
## Recommended Mitigation Steps
Use another variable in the nonce calculation instead of `ownerSafeCount`. Update `ownerSafeCount` after a successful deployment.


## Assessed type

Other
Design Flaw, registry addresses can not be changed
Audit: 2023-10-brahma-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-brahma/blob/main/contracts/src/core/AddressProvider.sol#L97-L105


# Vulnerability details

### Impact
In `AddressProvider.sol`, registry addresses can be set using `setRegistry`. 
```javascript
source: contracts/src/core/AddressProvider.sol

function setRegistry(bytes32 _key, address _registry) external {
        _onlyGov();
        _ensureAddressProvider(_registry);

        if (registries[_key] != address(0)) revert RegistryAlreadyExists();
        registries[_key] = _registry;

        emit RegistryInitialised(_registry, _key);
    }
```

However, due to this line of code, it is not possible to change the registries:
```javascript
source: contracts/src/core/AddressProvider.sol
function setRegistry(..) {
// ommitted code
        if (registries[_key] != address(0)) revert RegistryAlreadyExists();
// ommitted code
}
```

In `setAuthorizedAddress` it is possible to change the authorized address, it `setGovernance` it is possible to change the governance but in `setRegistry` it is not possible to change the registries because if a registries is already set it will always revert with the error code `RegistryAlreadyExists`. This can lead to unexpected behaviour in the future.

### Tools Used
Manual Review
## Recommended Mitigation Steps
Allow registries, just like the governance and authorized addresses, to be overridden by removing the `if-statement`:
```diff
- if (registries[_key] != address(0)) revert RegistryAlreadyExists();
```


## Assessed type

Other
ConsoleFallbackHandler.isValidSignature does not follow ERC1271 standards
Audit: 2023-10-brahma-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-brahma/blob/main/contracts/src/core/ConsoleFallbackHandler.sol#L39-L55


# Vulnerability details

## Impact
[EIP-1271](https://eips.ethereum.org/EIPS/eip-1271) requires the `ERC1271_MAGIC_VALUE` being `0x1626ba7e` instead of `0x20c13b0b` and the `isValidSignature` signature should take `bytes32,bytes` as parameters.
This is not correctly implemented in `ConsoleFallbackHandler.isValidSignature`:
```javascript
source: contracts/src/core/ConsoleFallbackHandler.sol

bytes4 internal constant UPDATED_MAGIC_VALUE = 0x1626ba7e;

function isValidSignature(bytes memory _data, bytes memory _signature) public view override returns (bytes4) {
//.. ommitted code
return EIP1271_MAGIC_VALUE
//.. ommitted code
}
```

### Tools Used
Manual Review
## Recommended Mitigation Steps
Update `isValidSignature` to adhere to the `ERC1271` standards.



## Assessed type

Other
consoleAccounts and subAccounts created do not adhere to the ERC-4337 standard
Audit: 2023-10-brahma-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-brahma/blob/main/contracts/src/core/SafeDeployer.sol#L1-L256


# Vulnerability details

### Impact
Brahma uses version 1.3.0 of the Safe contracts. Whenever a consoleAccount or subAccount are created, `GnosisSafe.setup` gets called. In this function, `setupModules` gets called:
```javascript
function setup(
        address[] calldata _owners,
        uint256 _threshold,
        address to,
        bytes calldata data,
        address fallbackHandler,
        address paymentToken,
        uint256 payment,
        address payable paymentReceiver
    ) external {
        // setupOwners checks if the Threshold is already set, therefore preventing that this method is called twice
        setupOwners(_owners, _threshold);
        if (fallbackHandler != address(0)) internalSetFallbackHandler(fallbackHandler);
        // As setupOwners can only be called if the contract has not been initialized we don't need a check for setupModules
        // vvvvvvvvvvvvvvvvvv
        setupModules(to, data);

        if (payment > 0) {
            // To avoid running into issues with EIP-170 we reuse the handlePayment function (to avoid adjusting code of that has been verified we do not adjust the method itself)
            // baseGas = 0, gasPrice = 1 and gas = payment => amount = (payment + 0) * 1 = payment
            handlePayment(payment, 0, 1, paymentToken, paymentReceiver);
        }
        emit SafeSetup(msg.sender, _owners, _threshold, to, fallbackHandler);
    }
```

However, in `setupModules`, the `GAS` opcode is used without following this opcode up with `CALL`, `DELEGATECALL`,  `CALLCODE` or `STATICCALL`, which is required by the [ERC4337-standard](https://eips.ethereum.org/EIPS/eip-4337):
```javascript
		source: contracts/lib/safe-contracts/contracts/base/ModuleManager.sol
		
 function execTransactionFromModule(
        require(msg.sender != SENTINEL_MODULES && modules[msg.sender] != address(0), "GS104");
        // Execute transaction without further confirmations.
        // GAS opcode is used here bby using gasleft()
        success = execute(to, value, data, operation, gasleft());
```

This has been [fixed](https://github.com/safe-global/safe-contracts/blob/main/CHANGELOG.md?plain=1#L178) in the version of Safe 1.4.0, however, since Brahma implements the version 1.3.0 of the Safe contracts, this has not been implemented yet.
### Tools Used
Manual Review
## Recommended Mitigation Steps
Use a more recent version of Safe.


## Assessed type

Access Control
Authorised addresses can be set that have no AddressProviderService interface
Audit: 2023-10-brahma-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-brahma/blob/main/contracts/src/core/AddressProvider.sol#L77-L90


# Vulnerability details

### Impact
In `AddressProvider.setAuthorizedAddress`, the `governor` can set authorized addresses.  

According to the [docs](https://github.com/code-423n4/2023-10-brahma/blob/main/contracts/docs/Core.md), these authorized addresses should all implement a valid `IAdressProviderService`:
```
It enforces governance control over these updates and ensures that the addresses implement a valid `IAddressProviderService` interface.
```

This check happens in the `if-statement` in  `setAuthorizedAddress()`:
```javascript
source: contracts/src/core/AddressProvider.sol
function setAuthorizedAddress(..){
//.. ommitted code
if (_authorizedAddress.code.length != 0) _ensureAddressProvider(_authorizedAddress);
//.. ommittedd code
}
```

Here lays the problem.
### Proof of Concept
Consider the following scenario:
- A contract address gets pre-computed using `CREATE2`. This pre-computed address does not implement the `IAddressProviderService`
- This address gets added as an authorized address by calling `setAuthorizedAddress`
	- This call will succeed because the length of the address == 0 since the contract has not been deployed yet.

### Tools Used
Manual Review
## Recommended Mitigation Steps
Consider using a different way to validate if a contract has implemented `IAddressProviderService`.



## Assessed type

Invalid Validation