diff --git a/contracts/claims-processor/src/lib.rs b/contracts/claims-processor/src/lib.rs index df0deb6..5797672 100644 --- a/contracts/claims-processor/src/lib.rs +++ b/contracts/claims-processor/src/lib.rs @@ -496,6 +496,10 @@ impl ClaimsProcessor { if policy.policyholder != *claimant { panic_with_error!(env, Error::Unauthorized); } + // Guard: reject claims for cancelled policies (issue #731). + if policy.status == parashield_policy_engine::PolicyStatus::Cancelled { + panic_with_error!(env, Error::PolicyNotActive); + } if policy.status != parashield_policy_engine::PolicyStatus::Active { panic_with_error!(env, Error::PolicyNotActive); } diff --git a/contracts/governance-dao/src/lib.rs b/contracts/governance-dao/src/lib.rs index c5858f0..79623a7 100644 --- a/contracts/governance-dao/src/lib.rs +++ b/contracts/governance-dao/src/lib.rs @@ -324,11 +324,10 @@ impl GovernanceDao { panic_with_error!(&env, Error::TitleTooLong); } - // SECURITY FIX: Validate impact analysis length to prevent storage exhaustion. - // Limit to 4096 bytes — reasonable for a proposal description without enabling - // attacks that reserve huge storage or inflate transaction costs. - if impact_analysis.is_empty() { - panic_with_error!(&env, Error::InvalidInput); + // SECURITY FIX: Validate proposal description (impact_analysis) is not empty (issue #732). + // Enforce non-empty and minimum length to ensure informative proposals, bounded up to 4096 bytes. + if impact_analysis.is_empty() || impact_analysis.len() < 5 { + panic_with_error!(&env, Error::EmptyDescription); } if impact_analysis.len() > 4096 { panic_with_error!(&env, Error::InvalidInput); @@ -1872,6 +1871,10 @@ impl GovernanceDao { ) { Self::require_admin(&env, &admin); + if description.is_empty() { + panic_with_error!(&env, Error::EmptyDescription); + } + if env.storage().instance().has(&StorageKey::Template(name.clone())) { panic_with_error!(&env, Error::DuplicateTemplateName); } diff --git a/contracts/policy-engine/src/lib.rs b/contracts/policy-engine/src/lib.rs index bbdb973..b88abdb 100644 --- a/contracts/policy-engine/src/lib.rs +++ b/contracts/policy-engine/src/lib.rs @@ -218,6 +218,8 @@ impl PolicyEngine { if oracle_buf[0] != b'C' { panic_with_error!(&env, Error::InvalidAddress); } + // Reject zero address as oracle (issue #730) + Self::require_not_zero_address(&env, &oracle_address); let balance_res = env.try_invoke_contract::( &usdc_token, @@ -367,6 +369,8 @@ impl PolicyEngine { if oracle_buf[0] != b'C' { panic_with_error!(&env, Error::InvalidAddress); } + // Reject zero address as oracle (issue #730) + Self::require_not_zero_address(&env, &new_oracle); let change = PendingOracleChange { new_oracle: new_oracle.clone(), @@ -2004,6 +2008,17 @@ impl PolicyEngine { } fn migrate_v2_to_v3(_env: &Env) {} + + /// Require that an address is not the zero address (issue #730). + fn require_not_zero_address(env: &Env, address: &Address) { + let zero = Address::from_string(&soroban_sdk::String::from_str( + env, + "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF", + )); + if *address == zero { + panic_with_error!(env, Error::InvalidAddress); + } + } } #[cfg(test)] diff --git a/contracts/risk-pool/src/lib.rs b/contracts/risk-pool/src/lib.rs index 756fd91..aba76fd 100644 --- a/contracts/risk-pool/src/lib.rs +++ b/contracts/risk-pool/src/lib.rs @@ -281,8 +281,8 @@ impl RiskPool { if env.storage().instance().has(&StorageKey::Initialized) { panic_with_error!(&env, Error::AlreadyInitialized); } - // An empty category would make the pool's accounting and LP NFT - // metadata ambiguous, so reject it up front (issue #520). + // Category name cannot be empty and must describe a valid risk category (issue #729, #520). + // Empty category names are rejected to prevent unnamed categories in the pool registry. if category == Symbol::new(&env, "") { panic_with_error!(&env, Error::InvalidCategory); }