From b6e576a49806d07ca72d0a16743d50f08d3fc5fc Mon Sep 17 00:00:00 2001 From: justprotocol Date: Thu, 16 Jul 2026 09:24:59 +0100 Subject: [PATCH 1/4] feat: enforce maximum Merkle proof length in claim_with_proof to prevent CPU DoS --- app/onchain/contracts/aid_escrow/src/lib.rs | 8 +++++ .../aid_escrow/tests/aid_escrow_tests.rs | 29 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/app/onchain/contracts/aid_escrow/src/lib.rs b/app/onchain/contracts/aid_escrow/src/lib.rs index 846bf863..0628c8eb 100644 --- a/app/onchain/contracts/aid_escrow/src/lib.rs +++ b/app/onchain/contracts/aid_escrow/src/lib.rs @@ -108,6 +108,7 @@ pub enum Error { TokenTransferFailed = 18, // Merkle allowlist root has expired (merkle_root_expires_at <= now) AllowlistExpired = 19, + ProofTooLarge = 19, } // --- Contract Events (indexer-friendly; stable topics & payloads) --- @@ -807,6 +808,13 @@ impl AidEscrow { proof: Vec, ) -> Result<(), Error> { Self::check_action_paused(&env, symbol_short!("claim"))?; + + // --- ENFORCE MERKLE PROOF CAP --- + if proof.len() > 32 { + return Err(Error::ProofTooLarge); + } + // --------------------------------- + let key = (symbol_short!("pkg"), id); let mut package: Package = env .storage() diff --git a/app/onchain/contracts/aid_escrow/tests/aid_escrow_tests.rs b/app/onchain/contracts/aid_escrow/tests/aid_escrow_tests.rs index 591395d3..3f02d02b 100644 --- a/app/onchain/contracts/aid_escrow/tests/aid_escrow_tests.rs +++ b/app/onchain/contracts/aid_escrow/tests/aid_escrow_tests.rs @@ -643,3 +643,32 @@ mod token_decimal_normalization { assert!(result.is_ok()); } } +#[test] +fn test_claim_with_proof_oversized_fails() { + let env = Env::default(); + env.mock_all_auths(); + + // 1. Generate an oversized proof vector containing 33 elements (limit is 32) + let mut oversized_proof = Vec::new(&env); + for _ in 0..33 { + oversized_proof.push_back(String::from_str(&env, "a")); + } + + // 2. Instantiate dummy variables for the invocation + let dummy_id = 1u64; + let dummy_claimant = Address::generate(&env); + + // 3. Setup your contract client (replace AidEscrowClient with your exact contract client name) + // Note: If AidEscrowClient has a different constructor in this test file, match its existing pattern. + let contract_id = env.register_contract(None, crate::AidEscrow); + let client = AidEscrowClient::new(&env, &contract_id); + + // 4. Try the claim invocation and assert it rejects with our custom error + let result = client.try_claim_with_proof( + &dummy_id, + &dummy_claimant, + &oversized_proof, + ); + + assert_eq!(result, Err(Ok(crate::Error::ProofTooLarge))); +} \ No newline at end of file From fbd09536a2ca5c84b79447878eac1326e431f073 Mon Sep 17 00:00:00 2001 From: justprotocol Date: Thu, 16 Jul 2026 09:40:03 +0100 Subject: [PATCH 2/4] style: run cargo fmt to fix CI formatting checks --- app/onchain/contracts/aid_escrow/src/lib.rs | 2 +- .../contracts/aid_escrow/tests/aid_escrow_tests.rs | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/app/onchain/contracts/aid_escrow/src/lib.rs b/app/onchain/contracts/aid_escrow/src/lib.rs index 0628c8eb..426038dd 100644 --- a/app/onchain/contracts/aid_escrow/src/lib.rs +++ b/app/onchain/contracts/aid_escrow/src/lib.rs @@ -814,7 +814,7 @@ impl AidEscrow { return Err(Error::ProofTooLarge); } // --------------------------------- - + let key = (symbol_short!("pkg"), id); let mut package: Package = env .storage() diff --git a/app/onchain/contracts/aid_escrow/tests/aid_escrow_tests.rs b/app/onchain/contracts/aid_escrow/tests/aid_escrow_tests.rs index 3f02d02b..2f93ede1 100644 --- a/app/onchain/contracts/aid_escrow/tests/aid_escrow_tests.rs +++ b/app/onchain/contracts/aid_escrow/tests/aid_escrow_tests.rs @@ -664,11 +664,7 @@ fn test_claim_with_proof_oversized_fails() { let client = AidEscrowClient::new(&env, &contract_id); // 4. Try the claim invocation and assert it rejects with our custom error - let result = client.try_claim_with_proof( - &dummy_id, - &dummy_claimant, - &oversized_proof, - ); + let result = client.try_claim_with_proof(&dummy_id, &dummy_claimant, &oversized_proof); assert_eq!(result, Err(Ok(crate::Error::ProofTooLarge))); -} \ No newline at end of file +} From f4d8dd26703e374b7fd2b124882badc32e55d288 Mon Sep 17 00:00:00 2001 From: justprotocol Date: Thu, 16 Jul 2026 09:58:29 +0100 Subject: [PATCH 3/4] test: fix Soroban SDK types and resolve register_contract deprecation --- app/onchain/contracts/aid_escrow/tests/aid_escrow_tests.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/onchain/contracts/aid_escrow/tests/aid_escrow_tests.rs b/app/onchain/contracts/aid_escrow/tests/aid_escrow_tests.rs index 2f93ede1..e7b29212 100644 --- a/app/onchain/contracts/aid_escrow/tests/aid_escrow_tests.rs +++ b/app/onchain/contracts/aid_escrow/tests/aid_escrow_tests.rs @@ -651,16 +651,15 @@ fn test_claim_with_proof_oversized_fails() { // 1. Generate an oversized proof vector containing 33 elements (limit is 32) let mut oversized_proof = Vec::new(&env); for _ in 0..33 { - oversized_proof.push_back(String::from_str(&env, "a")); + oversized_proof.push_back(soroban_sdk::String::from_str(&env, "a")); } // 2. Instantiate dummy variables for the invocation let dummy_id = 1u64; let dummy_claimant = Address::generate(&env); - // 3. Setup your contract client (replace AidEscrowClient with your exact contract client name) - // Note: If AidEscrowClient has a different constructor in this test file, match its existing pattern. - let contract_id = env.register_contract(None, crate::AidEscrow); + // 3. Register the contract using the non-deprecated `.register` method + let contract_id = env.register(crate::AidEscrow, ()); let client = AidEscrowClient::new(&env, &contract_id); // 4. Try the claim invocation and assert it rejects with our custom error From d08b6b42180b7df9bacc4efd8e82d86793a12a8b Mon Sep 17 00:00:00 2001 From: justprotocol Date: Fri, 17 Jul 2026 19:01:36 +0100 Subject: [PATCH 4/4] fix: update ProofTooLarge error discriminant to 20 --- app/onchain/contracts/aid_escrow/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/onchain/contracts/aid_escrow/src/lib.rs b/app/onchain/contracts/aid_escrow/src/lib.rs index 426038dd..6437c0dc 100644 --- a/app/onchain/contracts/aid_escrow/src/lib.rs +++ b/app/onchain/contracts/aid_escrow/src/lib.rs @@ -108,7 +108,7 @@ pub enum Error { TokenTransferFailed = 18, // Merkle allowlist root has expired (merkle_root_expires_at <= now) AllowlistExpired = 19, - ProofTooLarge = 19, + ProofTooLarge = 20, } // --- Contract Events (indexer-friendly; stable topics & payloads) ---