xanoutas/solidity-security-findings huggingface dataset:
Lack of validation in `setlistforsell` allows changing denom while there is active bid, leading to stealing of other users' 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#L619-L645


# Vulnerability details

## Impact
This vulnerability allows attacker to manipulate the token denom during an active bid. By exploiting this bug, attackers can cancel their own bids and receive refunds in a more valuable token than originally used, effectively stealing funds from the contract's pool of user deposits.  
## Description
The bug stems from a lack of validation in the `setlistforsell` function, which allows sellers to change the payment token (denom) even when there are active bids on a token.  

The `setbidtobuy` function, when used to cancel a bid, refunds the buyer using the current denom specified for the token:  
```rust
pub fn setlistforsell(
    &self,
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    islisted:bool,
    token_id: String,
    denom: String,
    price: u64,
    auto_approve: bool,
) -> 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)?;

    // @c4-contest: no validation whether there is active bid
    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)?;

    Ok(Response::new()
        .add_attribute("action", "setlistforsell")
        .add_attribute("sender", info.sender)
        .add_attribute("token_id", token_id))
}
pub fn setbidtobuy(
    &self,
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    token_id: String,
) -> Result<Response<C>, ContractError> {
    // ... (snipped code)

    if position != -1 && (amount > Uint128::from(0u64)) { // if the bid exists
        Ok(Response::new()
        .add_attribute("action", "setbidtobuy")
        .add_attribute("sender", info.sender.clone())
        .add_attribute("token_id", token_id)
        .add_message(BankMsg::Send {
            to_address: info.sender.to_string(),
            amount: vec![Coin {
                denom: token.sell.denom, // funds are sent back in the denom set in `setlistforsell`
                amount: amount,
            }],
        }))
    }
    // ... (snipped code)
}
```

However, the `setlistforsell` function lacks checks for active bids, allowing a seller to change the denom at any time. This creates an exploit scenario where an attacker can:  
1. Mint a new token.
2. List the token for sale, specifying a low-value token (e.g., TokenX worth $0.01) as the denom.
3. Bid on their own token, paying with the low-value TokenX.
4. Call `setlistforsell` again, changing the denom to a high-value token (e.g., USDC worth $1).
5. Cancel their bid by calling `setbidtobuy`, receiving a refund in the new, more valuable USDC.  

This exploit allows the attacker to drain funds from the contract that were deposited by other
Logic flaw in `check_can_edit_short` allows editing short-term rental before finalization enabling theft of users' deposited 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#L1953-L1972


# Vulnerability details

## Impact
Malicious actor can exploit this vulnerability to steal other users' deposited token from the contract.  
## Description
The landlord (property owner) invokes `finalizeshorttermrental` on a specific rental to settle the payment. If the rental is canceled after approval or has concluded (reached check-out time), the contract sends the payment to the token owner's address.  

The bug stems from an oversight in the function that checks whether a property can be re-listed for short-term rental.  
The `finalizeshorttermrental` function uses the `denom` (token type) stored in the `shortterm_rental` struct to determine which token to use for payment:  
```rust
fn finalizeshorttermrental(
    ...snipped...
    if amount > Uint128::new(0) {
    Ok(Response::new()
        .add_attribute("action", "finalizeshorttermrental")
        .add_attribute("sender", info.sender)
        .add_attribute("token_id", token_id)
        .add_message(BankMsg::Send {
            to_address: target.clone(),
            amount: vec![Coin {
                denom: token.shortterm_rental.denom, // @contest-info denom is loaded from short-term rental agreement
                amount: amount,
            }],
        }))            
    } 
    ...snipped...
```

