xanoutas/solidity-security-findings huggingface dataset:
Denial of Service Due to Underflow Error in MagicSpend
Audit: 2024-03-coinbase-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-coinbase/blob/main/src/MagicSpend/MagicSpend.sol#L156


# Vulnerability details

## Impact
Denial of Service Due to Underflow Error in postOp(...) function in MagicSpend contract
## Proof of Concept
```solidity
   function postOp(IPaymaster.PostOpMode mode, bytes calldata context, uint256 actualGasCost)
        external
        onlyEntryPoint
    {
        // `PostOpMode.postOpReverted` should be impossible.
        // Only possible cause would be if this contract does not own enough ETH to transfer
        // but this is checked at the validation step.
        assert(mode != PostOpMode.postOpReverted);

>>>        (uint256 maxGasCost, address account) = abi.decode(context, (uint256, address));

        // Compute the total remaining funds available for the user accout.
        // NOTE: Take into account the user operation gas that was not consummed.
>>>        uint256 withdrawable = _withdrawableETH[account] + (maxGasCost - actualGasCost);

        // Send the all remaining funds to the user accout.
        delete _withdrawableETH[account];
        if (withdrawable > 0) {
            SafeTransferLib.forceSafeTransferETH(account, withdrawable, SafeTransferLib.GAS_STIPEND_NO_STORAGE_WRITES);
        }
    }
```
The function above shows how postOp(...) function is implemented in the MagicSpend contract. As noted from the pointer a subtraction operation is carried out between maxGasCost  and actualGasCost, depending on the external contract calling this function, whenever actualGasCost is greater than maxGasCost which is derived from context decode, there would be denial of service due to underflow error, there by reverting.
## Tools Used
Manual Review
## Recommended Mitigation Steps
Protocol should adjust postOp(...) function such that when `actualGasCost` is greater than `maxGasCost`, it is reassigned the maxGasCost value to prevent Denial of Service to any external contract calling this function, as provided below.
```solidity
   function postOp(IPaymaster.PostOpMode mode, bytes calldata context, uint256 actualGasCost)
        external
        onlyEntryPoint
    {
        // `PostOpMode.postOpReverted` should be impossible.
        // Only possible cause would be if this contract does not own enough ETH to transfer
        // but this is checked at the validation step.
        assert(mode != PostOpMode.postOpReverted);

        (uint256 maxGasCost, address account) = abi.decode(context, (uint256, address));
+++     if ( actualGasCost > maxGasCost ) {
+++       actualGasCost = maxGasCost;
+++     }
        // Compute the total remaining funds available for the user accout.
        // NOTE: Take into account the user operation gas that was not consummed.
        uint256 withdrawable = _withdrawableETH[account] + (maxGasCost - actualGasCost);

        // Send the all remaining funds to the user accout.
        delete _withdrawableETH[account];
        if (withdrawable > 0) {
            SafeTran
Non-compliance with EIP-4337
Audit: 2024-03-coinbase-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-coinbase/blob/main/src/SmartWallet/CoinbaseSmartWallet.sol#L137


# Vulnerability details

## Impact
CoinbaseSmartWallet.sol validateUserOp() is not compliant with the EIP-4337.
According to the https://eips.ethereum.org/EIPS/eip-4337 validateUserOp() should return a time range, as per the EIP specifications:
```
The return value is packed of sigFailure, validUntil and validAfter timestamps.
		- sigFailure is 1 byte value of “1” the signature check failed (should not revert on signature failure, to support estimate)
		- validUntil is 8-byte timestamp value, or zero for “infinite”. The UserOp is valid only up to this time.
		- validAfter is 8-byte timestamp. The UserOp is valid only after this time.
