xanoutas/solidity-security-findings huggingface dataset:
Attakers can steal the funds from long-term reservation 
Audit: 2024-10-coded-estate-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-10-coded-estate/blob/main/contracts/codedestate/src/execute.rs#L1795


# Vulnerability details

## Description
In this protocol NFT owner can set the NFT in sale even if it is still under active rent by triggering `execute.rs#setlistforsell()` which could set `token.sell.auto_approve` to a true value (means anyone can directly be approved and this will open multiple doors for attackers)

Users can call `execute.rs#setbidtobuy()` and send the necessary amount to gain approval of this NFT

```rust
File: execute.rs#setbidtobuy()

675:             if token.sell.auto_approve {
676:                 // update the approval list (remove any for the same spender before adding)
677:                 let expires = Expiration::Never {  };
678:                 token.approvals.retain(|apr| apr.spender != info.sender);
679:                 let approval = Approval {
680:                     spender: info.sender.clone(),
681:                     expires,
682:                 };
683:                 token.approvals.push(approval);
684:                 
685:             }

```
Using the same function `setbidtobuy()` any address that has an existing bid in the NFT can cancel its bid and receive back all the initial funds (no fees in this function).

On the other side, the owner or any approved address can invoke `execute.rs#withdrawtolandlord()` and specify the receiver of the withdrawal funds (this function gives the homeowners the ability to withdraw a part of the funds even before the rent end, this is only for longterm rentals  )
```rust
File: execute.rs

1787:     pub fn withdrawtolandlord(
/**CODE**/
1796:         address:String
1797:     ) -> Result<Response<C>, ContractError> {
/**CODE**/
1848:             .add_message(BankMsg::Send {
1849:                 to_address: address,
1850:                 amount: vec![Coin {
1851:                     denom: token.longterm_rental.denom,
1852:                     amount: Uint128::from(amount) - Uint128::new((u128::from(amount) * u128::from(fee_percentage)) / 10000),

```
However, the Attacker can create a sophisticated attack using `withdrawtolandlord()` and `setbidtobuy()`
1- Choose an NFT that has a `token.sell.auto_approve == true` and an active long-term rental 
2- Call `setbidtobuy()` this will give him the necessary approval to finish the attack, he also need to transfer the asked funds .
3- Trigger `withdrawtolandlord()` and transfer the maximum amount of tokens 
```rust
File: execute.rs#withdrawtolandlord()

1832:                 if item.deposit_amount - Uint128::from(token.longterm_rental.price_per_month) < Uint128::from(amount)  {
1833:                     return Err(ContractError::UnavailableAmount {  });
1834:                 }
``` 
4- Invoke `setbidtobuy()` to receive his original deposited funds.

## Impact

Steal the funds from long-term reservations using `setbidtobuy()`

## Tools Used

Manual Review

## Recommended Mitigation Steps
```diff
File: execu
Tenant can cansel their long-term reservation and recive all the deposit amoint back
Audit: 2024-10-coded-estate-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-10-coded-estate/blob/main/contracts/codedestate/src/execute.rs#L1129-L1177


# Vulnerability details

## Description
The Coded Estate smart contract is designed in a way that if a long-term reservation gets approved by NFT owner after calling `execute.rs#setapproveforlongterm()` the `approved_date` will get updated
```rust
File: execute.rs#setapproveforlongterm()

1637:         token.rentals[position as usize].approved_date = Some(approved_date);
1638: 
1639:         self.tokens.save(deps.storage, &token_id, &token)?;
``` 
At this point, The user can cancel his reservation but he will receive zero refund by triggering `execute.rs#cancelreservationafterapprovalforlongterm()`

However, a malicious user can call `execute.rs#cancelreservationbeforeapprovalforshortterm()` and because the `token.rentals[].approved` is false in long-term rent, it will successfully return back the initial deposited amount  

