@@ -9,7 +9,6 @@ use soar_core::{
99} ;
1010use soar_db:: repository:: metadata:: MetadataRepository ;
1111use soar_dl:: { download:: Download , oci:: OciDownload , types:: OverwriteMode } ;
12- use soar_events:: SoarEvent ;
1312use soar_utils:: hash:: calculate_checksum;
1413use tracing:: debug;
1514
@@ -27,6 +26,7 @@ pub async fn prepare_run(
2726 package_name : & str ,
2827 repo_name : Option < & str > ,
2928 pkg_id : Option < & str > ,
29+ no_verify : bool ,
3030) -> SoarResult < PrepareRunResult > {
3131 debug ! ( package_name = package_name, "preparing run" ) ;
3232 let config = ctx. config ( ) ;
@@ -39,9 +39,6 @@ pub async fn prepare_run(
3939 let version = query. version . as_deref ( ) ;
4040
4141 let output_path = cache_bin. join ( package_name) ;
42- if output_path. exists ( ) {
43- return Ok ( PrepareRunResult :: Ready ( output_path) ) ;
44- }
4542
4643 let metadata_mgr = ctx. metadata_manager ( ) . await ?;
4744
@@ -108,6 +105,34 @@ pub async fn prepare_run(
108105
109106 let package = packages. into_iter ( ) . next ( ) . unwrap ( ) . resolve ( version) ;
110107
108+ // Refuse to execute a package whose integrity cannot be checked. OCI
109+ // artifacts are digest-verified during download, so they are exempt.
110+ if !no_verify && package. bsum . is_none ( ) && package. ghcr_blob . is_none ( ) {
111+ return Err ( SoarError :: Custom ( format ! (
112+ "Refusing to run {}#{}: no checksum to verify integrity (use --no-verify to override)" ,
113+ package. pkg_name, package. pkg_id
114+ ) ) ) ;
115+ }
116+
117+ // Reuse a cached binary only after re-verifying it against the expected
118+ // checksum, so a stale or tampered cache entry is never executed blindly.
119+ if output_path. exists ( ) {
120+ match package. bsum {
121+ Some ( ref bsum) if !no_verify => {
122+ let checksum = calculate_checksum ( & output_path) ?;
123+ if checksum == * bsum {
124+ return Ok ( PrepareRunResult :: Ready ( output_path) ) ;
125+ }
126+ debug ! (
127+ package = %package. pkg_name,
128+ "cached binary checksum mismatch; re-downloading"
129+ ) ;
130+ fs:: remove_file ( & output_path) . ok ( ) ;
131+ }
132+ _ => return Ok ( PrepareRunResult :: Ready ( output_path) ) ,
133+ }
134+ }
135+
111136 fs:: create_dir_all ( & cache_bin)
112137 . with_context ( || format ! ( "creating directory {}" , cache_bin. display( ) ) ) ?;
113138
@@ -119,22 +144,13 @@ pub async fn prepare_run(
119144 package. pkg_id . clone ( ) ,
120145 ) ;
121146
122- download_to_cache ( & package, & output_path, & cache_bin, progress_callback) ?;
123-
124- // Checksum verification
125- let checksum = calculate_checksum ( & output_path) ?;
126- if let Some ( ref bsum) = package. bsum {
127- if checksum != * bsum {
128- ctx. events ( ) . emit ( SoarEvent :: Log {
129- level : soar_events:: LogLevel :: Warning ,
130- message : format ! (
131- "Checksum mismatch for {}: expected {}, got {}" ,
132- package. pkg_name, bsum, checksum
133- ) ,
134- } ) ;
135- return Err ( SoarError :: InvalidChecksum ) ;
136- }
137- }
147+ download_to_cache (
148+ & package,
149+ & output_path,
150+ & cache_bin,
151+ no_verify,
152+ progress_callback,
153+ ) ?;
138154
139155 Ok ( PrepareRunResult :: Ready ( output_path) )
140156}
@@ -157,6 +173,7 @@ fn download_to_cache(
157173 package : & Package ,
158174 output_path : & Path ,
159175 cache_bin : & Path ,
176+ no_verify : bool ,
160177 progress_callback : Arc < dyn Fn ( soar_dl:: types:: Progress ) + Send + Sync > ,
161178) -> SoarResult < ( ) > {
162179 if let Some ( ref url) = package. ghcr_blob {
@@ -176,6 +193,11 @@ fn download_to_cache(
176193 . overwrite ( OverwriteMode :: Force )
177194 . extract ( true )
178195 . extract_to ( & extract_dir) ;
196+ if !no_verify {
197+ if let Some ( ref bsum) = package. bsum {
198+ dl = dl. checksum ( bsum. clone ( ) ) ;
199+ }
200+ }
179201 dl = dl. progress ( move |p| {
180202 cb ( p) ;
181203 } ) ;
0 commit comments