From 29df087f9c77c8325a059757fa5deaf4adec8407 Mon Sep 17 00:00:00 2001 From: alberto-crossmint Date: Thu, 16 Apr 2026 23:09:11 +0200 Subject: [PATCH] fix: reject limit == i128::MAX in TokenTransferPolicy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit extract_transfer_total and check_spending_limit both use checked_add(...).unwrap_or(i128::MAX) — saturating on overflow. That is safe as long as the configured limit is strictly less than i128::MAX, because the subsequent `new_total > limit` check rejects overflowed values correctly. But `limit == i128::MAX` degenerates into unlimited spending: `i128::MAX > i128::MAX` is false, so every spend is approved and the tracker saturates and stays saturated. validate_policy already rejects `limit <= 0`; extend it to also reject `limit == i128::MAX`. Same error code (InvalidPolicy) since the surface to integrators is unchanged: pass a sensible positive bound. --- .../src/auth/policy/token_transfer.rs | 6 +++++- .../src/tests/token_transfer_policy_test.rs | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/contracts/smart-account/src/auth/policy/token_transfer.rs b/contracts/smart-account/src/auth/policy/token_transfer.rs index 5322ee3..2da7be4 100644 --- a/contracts/smart-account/src/auth/policy/token_transfer.rs +++ b/contracts/smart-account/src/auth/policy/token_transfer.rs @@ -179,7 +179,11 @@ impl AuthorizationCheck for TokenTransferPolicy { fn validate_policy(policy: &TokenTransferPolicy, env: &Env) -> Result<(), SmartAccountError> { if let Some(limit) = policy.limit { - if limit <= 0 { + // `<= 0` rejects zero and negative limits. `== i128::MAX` is also + // rejected: `extract_transfer_total` and `check_spending_limit` + // saturate on overflow, so `limit == i128::MAX` would degenerate + // into unlimited spending (`new_total > i128::MAX` is never true). + if limit <= 0 || limit == i128::MAX { return Err(SmartAccountError::InvalidPolicy); } } diff --git a/contracts/smart-account/src/tests/token_transfer_policy_test.rs b/contracts/smart-account/src/tests/token_transfer_policy_test.rs index a478b81..57d4baf 100644 --- a/contracts/smart-account/src/tests/token_transfer_policy_test.rs +++ b/contracts/smart-account/src/tests/token_transfer_policy_test.rs @@ -928,3 +928,19 @@ fn test_updating_allowed_recipients_preserves_spending_tracker() { let contexts = vec![&env, make_transfer_context(&env, &token, &allowed_2, 300)]; check_auth(&env, &contract_id, &signer, &contexts).unwrap(); } + +// ============================================================================ +// validate_policy must reject `limit = Some(i128::MAX)`. +// With saturating-adds in extract_transfer_total / check_spending_limit, +// a MAX limit silently degenerates into unlimited spending. +// ============================================================================ + +#[test] +#[should_panic(expected = "#80")] +fn test_on_add_rejects_i128_max_limit() { + let env = setup(); + let token = Address::generate(&env); + let mut policy = make_policy(&env, &token, Some(1000)); + policy.limit = Some(i128::MAX); + let _ = setup_account_with_policy(&env, &policy); +}