From 59f20f2ef1df4f525b5174a4b29d52b434a0dfdc Mon Sep 17 00:00:00 2001 From: ananas-block Date: Fri, 6 Feb 2026 08:04:32 +0100 Subject: [PATCH 1/3] fix: validate ATA derivation in both idempotent and non-idempotent paths Audit issue #3 (MEDIUM): validate_ata_derivation was only called inside the IDEMPOTENT block, so non-idempotent ATA creation never verified the PDA derivation. Move validation before the IDEMPOTENT check so both code paths enforce correct ATA derivation. --- programs/compressed-token/program/src/ctoken/create_ata.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/programs/compressed-token/program/src/ctoken/create_ata.rs b/programs/compressed-token/program/src/ctoken/create_ata.rs index a1608aa8cf..55b63a4cb2 100644 --- a/programs/compressed-token/program/src/ctoken/create_ata.rs +++ b/programs/compressed-token/program/src/ctoken/create_ata.rs @@ -63,9 +63,11 @@ fn process_create_associated_token_account_with_mode( let mint_bytes = mint.key(); let bump = inputs.bump; + // Always validate ATA derivation (both idempotent and non-idempotent modes) + validate_ata_derivation(associated_token_account, owner_bytes, mint_bytes, bump)?; + // If idempotent mode, check if account already exists if IDEMPOTENT { - validate_ata_derivation(associated_token_account, owner_bytes, mint_bytes, bump)?; if associated_token_account.is_owned_by(&crate::LIGHT_CPI_SIGNER.program_id) { return Ok(()); } From 64f260f4f7507a33eda20d1df64eed097169e974 Mon Sep 17 00:00:00 2001 From: ananas-block Date: Fri, 6 Feb 2026 08:42:39 +0100 Subject: [PATCH 2/3] fix: collapse nested if per clippy collapsible_if --- .../compressed-token/program/src/ctoken/create_ata.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/programs/compressed-token/program/src/ctoken/create_ata.rs b/programs/compressed-token/program/src/ctoken/create_ata.rs index 55b63a4cb2..d56310d996 100644 --- a/programs/compressed-token/program/src/ctoken/create_ata.rs +++ b/programs/compressed-token/program/src/ctoken/create_ata.rs @@ -67,10 +67,10 @@ fn process_create_associated_token_account_with_mode( validate_ata_derivation(associated_token_account, owner_bytes, mint_bytes, bump)?; // If idempotent mode, check if account already exists - if IDEMPOTENT { - if associated_token_account.is_owned_by(&crate::LIGHT_CPI_SIGNER.program_id) { - return Ok(()); - } + if IDEMPOTENT + && associated_token_account.is_owned_by(&crate::LIGHT_CPI_SIGNER.program_id) + { + return Ok(()); } // Check account is owned by system program (uninitialized) From f7fef3edf9f43f8a2449d64d6ea2b78c28dc4e41 Mon Sep 17 00:00:00 2001 From: ananas-block Date: Fri, 6 Feb 2026 17:43:19 +0100 Subject: [PATCH 3/3] fix: fmt and update test assertion for ATA derivation check Run cargo fmt on create_ata.rs and update test_create_ata_failing test 5 to expect error 3 (InvalidAccountData) since validate_ata_derivation now catches wrong bumps before account creation. --- .../tests/light_token/create_ata.rs | 13 +++---------- .../program/src/ctoken/create_ata.rs | 4 +--- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/program-tests/compressed-token-test/tests/light_token/create_ata.rs b/program-tests/compressed-token-test/tests/light_token/create_ata.rs index 78db3428d3..ce6b248b86 100644 --- a/program-tests/compressed-token-test/tests/light_token/create_ata.rs +++ b/program-tests/compressed-token-test/tests/light_token/create_ata.rs @@ -545,16 +545,9 @@ async fn test_create_ata_failing() { .create_and_send_transaction(&[ix], &payer_pubkey, &[&context.payer]) .await; - // Wrong bump can trigger either ProgramFailedToComplete (21) or PrivilegeEscalation (19) - // depending on runtime state - accept either - let is_valid_error = - light_program_test::utils::assert::assert_rpc_error(result.clone(), 0, 21).is_ok() - || light_program_test::utils::assert::assert_rpc_error(result, 0, 19).is_ok(); - - assert!( - is_valid_error, - "Expected either ProgramFailedToComplete (21) or PrivilegeEscalation (19)" - ); + // Wrong bump is now caught by validate_ata_derivation before account creation + // Error: 3 (InvalidAccountData - PDA derivation mismatch) + light_program_test::utils::assert::assert_rpc_error(result, 0, 3).unwrap(); } // Test 6: Invalid config account owner diff --git a/programs/compressed-token/program/src/ctoken/create_ata.rs b/programs/compressed-token/program/src/ctoken/create_ata.rs index d56310d996..ded9d93ba0 100644 --- a/programs/compressed-token/program/src/ctoken/create_ata.rs +++ b/programs/compressed-token/program/src/ctoken/create_ata.rs @@ -67,9 +67,7 @@ fn process_create_associated_token_account_with_mode( validate_ata_derivation(associated_token_account, owner_bytes, mint_bytes, bump)?; // If idempotent mode, check if account already exists - if IDEMPOTENT - && associated_token_account.is_owned_by(&crate::LIGHT_CPI_SIGNER.program_id) - { + if IDEMPOTENT && associated_token_account.is_owned_by(&crate::LIGHT_CPI_SIGNER.program_id) { return Ok(()); }