Also, In the same way, the malicious user could go into more danger and wait for the rent to end and do the same thing to get free rent, But they could end up receiving back only the amount of funds of one month because NFT owner could withdraw his funds by calling  `execute.rs#withdrawtolandlord()`.

## Impact

Tenant can cancel their long-term reservation and receive all the deposit amount back or could get free long-term rent

## Tools Used

Manual Review

## Recommended Mitigation Steps

check `rental_type` in `cancelreservationbeforeapprovalforshortterm()`



## Assessed type

Other
Tenant can bypass the cancellation penalty or get free rent (short-term)
Audit: 2024-10-coded-estate-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-10-coded-estate/blob/main/contracts/codedestate/src/execute.rs#L1081-L1097


# Vulnerability details

## Description

**Case_01**
in case the short-term is auto approval or the NFT owner calls `setapproveforshortterm()` to approve the reservation.
Now, the tenant needs to trigger `cancelreservationafterapprovalforshortterm()` which will lead him to pay the cancellation penalty to the homeowner.

```rust
File: execute.rs#cancelreservationafterapprovalforshortterm()

1080:         let diff_days = (check_in_time_timestamp - current_time)/86400;
1081:         for (_i, item) in cancellation.iter().enumerate() {
1082:             if item.deadline < diff_days {
1083:                 refundable_amount =  Uint128::new((amount.u128() * u128::from(item.percentage)) / 100);
1084:                 break;
1085:             }
1086:         }

```
However, in case the `token.longterm_rental.denom == token.shortterm_rental.denom`
A malicious user can trigger `cancelreservationbeforeapprovalforlongterm()` and he will receive back all the initial deposit from his short-term reservation.

This is possible due to this check 
```rust
File: execute.rs#cancelreservationbeforeapprovalforlongterm()

1451:                 if item.approved_date.is_some() {
1452:                     return Err(ContractError::ApprovedAlready {});
1453:                 } else {
1454:                     position = i as i32;
1455:                     amount = item.deposit_amount;
1456:                 }

```  
It can not detect if the `token.rentals[].approved` is true or false because the long-term uses `token.rentals[].approved_date` to check the approval. 

**Case_01** The worst-case scenario 
 Malicious users can reserve a short-term. let's the rent get active and wait until the last minute to call `execute.rs#cancelreservationbeforeapprovalforlongterm()`
 this will let them refund all the funds. 
So, the malicious user gets free rent   

## Impact

