diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 18c5695..99622ce 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -24,6 +24,7 @@ async-trait = "0.1.81" dirs = "5.0.1" libloading = "0.8.5" base64 = "0.22.1" +sha2 = "0.10.8" [dependencies.reqwest] version = "^0.11" diff --git a/lib/src/utils/extension.rs b/lib/src/utils/extension.rs index 701d3da..f8a893c 100644 --- a/lib/src/utils/extension.rs +++ b/lib/src/utils/extension.rs @@ -49,6 +49,8 @@ pub struct Extension { pub version: String, /// The file path to the extension. pub path: String, + /// SHA512 checksum of the extension binary. + pub checksum: String, /// An optional description of the extension. pub description: Option, } diff --git a/lib/src/utils/extension_manager.rs b/lib/src/utils/extension_manager.rs index afdfcb8..422dbbb 100644 --- a/lib/src/utils/extension_manager.rs +++ b/lib/src/utils/extension_manager.rs @@ -42,6 +42,8 @@ use crate::utils::expand_path_with_home_dir; use std::io::Read; use serde_json::Value; use std::collections::HashMap; +use sha2::{Sha512, Digest}; +use std::fs::File; type InstalledExtensions = Vec; @@ -173,7 +175,7 @@ impl ExtensionManager { if extensions_json.extensions.iter().any(|ext| ext.name == extension.name) { warn!("Extension '{}' already exists in the configuration.", extension.name); return Ok(()); - } + } let extension_lib_filename = crate::utils::extract_filename_from_url(extension.path.as_str()).unwrap(); let extension_folder_path = expand_path_with_home_dir(format!(".ga4gh/extensions/{}/", extension.name).as_str()); @@ -186,6 +188,20 @@ impl ExtensionManager { debug!("Downloading extension library from {} to {}", extension.path.as_str(), local_extension_lib_path); crate::utils::download_file(&extension.path, local_extension_lib_path.as_str()).await; + // Calculate the checksum of the downloaded file + let mut file = File::open(&local_extension_lib_path)?; + let mut hasher = Sha512::new(); + let mut buffer = Vec::new(); + file.read_to_end(&mut buffer)?; + hasher.update(&buffer); + let calculated_checksum = format!("{:x}", hasher.finalize()); + + if calculated_checksum != extension.checksum { + error!("Checksum mismatch for extension '{}'.\nExpected: {}\nCalculated: {}", extension.name, extension.checksum, calculated_checksum); + std::fs::remove_dir_all(&extension_folder_path)?; + return Err(Box::new(std::io::Error::new(std::io::ErrorKind::InvalidData, "Checksum mismatch"))); + } + let installed_definition_file_path = expand_path_with_home_dir(format!(".ga4gh/extensions/{}/{}.ga4gh-sdk-extension.json", extension.name, extension.name).as_str()); let full_definition_file_path = fs::canonicalize(&installed_definition_file_path)?.to_str().unwrap().to_string(); let new_extension_record = InstalledExtension {