From c114154f9ea7ae0634f99c93cebbe848b4bbb20d Mon Sep 17 00:00:00 2001 From: emwulrd Date: Wed, 21 Oct 2026 00:47:05 +0100 Subject: [PATCH] fix: validate cancellation reason, coverage limit boundary, split bps bounds, and oracle stake Closes #724 Closes #723 Closes #722 Closes #721 --- contracts/claims-processor/src/lib.rs | 3 ++- contracts/oracle-verifier/src/lib.rs | 7 +++---- contracts/policy-engine/src/lib.rs | 5 ++++- contracts/risk-pool/src/lib.rs | 4 ++-- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/contracts/claims-processor/src/lib.rs b/contracts/claims-processor/src/lib.rs index df0deb6..b75c33b 100644 --- a/contracts/claims-processor/src/lib.rs +++ b/contracts/claims-processor/src/lib.rs @@ -2157,7 +2157,8 @@ impl ClaimsProcessor { .unwrap_or_else(|| panic_with_error!(&env, Error::ClaimNotFound)); // Re-check the cap at release time: the payout may never exceed the - // policy's coverage amount (issue #550). + // policy's coverage amount (issue #550, #723). + // Accepts paid_amount == claim.coverage_amount; rejects strictly if paid_amount > claim.coverage_amount. if paid_amount > claim.coverage_amount { panic_with_error!(&env, Error::PayoutExceedsCoverage); } diff --git a/contracts/oracle-verifier/src/lib.rs b/contracts/oracle-verifier/src/lib.rs index 415b04d..d13b76b 100644 --- a/contracts/oracle-verifier/src/lib.rs +++ b/contracts/oracle-verifier/src/lib.rs @@ -347,9 +347,8 @@ impl OracleVerifier { panic_with_error!(&env, Error::TooManyOracles); } - // Enforce a minimum economic stake so oracles have skin in the game. - // Disabled by default (min_stake == 0) for backward compatibility with - // deployments/tests that don't use the staking feature. + // Enforce a minimum economic stake so oracles have skin in the game (issue #721). + // Reject zero-stake registration when minimum stake is enforced. let min_stake: i128 = env .storage() .instance() @@ -361,7 +360,7 @@ impl OracleVerifier { .persistent() .get(&StorageKey::OracleStakeAmt(data_type.clone(), oracle.clone())) .unwrap_or(0); - if staked < min_stake { + if staked <= 0 || staked < min_stake { panic_with_error!(&env, Error::InsufficientStake); } } diff --git a/contracts/policy-engine/src/lib.rs b/contracts/policy-engine/src/lib.rs index bbdb973..cdbec11 100644 --- a/contracts/policy-engine/src/lib.rs +++ b/contracts/policy-engine/src/lib.rs @@ -1084,7 +1084,10 @@ impl PolicyEngine { pub fn cancel_policy(env: Env, policyholder: Address, policy_id: u128, reason: soroban_sdk::Bytes) -> i128 { policyholder.require_auth(); - // Validate reason length to prevent storage exhaustion from extremely long reasons + // Validate reason length to prevent storage exhaustion from extremely long reasons (issue #724) + if reason.is_empty() { + panic_with_error!(&env, Error::InvalidInput); + } const MAX_REASON_LENGTH: u32 = 256; if reason.len() > MAX_REASON_LENGTH { panic_with_error!(&env, Error::ReasonTooLong); diff --git a/contracts/risk-pool/src/lib.rs b/contracts/risk-pool/src/lib.rs index 756fd91..ba8fc19 100644 --- a/contracts/risk-pool/src/lib.rs +++ b/contracts/risk-pool/src/lib.rs @@ -2108,7 +2108,7 @@ impl RiskPool { /// passed/executed proposal — a real design decision, not made here. pub fn update_premium_split(env: Env, admin: Address, lp_bps: i128, treas_bps: i128, backstop_bps: i128) { Self::require_admin(&env, &admin); - if lp_bps < 0 || treas_bps < 0 || backstop_bps < 0 { + if lp_bps < 0 || lp_bps > 10_000 || treas_bps < 0 || treas_bps > 10_000 || backstop_bps < 0 || backstop_bps > 10_000 { panic_with_error!(&env, Error::InvalidSplit); } if lp_bps + treas_bps + backstop_bps != 10_000 { @@ -2132,7 +2132,7 @@ impl RiskPool { backstop_bps: i128, ) { Self::require_admin(&env, &admin); - if lp_bps < 0 || treas_bps < 0 || backstop_bps < 0 { + if lp_bps < 0 || lp_bps > 10_000 || treas_bps < 0 || treas_bps > 10_000 || backstop_bps < 0 || backstop_bps > 10_000 { panic_with_error!(&env, Error::InvalidSplit); } if lp_bps + treas_bps + backstop_bps != 10_000 {