The `setlistforshorttermrental` function, which can change this `denom`, is supposed to be callable only when there are no active rentals. This is checked by the `check_can_edit_short` function:  
```rust
pub fn setlistforshorttermrental(
// function arguments
) -> 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)?;
    self.check_can_edit_short(&env, &token)?;

    token.shortterm_rental.islisted = Some(true);
    token.shortterm_rental.price_per_day = price_per_day;
    token.shortterm_rental.available_period = available_period;
    token.shortterm_rental.auto_approve = auto_approve;
    token.shortterm_rental.denom = denom;
    token.shortterm_rental.minimum_stay = minimum_stay;
    token.shortterm_rental.cancellation = cancellation;
    self.tokens.save(deps.storage, &token_id, &token)?;

    Ok(Response::new()
        .add_attribute("action", "setlistforshorttermrental")
        .add_attribute("sender", info.sender)
        .add_attribute("token_id", token_id))
}
pub fn check_can_edit_short(
    &self,
    env:&Env,
    token:&TokenInfo<T>,
) -> Result<(), ContractError> {
    if token.rentals.len() == 0 {
        return Ok(());
    }
    else {
        let current_time = env.block.time.seconds();
        let last_check_out_time = token.rentals[token.rentals.len()-1].renting_period[1];
        if last_check_out_time < current_time {
            return Ok(());
        }
        else {
            return Err(Contrac
Adversary can use `send_nft` to bypass the payment and steal seller's token in auto-approve scenario  
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#L419-L444


# Vulnerability details

## Impact
This vulnerability allows malicious actor to steal tokens without payment when auto-approve is enabled.  
## Description
The bug arises from an oversight in the token transfer mechanisms when `auto_approve` is set to true. While the `transfer_nft` function includes logic for settling payments, the `send_nft` function does not.  

When a seller enables `auto_approve`, a bidder is granted approval of the token upon calling the `setbidtobuy` function. This approval is intended to allow the buyer to use `transfer_nft` to complete the trade, as this function handles both the token transfer and payment settlement.  

However, the contract fails to account for the `send_nft` function, which can also be used to transfer tokens. Unlike `transfer_nft`, `send_nft` does not include any trade settlement logic:  
```rust
File: contracts/codedestate/src/execute.rs
fn send_nft(
    &self,
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    contract: String,
    token_id: String,
    msg: Binary,
) -> Result<Response<C>, ContractError> {
    // Transfer token
    self._transfer_nft(deps, &env, &info, &contract, &token_id)?; // @c4-contest: just transfer token, no trade settlement logic

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

    // Send message
    Ok(Response::new()
        .add_message(send.into_cosmos_msg(contract.clone())?)
        .add_attribute("action", "send_nft")
        .add_attribute("sender", info.sender)
        .add_attribute("recipient", contract)
        .add_attribute("token_id", token_id))
}

pub fn _transfer_nft(
    &self,
    deps: DepsMut,
    env: &Env,
    info: &MessageInfo,
    recipient: &str,
    token_id: &str,
) -> Result<Response<C>, ContractError> {
    let mut token = self.tokens.load(deps.storage, token_id)?;
    // ensure we have permissions
    self.check_can_send(deps.as_ref(), env, info, &token)?;
    // set owner and remove existing approvals
    token.owner = deps.api.addr_validate(recipient)?;
    token.approvals = vec![];

    self.tokens.save(deps.storage, token_id, &token)?;
    Ok(Response::new()
    .add_attribute("action", "_transfer_nft")
    .add_attribute("sender", info.sender.clone())
    .add_attribute("token_id", token_id))
}
```

This oversight allows a malicious buyer to exploit the system through the following steps:  
1. Place a bid on a token with `auto_approve` set to true, gaining approval.
2. Use `send_nft` to transfer the token to their own custom contract that implements `Cw721ReceiveMsg`, bypassing payment.  
3. Cancel their original bid to receive a full refund.  

This exploit effectively allows the attacker to steal the token from the seller without providing any payment to the seller.  

## Proof-of-Concept
The following test demonstrate t
Token owner can burn their token with active rental leading to renters' funds being stuck
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

## Impact
If the property owner calls the `burn` function while active rentals exist, the rental information, including deposits, is deleted. This prevents renters from retrieving their funds through the cancellation process, leading to funds of renters being stucked in the contract.  

## Description
The `burn` function in the contract deletes all data associated with a token, including any active rental information. 

In the CodedEstate, renters must deposit funds in advance for short-term rentals, and this information is stored in a vector, `rentals`, linked to the token.  

The issue arises because the `burn` function only checks whether the caller is the owner or has approval to burn the token. It does not validate whether there are any active rentals associated with the token. As a result, if the property owner calls the `burn` function while rentals are still active, all rental data, including the deposit amounts, is deleted from storage.

Without the rental information, renters can no longer use the cancellation function to retrieve their deposits, as the contract does not retain any record of the rental. This leads to irreversible loss of funds for the renters.  

### Relevant code snippets
```rust
File: contracts/codedestate/src/state.rs
pub struct TokenInfo<T> {
    /// The owner of the newly minted NFT
    pub owner: Addr,
    pub approvals: Vec<Approval>,
    pub longterm_rental: LongTermRental,
    pub shortterm_rental: ShortTermRental,
    pub rentals: Vec<Rental>,  // <-- rental information is stored here
    pub bids: Vec<Bid>,
    pub sell: Sell,
    pub token_uri: Option<String>,
    pub extension: T,
}