```

## Proof of Concept
https://github.com/code-423n4/2024-03-coinbase/blob/main/src/SmartWallet/CoinbaseSmartWallet.sol#L137

## Tools Used


## Recommended Mitigation Steps
Refactor the code that is not compliant with the EIP


## Assessed type

Other
Enhancing User Interaction in ERC4337: Addressing Challenges with Account Removal with function removeOwnerAtIndex
Audit: 2024-03-coinbase-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/SmartWallet/MultiOwnable.sol#L97-L110


# Vulnerability details



## Introduction:

ERC4337 aims to improve user interaction within the Ethereum ecosystem, striving for smoother and more intuitive operations. While the protocol has made significant progress towards this goal, an issue arises with the `removeOwnerAtIndex` function, which requires users to know the index of the account they wish to remove. This reliance on index-based removal complicates the user experience and may lead to errors, especially when dealing with multiple accounts.

## Issue Description:
The `removeOwnerAtIndex` function in ERC4337 presents a usability challenge for users as it requires knowledge of the index of the account to be removed

```solidity
   /// @notice Removes an owner from the given `index`.
    ///
    /// @dev Reverts if the owner is not registered at `index`.
    ///
    /// @param index The index to remove from.
    function removeOwnerAtIndex(uint256 index) public virtual onlyOwner {
        bytes memory owner = ownerAtIndex(index);
        if (owner.length == 0) revert NoOwnerAtIndex(index);

        delete _getMultiOwnableStorage().isOwner[owner];
        delete _getMultiOwnableStorage().ownerAtIndex[index];

        emit RemoveOwner(index, owner);
    }
``` 
. Unlike other functions within the protocol that allow seamless interaction, this function lacks a straightforward method for identifying accounts. Instead, users must manually determine the index of the account they wish to remove, which can be cumbersome 
```solidity
/// @notice Returns the owner bytes at the given `index`.
///
/// @param index The index to lookup.
///
/// @return The owner bytes (empty if no owner is registered at this `index`).
function ownerAtIndex(uint256 index) public view virtual returns (bytes memory) {
    return _getMultiOwnableStorage().ownerAtIndex[index];
}
``` 
and prone to errors if user guesses or pass in a wrong index that is available but not the account desired to remove .

## Impact:
The reliance on index-based account removal undermines the user-friendliness of ERC4337 and may result in frustration and inefficiency for users. This issue can lead to confusion and errors, particularly when managing multiple accounts. As a result, users may experience difficulty in removing accounts, hindering the protocol's effectiveness in facilitating smooth user interactions.

## Proof of Concept:
The issue with the `removeOwnerAtIndex` function can be demonstrated through testing scenarios where users attempt to remove accounts without knowing the exact index. Users may face challenges in identifying the correct index, leading to errors and potential frustration. Without a straightforward method for identifying accounts, users may struggle to effectively manage their accounts within the protocol. 
if Alice passed in 6 owners addresses in the creation of th
Passkey based owner can't be able call onlyOwner protected functions 
Audit: 2024-03-coinbase-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-coinbase/blob/main/src/SmartWallet/MultiOwnable.sol#L201-L208


# Vulnerability details

## Impact
Currently, an owner with a secp256r1 public key (X, Y) cannot access or execute their wallet without relying on an intermediate `entryPoint` contract.`CoinbaseSmartWallet.sol#onlyEntryPointOrOwner` and `MultiOwnable.sol#onlyOwner` modifier using `_checkOwner()` function that is checking current context `caller()` is owner or not. 

```=201
    function _checkOwner() internal view virtual {
        if (isOwnerAddress(msg.sender) || (msg.sender == address(this))) {
            return;
        }

        revert Unauthorized();
    }
``` 



