@@ -567,6 +567,39 @@ async fn snapshot_restore_does_not_reset_session_exec_limit_with_tampered_counte
567567 ) ;
568568}
569569
570+ #[ tokio:: test]
571+ async fn keyed_snapshot_restore_carries_session_exec_budget_forward ( ) {
572+ let key = b"session-budget-hmac-key" ;
573+ let session_limits = SessionLimits :: new ( ) . max_exec_calls ( 2 ) ;
574+ let mut bash = Bash :: builder ( )
575+ . session_limits ( session_limits. clone ( ) )
576+ . build ( ) ;
577+ bash. exec ( "echo first" ) . await . unwrap ( ) ;
578+ let bytes = bash. snapshot_to_bytes_keyed ( key) . unwrap ( ) ;
579+
580+ let mut restored = Bash :: builder ( ) . session_limits ( session_limits) . build ( ) ;
581+ restored. restore_snapshot_keyed ( & bytes, key) . unwrap ( ) ;
582+ assert_eq ! ( restored. session_counters( ) . 1 , 1 ) ;
583+
584+ restored. exec ( "echo second" ) . await . unwrap ( ) ;
585+ let third = restored. exec ( "echo third" ) . await ;
586+ assert ! (
587+ third. is_err( ) ,
588+ "authenticated snapshot resume must not grant a fresh exec-call budget"
589+ ) ;
590+ }
591+
592+ #[ tokio:: test]
593+ async fn from_snapshot_keyed_restores_session_counters ( ) {
594+ let key = b"session-counter-hmac-key" ;
595+ let mut bash = Bash :: new ( ) ;
596+ bash. exec ( "echo first" ) . await . unwrap ( ) ;
597+ let bytes = bash. snapshot_to_bytes_keyed ( key) . unwrap ( ) ;
598+
599+ let restored = Bash :: from_snapshot_keyed ( & bytes, key) . unwrap ( ) ;
600+ assert_eq ! ( restored. session_counters( ) , bash. session_counters( ) ) ;
601+ }
602+
570603#[ tokio:: test]
571604async fn snapshot_restore_rejects_tampered_shell_state_that_exceeds_memory_limits ( ) {
572605 let mut src = Bash :: new ( ) ;
0 commit comments