From 1d0c93e9cfe3d9c72fed75f8aab452c420f4b7ab Mon Sep 17 00:00:00 2001 From: moudyellaz Date: Thu, 2 Apr 2026 22:33:34 +0200 Subject: [PATCH] test: verify malicious self_program_id is rejected in public execution --- nssa/src/program.rs | 7 +++++ nssa/src/state.rs | 27 ++++++++++++++++ .../src/bin/malicious_self_program_id.rs | 31 +++++++++++++++++++ 3 files changed, 65 insertions(+) create mode 100644 test_program_methods/guest/src/bin/malicious_self_program_id.rs diff --git a/nssa/src/program.rs b/nssa/src/program.rs index 4622b9ef..27badcf9 100644 --- a/nssa/src/program.rs +++ b/nssa/src/program.rs @@ -338,6 +338,13 @@ mod tests { Self::new(FLASH_SWAP_CALLBACK_ELF.to_vec()) .expect("flash_swap_callback must be a valid Risc0 program") } + + #[must_use] + pub fn malicious_self_program_id() -> Self { + use test_program_methods::MALICIOUS_SELF_PROGRAM_ID_ELF; + Self::new(MALICIOUS_SELF_PROGRAM_ID_ELF.to_vec()) + .expect("malicious_self_program_id must be a valid Risc0 program") + } } #[test] diff --git a/nssa/src/state.rs b/nssa/src/state.rs index aa41e856..140420dd 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -384,6 +384,7 @@ pub mod tests { self.insert_program(Program::validity_window()); self.insert_program(Program::flash_swap_initiator()); self.insert_program(Program::flash_swap_callback()); + self.insert_program(Program::malicious_self_program_id()); self } @@ -3714,4 +3715,30 @@ pub mod tests { "standalone InvariantCheck should be rejected (caller_program_id is None)" ); } + + #[test] + fn malicious_self_program_id_rejected_in_public_execution() { + let program = Program::malicious_self_program_id(); + let acc_id = AccountId::from(program.id()); + let account = Account::default(); + + let mut state = V03State::new_with_genesis_accounts(&[], &[]).with_test_programs(); + state.force_insert_account(acc_id, account); + + let message = public_transaction::Message::try_new( + program.id(), + vec![acc_id], + vec![], + (), + ) + .unwrap(); + let witness_set = public_transaction::WitnessSet::for_message(&message, &[]); + let tx = PublicTransaction::new(message, witness_set); + + let result = state.transition_from_public_transaction(&tx, 1, 0); + assert!( + result.is_err(), + "program with wrong self_program_id in output should be rejected" + ); + } } diff --git a/test_program_methods/guest/src/bin/malicious_self_program_id.rs b/test_program_methods/guest/src/bin/malicious_self_program_id.rs new file mode 100644 index 00000000..00d0b4db --- /dev/null +++ b/test_program_methods/guest/src/bin/malicious_self_program_id.rs @@ -0,0 +1,31 @@ +use nssa_core::program::{ + AccountPostState, DEFAULT_PROGRAM_ID, ProgramInput, ProgramOutput, read_nssa_inputs, +}; + +type Instruction = (); + +fn main() { + let ( + ProgramInput { + self_program_id: _, // ignore the correct ID + caller_program_id: _, + pre_states, + instruction: (), + }, + instruction_words, + ) = read_nssa_inputs::(); + + let post_states = pre_states + .iter() + .map(|a| AccountPostState::new(a.account.clone())) + .collect(); + + // Deliberately output wrong self_program_id + ProgramOutput::new( + DEFAULT_PROGRAM_ID, // WRONG: should be self_program_id + instruction_words, + pre_states, + post_states, + ) + .write(); +}