## Proof of Concept
The provided demo site (https://www.smart-wallet.xyz/) allows creating and initializing a smart account using secp256r1 public key (X, Y) parameters as the owner. However, the user interface (UX) currently lacks functionality for users to enter their secp256k1-based address. 

To demonstrate the issue, I created a smart account using the demo site (https://www.smart-wallet.xyz/). The transaction details (txHash) are provided below:


txHash: https://sepolia.basescan.org/tx/0x08655455bd09cc4615eec7059b9f62c474921a697dbeee98939b60c89724abca
Function: handleOps((address,uint256,bytes,bytes,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[], address)

| Field Name | Type | Data | Description |
|---|---|---|---|
| `ops.sender` | address | 0x5d92BC2F31ec161DFf552d086E9FCcb55Bb0C993 | Sender's address |
| `ops.nonce` | uint256 | 0 | Transaction nonce |
| `ops.initCode` | bytes | (decoded below) | Initialization code for smart account creation |
|  |  | - First 20 bytes: Factory address | |
|  |  | - Remaining bytes: Function parameters | |
| `ops.callData` | bytes | (decoded below) | Additional function call data |
| `ops.callGasLimit` | uint256 | 93616 | Gas limit for the transaction |
| `ops.verificationGasLimit` | uint256 | 866208 | Gas limit for transaction verification |
| `ops.preVerificationGas` | uint256 | 697514620 | Pre-allocated gas for verification |
| `ops.maxFeePerGas` | uint256 | 1000264 | Maximum fee per unit of gas |
| `ops.maxPriorityFeePerGas` | uint256 | 1000000 | Maximum priority fee per unit of gas |
| `ops.paymasterAndData` | bytes | 0xf5d253... (truncated) | Paymaster and data (optional) |
| `ops.signature` | bytes | 00000000... (truncated) | Transaction signature |
| `beneficiary` | address | 0x8f508CEF7c94883655751408B9bA80c94937288b | Beneficiary address (unused in this case) |



The first 20 bytes of initCode represent the factory address.The remaining bytes contain the function parameters and their decoding is shown below :

```
createAccount(bytes[],uint256) -> 3ffba36f
bytes[] owners -> ['0xc21c9b3ff1e51f71d79c58fe32bb9baf27fcfeba8ba250b24a73b59de4631923a850b4c8dec0303339bc6e81e74616f3669319496d04cf2854e7e62ceb2c71e7']
uint256 nonce -> 0
```

## Tools Used
manual review

## Recommended Mitigation Steps
change `_ch
Lack of deadline for issued signatures
Audit: 2024-03-coinbase-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/WebAuthnSol/WebAuthn.sol#L21
https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/WebAuthnSol/WebAuthn.sol#L104
https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/SmartWallet/CoinbaseSmartWallet.sol#L137
https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/SmartWallet/CoinbaseSmartWallet.sol#L291


# Vulnerability details


## Impact
 
The providied `WebAuthnAuth` signature lacks a deadline parameter causing that the signatures can be postponed indefinitely. As a result, transactions using the signature can be held, as the action can be executed at any time in the future (possibly at an unfavorable moment for the signer), and the signer has no means to invalidate the signature.

The same works in the [`validateUserOp`](https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/SmartWallet/CoinbaseSmartWallet.sol#L137C14-L137C28) function in which upon verification success, the _validateSignature() function returns a result of 0, indicating the signature is correct and valid, without imposing any time-based restrictions. However, this approach results in the user operation's signature remaining valid indefinitely. This indefinite validity could expose users to specific attack vectors. For example, if a bundler behaves maliciously, they could withhold a user operation after receiving it from the user and submit the bundling transaction when there is a favorable profit opportunity. 

## Proof of Concept

The `WebAuthnAuth` struct doesn't hold a deadline parameter
```
    struct WebAuthnAuth {
        /// @dev The WebAuthn authenticator data.
        ///      See https://www.w3.org/TR/webauthn-2/#dom-authenticatorassertionresponse-authenticatordata.
        bytes authenticatorData;
        /// @dev The WebAuthn client data JSON.
        ///      See https://www.w3.org/TR/webauthn-2/#dom-authenticatorresponse-clientdatajson.
        string clientDataJSON;
        /// @dev The index at which "challenge":"..." occurs in `clientDataJSON`.
        uint256 challengeIndex;
        /// @dev The index at which "type":"..." occurs in `clientDataJSON`.
        uint256 typeIndex;
        /// @dev The r value of secp256r1 signature
        uint256 r;
        /// @dev The s value of secp256r1 signature
        uint256 s;
    }
```

and upon verification through the verify function, the verification returns true or false depending on the the validity.
```
    function verify(bytes memory challenge, bool requireUV, WebAuthnAuth memory webAuthnAuth, uint256 x, uint256 y)
        internal
        view
        returns (bool)
    {
        if (webAuthnAuth.s > P256_N_DIV_2) {
            // guard against signature malleability
            return false;
        }

        // 11. Verify that
Does not send missed fund to entry point when validateUserOp is triggered
Audit: 2024-03-coinbase-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/SmartWallet/CoinbaseSmartWallet.sol#L137


# Vulnerability details

# Does not send missed fund to entry point when validateUserOp is triggered

### Line of code
1. https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/SmartWallet/CoinbaseSmartWallet.sol#L137
2. https://eips.ethereum.org/EIPS/eip-4337

### Proof of concept
```solidity
function validateUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds)
        public
        payable
        virtual
        onlyEntryPoint
        payPrefund(missingAccountFunds)
        returns (uint256 validationData)
    {
        uint256 key = userOp.nonce >> 64;

        // 0xbf6ba1fc = bytes4(keccak256("executeWithoutChainIdValidation(bytes)"))
        if (userOp.callData.length >= 4 && bytes4(userOp.callData[0:4]) == 0xbf6ba1fc) {
            userOpHash = getUserOpHashWithoutChainId(userOp);
            if (key != REPLAYABLE_NONCE_KEY) {
                revert InvalidNonceKey(key);
            }
        } else {
            if (key == REPLAYABLE_NONCE_KEY) {
                revert InvalidNonceKey(key);
            }
        }

        // Return 0 if the recovered address matches the owner.
        if (_validateSignature(userOpHash, userOp.signature)) {
            return 0;
        }

        // Else return 1, which is equivalent to:
        // `(uint256(validAfter) << (160 + 48)) | (uint256(validUntil) << 160) | (success ? 0 : 1)`
        // where `validUntil` is 0 (indefinite) and `validAfter` is 0.
        return 1;
    }
```
If we take a look at the EIP 4337 specification,

In Account Contract Interface section,

[Link to EIP 4337 specification](https://eips.ethereum.org/EIPS/eip-4337)
```
MUST pay the entryPoint (caller) at least the “missingAccountFunds” (which might be zero, in case current account’s deposit is high enough)
```
but there is lack of logic to transfer the missingAccountFunds to entry point
### Impact
 when the account’s deposit is insufficient, the transaction will be halt because of missing payment
### Recommendation
send missed fund to entry point when validateUserOp is triggered


## Assessed type

Token-Transfer
Smart wallet is not capable of receiving NFT
Audit: 2024-03-coinbase-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/SmartWallet/CoinbaseSmartWallet.sol#L20


# Vulnerability details

# Smart wallet is not capable of receiving NFT

### Line of code
1. https://eips.ethereum.org/EIPS/eip-721
2. https://eips.ethereum.org/EIPS/eip-1155
### Proof of concept
[Line of code](https://eips.ethereum.org/EIPS/eip-721)
```solidity
/// @notice Transfers the ownership of an NFT from one address to another address
```
```solidity
/// @dev Throws unless `msg.sender` is the current owner, an authorized
///  operator, or the approved address for this NFT. Throws if `_from` is
///  not the current owner. Throws if `_to` is the zero address. Throws if
///  `_tokenId` is not a valid NFT. When transfer is complete, this function
///  checks if `_to` is a smart contract (code size > 0). If so, it calls
///  `onERC721Received` on `_to` and throws if the return value is not
///  `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
/// @param data Additional data with no specified format, sent in call to `_to`
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;
```
[Line of code](https://eips.ethereum.org/EIPS/eip-1155)
```solidity
interface ERC1155TokenReceiver {
    /**
        @notice Handle the receipt of a single ERC1155 token type.
        @dev An ERC1155-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeTransferFrom` after the balance has been updated.        
        This function MUST return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` (i.e. 0xf23a6e61) if it accepts the transfer.
        This function MUST revert if it rejects the transfer.
        Return of any other value than the prescribed keccak256 generated value MUST result in the transaction being reverted by the caller.
        @param _operator  The address which initiated the transfer (i.e. msg.sender)
        @param _from      The address which previously owned the token
        @param _id        The ID of the token being transferred
        @param _value     The amount of tokens being transferred
        @param _data      Additional data with no specified format
        @return           `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
    */
    function onERC1155Received(address _operator, address _from, uint256 _id, uint256 _value, bytes calldata _data) external returns(bytes4);

```
### Impact
but because the smart contract does not implement onERC1155Received and onERC721Received, 

the smart contract is not capable of receiving NFT via safeTransfer, which is one of very common usage of wallet.
### Recommendation 
 implement the `ERC721Receiver` and `ERC1155TokenReceiver` interfaces to handle incoming
CoinBaseSmartWallet.sol` is intended to be upgradable but inherits from contracts that contain storage and no gaps
Audit: 2024-03-coinbase-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/SmartWallet/MultiOwnable.sol#L36


# Vulnerability details

## Impact
CoinbaseSmartWallet.sol inherits from contracts that are not stateless and don't contain storage gaps which can be dangerous when upgrading.
## Proof of Concept
When creating upgradable contracts that inherit from other contracts is important that there are storage gap in case storage variable are added to inherited contracts. If an inherited contract is a stateless contract (i.e. it doesn't have any storage) then it is acceptable to omit a storage gap, since these function similar to libraries and aren't intended to add any storage. The issue is that CoinbaseSmartWallet.sol inherits from contracts that contain storage that don't contain any gaps such as MultiOwnable.sol. These contracts can pose a significant risk when updating a contract because they can shift the storage slots of all inherited contracts.
## Tools Used
Manual review.
## Recommended Mitigation Steps
Add storage gaps to all inherited contracts that contain storage variables.




## Assessed type

Upgradable
Zero Elliptic Curve Will pass through the Affine Representation
Audit: 2024-03-coinbase-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-coinbase/blob/main/src/FreshCryptoLib/FCL.sol#L290


# Vulnerability details

## Impact
The ecAff_IsZero function is designed to check if the curve is the zero curve in affine representation. However, it only validates the second parameter y, ignoring the first parameter. This could lead to incorrect results when the function is invoked with a non-zero x value and a zero y value. In a worst-case scenario, this could be exploited to bypass certain security checks or validations, leading to potential vulnerabilities in the system. This would also affect the function usage at [L278-L279](https://github.com/code-423n4/2024-03-coinbase/blob/main/src/FreshCryptoLib/FCL.sol#L278-L279) of the contract.
## Proof of Concept
```solidity
    /**
>>>     * @dev Check if the curve is the zero curve in affine rep.
     */
    // uint256 x, uint256 y)
    function ecAff_IsZero(uint256, uint256 y) internal pure returns (bool flag) {
>>>        return (y == 0);
    }
```
The code above shows how ecAff_IsZero(...) function is implemented in the FCL contract, as noted above, the function is suppose to check if the curve is the zero curve in affine rep, the function takes two parameters but only checks if y is zero. The x parameter is not used in the function, which could lead to potential issues if the function is used in a context where both x and y should be validated.
## Tools Used
Manual Review
## Recommended Mitigation Steps
The Protocol should adjust the code to ensure in the coordinate (X,Y), only Y is not validated but both X and Y where necessary
The correct adjustment could look something like this depending on Protocol expectations:
```solidity
function ecAff_IsZero(uint256 x, uint256 y) internal pure returns (bool flag) {
    return (x == 0 && y == 0);
}
```


## Assessed type

Invalid Validation
Lack of Validation on Decoded Value
Audit: 2024-03-coinbase-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/MagicSpend/MagicSpend.sol#L143-L163


# Vulnerability details

## Impact
The issue arises because the `account` is directly retrieved from the encoded `context` input without any form of verification. Since `account` is used in later functions, this could be problematic.

## Proof of Concept
```solidity=143
    function postOp(IPaymaster.PostOpMode mode, bytes calldata context, uint256 actualGasCost)
        external
        onlyEntryPoint
    {
        // `PostOpMode.postOpReverted` should be impossible.
        // Only possible cause would be if this contract does not own enough ETH to transfer
        // but this is checked at the validation step.
        assert(mode != PostOpMode.postOpReverted);

        (uint256 maxGasCost, address account) = abi.decode(context, (uint256, address));

        // Compute the total remaining funds available for the user accout.
        // NOTE: Take into account the user operation gas that was not consummed.
        uint256 withdrawable = _withdrawableETH[account] + (maxGasCost - actualGasCost);

        // Send the all remaining funds to the user accout.
        delete _withdrawableETH[account];
        if (withdrawable > 0) {
            SafeTransferLib.forceSafeTransferETH(account, withdrawable, SafeTransferLib.GAS_STIPEND_NO_STORAGE_WRITES);
        }
    }
```

## Tools Used
VS

## Recommended Mitigation Steps
It's recommended to add validations in the `postOp` function.


## Assessed type

en/de-code