#37595 [SC-Insight] `require_caller_is_bo_or_tm_or_sp_or_pm` did not emit correct message

Submitted on Dec 10th 2024 at 04:45:22 UTC by @InquisitorScythe for IOP | Fluid Protocol

  • Report ID: #37595

  • Report Type: Smart Contract

  • Report severity: Insight

  • Target: https://github.com/Hydrogen-Labs/fluid-protocol/tree/main/contracts/active-pool-contract/src/main.sw

  • Impacts:

    • Contract fails to deliver promised returns, but doesn't lose value

Description

Brief/Intro

in require_caller_is_bo_or_tm_or_sp_or_pm, if it did not pass thecheck, it will emit a error messge: "Active Pool: Caller is not BorrowOperations, TroveManager, ProtocolManager, or DefaultPool", but it actually perfrom checks on stability pool

Vulnerability Details

in contracts/active-pool-contract/src/main.sw:

#[storage(read)]
fn require_caller_is_bo_or_tm_or_sp_or_pm() {
    let caller = msg_sender().unwrap();
    let borrow_operations_contract = storage.borrow_operations_contract.read();
    let valid_trove_manager = storage.valid_trove_managers.get(caller).try_read().unwrap_or(false);
    let stability_pool_contract = storage.stability_pool_contract.read();
    let protocol_manager_contract = storage.protocol_manager_contract.read();
    require(
        caller == protocol_manager_contract || caller == borrow_operations_contract || valid_trove_manager || caller == stability_pool_contract,
        "Active Pool: Caller is not BorrowOperations, TroveManager, ProtocolManager, or DefaultPool",
    );
}

it perfrom checks on borrow_operation, trove_manager, protocol_manager, stability_pool, but error message states DefaultPool, so the correct message should be Active Pool: Caller is not BorrowOperations, TroveManager, ProtocolManager, or StabilityPool

Impact Details

Deliver wrong message when the checks fails, makes it hard to debug and confuse the users.

References

None

Proof of Concept

Proof of Concept

create test file:

use fuels::{prelude::*, types::Identity};
use test_utils::{
    data_structures::ContractInstance,
    interfaces::{
        active_pool::{active_pool_abi, ActivePool},
        token::{token_abi, Token},
    },
    setup::common::{deploy_active_pool, deploy_default_pool, deploy_token},
};

async fn get_contract_instance1() -> (
    ContractInstance<ActivePool<WalletUnlocked>>,
    Token<WalletUnlocked>,
    WalletUnlocked,
   WalletUnlocked
) {
    // Launch a local network and deploy the contract
    let mut wallets = launch_custom_provider_and_get_wallets(
        WalletsConfig::new(
            Some(2),             /* Single wallet */
            Some(1),             /* Single coin (UTXO) */
            Some(1_000_000_000), /* Amount per coin */
        ),
        None,
        None,
    )
    .await
    .unwrap();
    let wallet = wallets.pop().unwrap();
    let user_wallet = wallets.pop().unwrap();

    let instance = deploy_active_pool(&wallet).await;
    let default_pool = deploy_default_pool(&wallet).await;

    let asset = deploy_token(&wallet).await;

    token_abi::initialize(
        &asset,
        1_000_000_000,
        &Identity::Address(wallet.address().into()),
        "Mock".to_string(),
        "MOCK".to_string(),
    )
    .await
    .unwrap();

    active_pool_abi::initialize(
        &instance,
        Identity::Address(wallet.address().into()),
        Identity::Address(wallet.address().into()),
        default_pool.contract.contract_id().into(),
        Identity::Address(wallet.address().into()),
    )
    .await
    .unwrap();

    active_pool_abi::add_asset(
        &instance,
        asset
            .contract_id()
            .asset_id(&AssetId::zeroed().into())
            .into(),
        Identity::Address(wallet.address().into()),
    )
    .await;

    (instance, asset, wallet, user_wallet)
}

#[tokio::test]
async fn test_error_auth_msg() {
    let (active_pool, mock_fuel, admin, user) = get_contract_instance1().await;

    let tx_params = TxPolicies::