- malicious users can get short-term free rent 
- Tenants can bypass the cancellation penalty of the short-term rent by using long-term logic
- NFT owners losing cancellation penalty and potential of other reservations (because that period was reserved and other users won't be able to reserve it)


## Tools Used

Manual Review

## Recommended Mitigation Steps

check `rental_type` in `cancelreservationbeforeapprovalforlongterm()`


## Assessed type

Invalid Validation
An attacker can steal any tokens in this contract 
Audit: 2024-10-coded-estate-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-10-coded-estate/blob/main/contracts/codedestate/src/execute.rs#L783-L905
https://github.com/code-423n4/2024-10-coded-estate/blob/main/contracts/codedestate/src/execute.rs#L1490-L1541


# Vulnerability details


## Description

This protocol has two types of rent reservation (long and short-term) 
NFT owner has the flexibility to set the required token for each type of rent to any token by invoking `setlistforshorttermrental()` or `setlistforlongtermrental()`.

Also, each reservation types has it owne functions, however no function of these check the `renting_period` is related to the long-term or the short-term.

The attacker can simply mint NFT and set the `denom` of short-term rent to token X and `denom` of long-term rent to token Y. 
Where X has less market value than Y 
e.g. 1X=1$ and 1Y= 10$
This is how to steal all the funds in this contract in multiple transactions:

- call `execute.rs#setreservationforshortterm()` and transfer 10 X tokens (total value 10$)
- trigger `execute.rs#rejectreservationforlongterm()` this function will just transfer back to the attacker 10 Y tokens (total value 100$)

This is possible because all the functions in this contract don't check if this is a long or short-term reservation 

Another similar issue (`denom` is still different)
If user call `cancelreservationafterapprovalforshortterm()` he will receive the `refundable_amount` 
However, the NFT owner must invoke `finalizeshorttermrental()` to withdraw the cancellation penalty.
But Malicious NFT owners can use `finalizelongtermrental()` to withdraw the cancellation penalty funds in another token Y,

## Impact

An attacker can steal any tokens in this contract

## Tools Used

Manual Review

## Recommended Mitigation Steps
the `Rental` struct has the field name `rental_type` which is boolean 
false is for short 
true  is for long
make sure to check it in all the functions that are specified for only one type of reservation. 



## Assessed type

Other
buyers can lock the owner's funds
Audit: 2024-10-coded-estate-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-10-coded-estate/blob/main/contracts/codedestate/src/execute.rs#L385


# Vulnerability details


## Description

After the NFT owner sets his token on sale with `autoApprove == true`, the buyer can transfer the asked amount to the contract and gain permission to call `transfer_nft()`  
It checks if the param `recipient` has an existing offer inside `token.bids`
```rust
        for (i, item) in token.bids.iter().enumerate() {
            if item.address == recipient.to_string()
            {
                position = i as i32;
                amount = item.offer.into();
                break;  
```
if yes, it will cut the protocol fee and transfer the funds to the previous owner. 

However, a malicious user can pay the asked amount by the owner. but when it comes to calling the `transfer_nft()` function he will set the `recipient` param to an address that he owned. By doing this the for loop will never update the value of `position` and `amount` (Review the code snippet above).

which will make the logic to jump to the else block and not transfer the fund to the seller 
```rust
        if amount > Uint128::new(0) {
        /***CODE***/
            .add_message(BankMsg::Send {
                to_address: prev_owner.to_string(),
                amount: vec![Coin {
                    denom: token.sell.denom,
                    amount: amount_after_fee,
                }],
            }))
        } else {
            Ok(Response::new()
            .add_attribute("action", "transfer_nft")
            .add_attribute("sender", info.sender.clone())
            .add_attribute("token_id", token_id))
        }
```

## Impact

malicious buyer can arbitrarily lock the owner's funds. so NFT owner will sell his token but receive nothing.

## Tools Used

Manual Review

## Recommended Mitigation Steps

Check against the `info.sender`
```diff
        for (i, item) in token.bids.iter().enumerate() {
-            if item.address == recipient.to_string()
+            if item.address == info.sender.to_string()
            {
                position = i as i32;
                amount = item.offer.into();
                break;  
            }
        }
```



## Assessed type

Other
Attacker can drain all the funds in this contract 
Audit: 2024-10-coded-estate-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-10-coded-estate/blob/main/contracts/codedestate/src/execute.rs#L708


# Vulnerability details

## Description
Every NFT has this `pub sell: Sell` field in its `TokenInfo` struct which is for selling NFTs
```rust
pub struct Sell {
    pub islisted: Option<bool>,
    pub auto_approve: bool,
    pub price: u64,
    pub denom: String,
}
```
This contract is used by multiple homeowners which means it holds the funds (from the reservation and bid) for all the NFT in one place.
NFT owner can update the `pub sell: Sell` values by triggering `execute.rs#setlistforsell()` 

```rust
    pub fn setlistforsell(
    /***CODE***/
    ) -> Result<Response<C>, ContractError> {

        let mut token = self.tokens.load(deps.storage, &token_id)?;
        // ensure we have permissions
        self.check_can_approve(deps.as_ref(), &env, &info, &token)?;

        token.sell.islisted = Some(islisted);
        token.sell.price = price;
        token.sell.auto_approve = auto_approve;
        token.sell.denom = denom;
        self.tokens.save(deps.storage, &token_id, &token)?;
```
Due to the lack of checking the current state of the NFT in this function address with permissions can update the values at any time.
Malicious users can take this advantage to steal all the funds (native token NIBI + all tokens from IBC ) by:
1- Mint NFT and set it for sell, set the `token.sell.denom` to IBC token that has a dust value in the market
2- Then call `setbidtobuy()` function to set `offer:info.funds[0].amount` this amount will stay in the smart contract 
3- Now, re-set  `token.sell.denom` (by invoking `setlistforsell()`) to a token that has more value (or even fewer decimals)
4- Back to `setbidtobuy()` call it, this will lead to draining all the funds (of the specific token) in this cw721 smart contract  

this happens because this message takes `denom` from the sell ` denom: token.sell.denom` which can be changed at any time.
```rust
            .add_message(BankMsg::Send {
                to_address: info.sender.to_string(),
                amount: vec![Coin {
                    denom: token.sell.denom,
                    amount: amount,
                }],
            }))
```
 
## Impact

An attacker can drain all the funds in this contract

Manual Review

## Recommended Mitigation Steps

Do not allow to update `denom` if `pub bids: Vec<Bid>` is not empty 



## Assessed type

Invalid Validation
Malicious users can steal NFT that under sell
Audit: 2024-10-coded-estate-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-10-coded-estate/blob/main/contracts/codedestate/src/execute.rs#L429


# Vulnerability details

## Description

`execute.rs#send_nft()` Moves `token_id` from the `info.sender` account (or have permissions) to the contract account. contract must be an address of a contract that implements the Receiver interface. The `msg` will be passed to the recipient contract, along with the  `token_id`.

However, this protocol is built on top of cw721 and has multiple features that must be handled.
One of them is the transfer ownership function for selling NFTs
users will trigger `SetBidToBuy()` function to get approved, and then they transfer NFT to take ownership.
  
In CW721 we have two ways to transfer ownership
- `send_nft(deps, env, info, contract, token_id, msg)`
- transfer_nft(deps, env, info, recipient, token_id)

The `transfer_nft()` function transfers the funds paid by the buyer to the seller. 
But `send_nft()` sub-call to `_transfer_nft()`
```rust
 fn send_nft(
   /*CODE*/
    ) -> Result<Response<C>, ContractError> {
        // Transfer token
        self._transfer_nft(deps, &env, &info, &contract, &token_id)?;

        let send = Cw721ReceiveMsg {
            sender: info.sender.to_string(),
            token_id: token_id.clone(),
            msg,
        };
```
The `_transfer_nft` is a stander function for CW721 don't check the sells 

So, a malicious user can call `SetBidToBuy()` (assume it has an auto approve) then he just invokes `send_nft()` after he deploys a contract to receive the NFT on it. 
The `check_can_send()` check will not revert.
```rust
        // any non-expired token approval can send
        if token
            .approvals
            .iter()
            .any(|apr| apr.spender == info.sender && !apr.is_expired(&env.block))
        {
            return Ok(());
        }
```
Now, he can just call `SetBidToBuy()` again to receive back his funds from the Coded Estate contract.

## Impact

Malicious users can steal NFT that under sell 

## Tools Used

Manual Review

## Recommended Mitigation Steps
```diff
 fn send_nft(
   /*CODE*/
    ) -> Result<Response<C>, ContractError> {
        // Transfer token
-       self._transfer_nft(deps, &env, &info, &contract, &token_id)?;
+       self.transfer_nft(deps, &env, &info, &contract, &token_id)?;

        let send = Cw721ReceiveMsg {
            sender: info.sender.to_string(),
            token_id: token_id.clone(),
            msg,
        };
```


## Assessed type

Other
Malicious homeowners can rug
Audit: 2024-10-coded-estate-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-10-coded-estate/blob/main/contracts/codedestate/src/execute.rs#L523-L541


# Vulnerability details

## Description

Homeowners can trigger `burn()` (CW_721) from `execute.rs` file. 
They can pass the `token_id` and burn it without checking if the homeowner has ongoing rent or any reservations.

```rust
    fn burn(
        &self,
        deps: DepsMut,
        env: Env,
        info: MessageInfo,
        token_id: String,
    ) -> Result<Response<C>, ContractError> {
        let token = self.tokens.load(deps.storage, &token_id)?;
        self.check_can_send(deps.as_ref(), &env, &info, &token)?;

        self.tokens.remove(deps.storage, &token_id)?;
        self.decrement_tokens(deps.storage)?;
``` 
So, a malicious homeowner can rug his customers which will lead the travelers with approved/non-approved rent will lose their deposit.
By triggering `execute.rs#burn()` at any time.

## Impact

- Malicious homeowners can rug  
- Travelers with approved/non-approved rent will lose their funds
- the current tenant can't finish his paid period 

## Tools Used

Manual Review

## Recommended Mitigation Steps
call `check_can_edit_long()` or `check_can_edit_short()` (currently are the same thing) to check if the `rentals` is empty or no active rent/reservation 
```diff
File: execute.rs#burn()

fn burn(
        &self,
        deps: DepsMut,
        env: Env,
        info: MessageInfo,
        token_id: String,
    ) -> Result<Response<C>, ContractError> {
        let token = self.tokens.load(deps.storage, &token_id)?;
        self.check_can_send(deps.as_ref(), &env, &info, &token)?;
+       self.check_can_edit_short(&env, &token)?;

```


## Assessed type

Rug-Pull
withdrawToLandlord() cant send all rent earned overtime to landlord
Audit: 2024-10-coded-estate-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-10-coded-estate/blob/97efb35fd3734676f33598e6dff70119e41c7032/contracts/codedestate/src/execute.rs#L1823-L1834


# Vulnerability details

## Impact
`withdrawtolandlord()` function does not  allow token.ower or any approved/operator address to withdraw all the earned rent to the provided landlord address 

## Proof Of Concept 
https://github.com/code-423n4/2024-10-coded-estate/blob/97efb35fd3734676f33598e6dff70119e41c7032/contracts/codedestate/src/execute.rs#L1823-L1834
```rust 
           for (i, item) in token.rentals.iter().enumerate() {
            if item.address == Some(Addr::unchecked(tenant.clone()))
                && item.renting_period[0].to_string() == renting_period[0]
                && item.renting_period[1].to_string() == renting_period[1]
            {
                position = i as i32;
                if item.cancelled {
                    return Err(ContractError::NotApproved { });
                }
                if item.deposit_amount - Uint128::from(token.longterm_rental.price_per_month) < Uint128::from(amount)  { 
                    return Err(ContractError::UnavailableAmount {  });
                }

            }
```

The snippet above is from the iteration logic in `withdrawtolandlord()`. The inner if statement here does not allow the landlord to receive 100% of the rental value it has earned on the protocol. 

For example if a tennant has paid 10,000 USD over the span of 10 months and `price_per_month` is 1000 USD, the landlord wants to receive its 10,000 USD which is the total rent earned over time, the logic there will interpret to be:
 
item.deposit_amount = 10,000
token.longterm_rental.price_per_month = 1000 
amount = 10,000 

```rust 
                if  10,000 - Uint128::from(1000) < Uint128::from(10,000)  { 
                    return Err(ContractError::UnavailableAmount {  });
                }

```

 10,000 - 1,000 = 9,000 is lower than 10,000 (the amount to be withdrawn) so this means the function will revert with `unavailableAmount` error. A months's rent will be stuck in the protocol and cannot be sent to the landlord. 

The validation for amount to be withdrawn to landlord is incorrect. 

## Recommened Mitigation 
change the validation to be 

```rust
if  Uint128::from(amount) > item.deposit_amount { 
                    return Err(ContractError::UnavailableAmount {  });
                }

```





## Assessed type

Context