File: contracts/codedestate/src/execute.rs
pub fn setlistforshorttermrental(
    //...
    //... function arguments
    //...
) -> Result<Response<C>, ContractError> {
    ...
    ... snipped
    ...
    let traveler = Rental {
        denom:token.shortterm_rental.denom.clone(),
        rental_type:false,
        approved_date:None,
        deposit_amount: Uint128::from(rent_amount),
        renting_period: vec![new_checkin_timestamp, new_checkout_timestamp],
        address: Some(info.sender.clone()),
        approved: token.shortterm_rental.auto_approve,
        cancelled:false,
        guests:guests,
    };

    // token.shortterm_rental.deposit_amount += sent_amount;
    token
        .rentals
        .insert(placetoreserve as usize, traveler); // deposited amount is stored in rentals vector
    ...
    ... snipped
    ...
}

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)?; // <-- Only checks ownership or approval

    self.tokens.remove(deps.storage, &token_id)?;
`finalizelongtermrental()` refunds money to the token owner/ property manager instead of the tenant
Audit: 2024-10-coded-estate-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
`finalizelongtermrental()` sends money to the token owner/ property manager instead of the tenant when the rental reservation has been cancelled. 

## Proof Of Concept 
https://github.com/code-423n4/2024-10-coded-estate/blob/97efb35fd3734676f33598e6dff70119e41c7032/contracts/codedestate/src/execute.rs#L1727-L1731
```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;
                amount = item.deposit_amount;

                if item.cancelled {
                    target = token.owner.to_string();
                    let fee_percentage = self.get_fee(deps.storage)?;
                    self.increase_balance(deps.storage, token.longterm_rental.denom.clone(), Uint128::new((u128::from(amount) * u128::from(fee_percentage)) / 10000))?;

                    amount -= Uint128::new((u128::from(amount) * u128::from(fee_percentage)) / 10000);
                }

                    
                    ....

        if amount > Uint128::new(0) {
            Ok(Response::new()
                .add_attribute("action", "finalizelongtermrental")
                .add_attribute("sender", info.sender)
                .add_attribute("token_id", token_id)
                .add_message(BankMsg::Send {
                    to_address: target.clone(),
                    amount: vec![Coin {
                        denom: token.longterm_rental.denom,
                        amount: amount,
                    }],
                }))            
        } 
```
`finalizelongtermrental()` iterates through the `token.rentals` array to find a specific rental. If this rental was cancelled by the tenant or token owner, the deposit_amount which is the rent should be refunded back to the tenant, the address which provided the rent amount/made reservation in the first place. But in the logic above, if `item.cancelled == true`, target is set to the token owner, the token owner is the manager/landlord of the rental property. This means that even if a reservation is cancelled, the `deposit_amount` still goes to the token owner.  `item.cancelled` is set to true in `cancelreservationafterapprovalforlongterm()`. 

So if tenant makes reservation via `setreservationforlongterm()` and then proceeds to cancel after approval via `cancelreservationafterapprovalforlongterm()`, the deposit_amount which can be rent paid will not be refunded to the tenant when `finalizelongtermrental()` is called. It will go to the token owner. 


The correct logic should be that if reservation item is marked cancelled, deposit amount should b
Malicious NFT owners can rug the reservation of the long-term
Audit: 2024-10-coded-estate-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details


## Description

Due to the long period of the long-term rent, the Homeowner has an advantage in that type of reservation, which is the ability to withdraw a part from the deposited amount by the tenant (This applies only to reservations made more than one month in advance). this could be done by using `execute.rs#withdrawtolandlord()` function.

