File tree Expand file tree Collapse file tree
rust/main/chains/hyperlane-sealevel/src Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -342,14 +342,11 @@ impl SealevelMailbox {
342342 let account_metas = self . get_account_metas ( instruction) . await ?;
343343
344344 // Ensure dynamically provided account metas are safe to prevent theft from the payer.
345- // The identity signer (if configured and distinct from payer) is allowed through as a
346- // trusted co-signer (e.g. required by TrustedRelayer ISMs).
347- let identity = self . get_signer_if_separate ( ) . map ( |s| s. pubkey ( ) ) ;
348- sanitize_dynamic_accounts (
349- account_metas,
350- & self . get_payer ( ) ?. pubkey ( ) ,
351- identity. as_ref ( ) ,
352- )
345+ // Identity is NOT passed here: neither the ISM-getter nor handle paths should trust
346+ // an external program to request the relayer's identity as a signer. Only the ISM
347+ // verify fixpoint loop (get_ism_verify_account_metas) passes identity, after it has
348+ // already converged on a stable account set from a known ISM program.
349+ sanitize_dynamic_accounts ( account_metas, & self . get_payer ( ) ?. pubkey ( ) , None )
353350 }
354351
355352 async fn get_process_payload (
Original file line number Diff line number Diff line change @@ -129,4 +129,28 @@ mod test {
129129 assert ! ( !result[ 0 ] . is_signer) ;
130130 assert ! ( result[ 1 ] . is_signer) ;
131131 }
132+
133+ /// Regression test: ISM-getter and handle paths pass `None` as identity, so a malicious
134+ /// recipient program cannot trick the relayer into co-signing with its identity keypair
135+ /// by returning it with `is_signer: true` in the account metas simulation.
136+ #[ test]
137+ fn test_sanitize_dynamic_accounts_strips_identity_signer_when_none ( ) {
138+ use solana_sdk:: instruction:: AccountMeta ;
139+
140+ let payer = Pubkey :: new_unique ( ) ;
141+ let identity = Pubkey :: new_unique ( ) ;
142+
143+ // Simulate a malicious recipient returning the relayer's identity as a signer.
144+ let account_metas = vec ! [
145+ AccountMeta :: new_readonly( [ 0u8 ; 32 ] . into( ) , false ) ,
146+ AccountMeta :: new_readonly( identity, true ) ,
147+ ] ;
148+
149+ // `None` identity — as used by get_non_signer_account_metas_with_instruction_bytes.
150+ let result = sanitize_dynamic_accounts ( account_metas, & payer, None ) . unwrap ( ) ;
151+
152+ // Both accounts must have is_signer stripped, including the identity key.
153+ assert ! ( !result[ 0 ] . is_signer) ;
154+ assert ! ( !result[ 1 ] . is_signer) ;
155+ }
132156}
You can’t perform that action at this time.
0 commit comments