```rust
if item.deposit_amount - Uint128::from(token.longterm_rental.price_per_month) < Uint128::from(amount)  {
```
The withdrawn amount will be subtracted from the user's `deposit_amount` state. 

```rust
token.rentals[position as usize].deposit_amount -= Uint128::from(amount);
```
On the other side, the NFT owner can trigger `execute.rs#rejectreservationforlongterm()` to reject any reservation at any time even if it currently running. it will send back `.deposit_amount` as a refundable amount to the user.

However, a malicious homeowner can the advantages of `execute.rs#rejectreservationforlongterm()` and `execute.rs#withdrawtolandlord()` to steal a user's funds and reject them in two simple steps 
- Wait for the reservation to start and call `execute.rs#withdrawtolandlord()`. this will transfer most of the funds out.
- Now, invoke `execute.rs#rejectreservationforlongterm()` to kick the user out, this will transfer back to the user only a small presenting of his initial deposit.  

note:
The homeowner has the power to reject any reservation even if it is currently active by triggering `rejectreservationforlongterm()` and refunding user money, but using this function the refundable amount is the same initial deposit 

## Impact

Malicious NFT owners can rug the reservation of the long-term 

## Tools Used

Manual Review

## Recommended Mitigation Steps

Don't allow to reject active reservations. 



## Assessed type

Other
Users will wait untille the last day to cancel reservation to avoid cancellation penalty
Audit: 2024-10-coded-estate-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Description
This protocol gives the right to homeowners to apply a penalty for cancellation after the rent gets approved
The logic is [here](https://github.com/code-423n4/2024-10-coded-estate/blob/main/contracts/codedestate/src/execute.rs#L1080C1-L1086C10) in `execute.rs#cancelreservationafterapprovalforshortterm()` function
```rust
        let diff_days = (check_in_time_timestamp - current_time)/86400;
        for (_i, item) in cancellation.iter().enumerate() {
            if item.deadline < diff_days {
                refundable_amount =  Uint128::new((amount.u128() * u128::from(item.percentage)) / 100);
                break;
            }
        }
```
so the cancellation penalty depends on how many days are left before your rent starts.
 It calculates the `diff_days` by subtracting the check-in time from the current time and dividing by `86400`
 How ever in case the subtracting gives a number less than `86400` the `diff_days` will be zero
 
and this check here ` if item.deadline < diff_days {` will never be true 
So these two lines will set the ` token.rentals[].cancelled` to true and keep the `token.rentals[].deposit_amount`  same as before because the `refundable_amount` is zero
```rust
            token.rentals[position as usize].cancelled = true;
            token.rentals[position as usize].deposit_amount = amount - refundable_amount;
```

## Impact

users will wait until the last day to cancel the reservation and withdraw all the initial deposit even with the existing of cancellation penalty.

## Tools Used

Manual Review

## Recommended Mitigation Steps

```diff
      for (_i, item) in cancellation.iter().enumerate() {
 -           if item.deadline < diff_days {
+            if item.deadline <= diff_days {
                refundable_amount =  Uint128::new((amount.u128() * u128::from(item.percentage)) / 100);
                break;
            }
        }
```



## Assessed type

Other
users can't cancel reservation due to out-of-gas
Audit: 2024-10-coded-estate-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Description
in `execute.rs#cancelreservationafterapprovalforshortterm` and `execute.rs#cancelreservationafterapprovalforlongterm()` , multiple iterations occur over the `cancellation` vector, which may cause the transaction to fail due to an out-of-gas error.

Consequently, Malicious NFT owners could exploit this by setting a big list inside the `cancellation` vector by invoking `execute.rs#setlistforshorttermrental()` or  `execute.rs#setlistforlongtermrental()`  
```rust
    pub fn setlistforlongtermrental(
    /***CODE***/
        cancellation: Vec<CancellationItem>,
    ) -> Result<Response<C>, ContractError> {
/***CODE***/
token.longterm_rental.cancellation = cancellation;
```
to force the cancellation of the reservation to fail due to gas limits,

## Impact

users can't cancel reservation

## Tools Used

Manual Review

## Recommended Mitigation Steps

Set a cap for the length of the `cancellation` vector that owners can set it.



## Assessed type

Other