diff --git a/hipcheck/src/cache/plugin_cache.rs b/hipcheck/src/cache/plugin_cache.rs index 4e3cfd6c..5baaa7bc 100644 --- a/hipcheck/src/cache/plugin_cache.rs +++ b/hipcheck/src/cache/plugin_cache.rs @@ -4,7 +4,7 @@ use std::path::{Path, PathBuf}; use pathbuf::pathbuf; -use crate::plugin::{PluginArch, PluginName, PluginPublisher, PluginVersion}; +use crate::plugin::{PluginName, PluginPublisher, PluginVersion, SupportedArch}; /// Plugins are stored with the following format `////` pub struct HcPluginCache { @@ -23,12 +23,12 @@ impl HcPluginCache { publisher: &PluginPublisher, name: &PluginName, version: &PluginVersion, - arch: &PluginArch, + arch: SupportedArch, ) -> PathBuf { self.path .join(&publisher.0) .join(&name.0) .join(&version.0) - .join(&arch.0) + .join(arch.to_string()) } } diff --git a/hipcheck/src/plugin/download_manifest.rs b/hipcheck/src/plugin/download_manifest.rs index 89d57147..1d684c38 100644 --- a/hipcheck/src/plugin/download_manifest.rs +++ b/hipcheck/src/plugin/download_manifest.rs @@ -1,8 +1,8 @@ -use super::plugin_manifest::PluginArch; use super::{extract_data, PluginName, PluginPublisher, PluginVersion}; use crate::cache::plugin_cache::HcPluginCache; use crate::context::Context; use crate::plugin::retrieval::{download_plugin, extract_plugin}; +use crate::plugin::supported_arch::SupportedArch; use crate::plugin::ParseKdlNode; use crate::string_newtype_parse_kdl_node; use crate::util::http::agent::agent; @@ -217,9 +217,8 @@ pub struct DownloadManifestEntry { /// A `SemVer` version of the plugin. Not a version requirement as in the plugin manifest file, /// but only a specific concrete version pub version: PluginVersion, - // TODO: make this a target-triple enum? /// The target architecture for a plugin - pub arch: String, + pub arch: SupportedArch, /// The URL of the archive file to download containing the plugin executable artifact and /// plugin manifest. pub url: url::Url, @@ -245,7 +244,7 @@ impl ParseKdlNode for DownloadManifestEntry { // Per RFD #0004, version is of type String let version = PluginVersion(node.get("version")?.value().as_string()?.to_string()); // Per RFD #0004, arch is of type String - let arch = node.get("arch")?.value().as_string()?.to_string(); + let arch = SupportedArch::from_str(node.get("arch")?.value().as_string()?).ok()?; // there should be one child for each plugin and it should contain the url, hash, compress // and size information @@ -276,7 +275,7 @@ impl DownloadManifestEntry { publisher: &PluginPublisher, name: &PluginName, version: &PluginVersion, - arch: &PluginArch, + arch: SupportedArch, ) -> Result<(), Error> { // currently plugins are put in HC_CACHE/plugins//// let download_dir = plugin_cache.plugin_download_dir(publisher, name, version, arch); @@ -312,7 +311,7 @@ impl DownloadManifestEntry { publisher.0, name.0, version.0, - arch.0 + arch ) }) } @@ -338,7 +337,7 @@ impl DownloadManifest { publisher: &PluginPublisher, name: &PluginName, version: &PluginVersion, - arch: &PluginArch, + arch: SupportedArch, ) -> Result<(), Error> { self.entries.iter().try_for_each(|entry| { entry.download_and_unpack_plugin(plugin_cache, publisher, name, version, arch) @@ -478,7 +477,7 @@ mod test { let expected_entry = DownloadManifestEntry { version: PluginVersion(version.to_string()), - arch: arch.to_string(), + arch: SupportedArch::from_str(arch).unwrap(), url: Url::parse(url).unwrap(), hash: HashWithDigest::new( HashAlgorithm::try_from(hash_alg).unwrap(), @@ -519,7 +518,7 @@ plugin version="0.1.0" arch="x86_64-apple-darwin" { assert_eq!( &DownloadManifestEntry { version: PluginVersion("0.1.0".to_owned()), - arch: "aarch64-apple-darwin".to_owned(), + arch: SupportedArch::Aarch64AppleDarwin, url: Url::parse("https://github.com/mitre/hipcheck/releases/download/hipcheck-v3.4.0/hipcheck-aarch64-apple-darwin.tar.xz").unwrap(), hash: HashWithDigest::new(HashAlgorithm::Sha256, "b8e111e7817c4a1eb40ed50712d04e15b369546c4748be1aa8893b553f4e756b".to_owned()), compress: Compress::new(ArchiveFormat::TarXz), @@ -532,7 +531,7 @@ plugin version="0.1.0" arch="x86_64-apple-darwin" { assert_eq!( &DownloadManifestEntry { version: PluginVersion("0.1.0".to_owned()), - arch: "x86_64-apple-darwin".to_owned(), + arch: SupportedArch::X86_64AppleDarwin, url: Url::parse("https://github.com/mitre/hipcheck/releases/download/hipcheck-v3.4.0/hipcheck-x86_64-apple-darwin.tar.xz").unwrap(), hash: HashWithDigest::new(HashAlgorithm::Sha256, "ddb8c6d26dd9a91e11c99b3bd7ee2b9585aedac6e6df614190f1ba2bfe86dc19".to_owned()), compress: Compress::new(ArchiveFormat::TarXz), diff --git a/hipcheck/src/plugin/mod.rs b/hipcheck/src/plugin/mod.rs index 642f5231..97e75697 100644 --- a/hipcheck/src/plugin/mod.rs +++ b/hipcheck/src/plugin/mod.rs @@ -3,6 +3,7 @@ mod kdl_parsing; mod manager; mod plugin_manifest; mod retrieval; +mod supported_arch; mod types; pub use crate::plugin::manager::*; @@ -11,7 +12,8 @@ pub use download_manifest::{ ArchiveFormat, DownloadManifest, DownloadManifestEntry, HashAlgorithm, HashWithDigest, }; pub use kdl_parsing::{extract_data, ParseKdlNode}; -pub use plugin_manifest::{PluginArch, PluginManifest, PluginName, PluginPublisher, PluginVersion}; +pub use plugin_manifest::{PluginManifest, PluginName, PluginPublisher, PluginVersion}; +pub use supported_arch::SupportedArch; use crate::Result; use futures::future::join_all; diff --git a/hipcheck/src/plugin/plugin_manifest.rs b/hipcheck/src/plugin/plugin_manifest.rs index 9fd9e734..4c730603 100644 --- a/hipcheck/src/plugin/plugin_manifest.rs +++ b/hipcheck/src/plugin/plugin_manifest.rs @@ -1,3 +1,5 @@ +use super::extract_data; +use crate::plugin::supported_arch::SupportedArch; use crate::plugin::ParseKdlNode; use crate::string_newtype_parse_kdl_node; use crate::{error::Error, hc_error}; @@ -5,10 +7,9 @@ use core::panic; use kdl::{KdlDocument, KdlEntry, KdlNode, KdlValue}; use petgraph::graphmap::NeighborsDirected; use std::collections::HashMap; +use std::fmt::write; use std::{fmt::Display, str::FromStr}; -use super::extract_data; - // NOTE: the implementation in this crate was largely derived from RFD #0004 #[derive(Clone, Debug, PartialEq, Eq)] @@ -27,26 +28,22 @@ string_newtype_parse_kdl_node!(PluginVersion, "version"); pub struct License(pub String); string_newtype_parse_kdl_node!(License, "license"); -// TODO: target-triple enum -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct PluginArch(pub String); - #[derive(Clone, Debug, PartialEq, Eq)] -pub struct Entrypoints(pub HashMap); +pub struct Entrypoints(pub HashMap); impl Entrypoints { pub fn new() -> Self { Self(HashMap::new()) } - pub fn insert(&mut self, arch: PluginArch, entrypoint: String) -> Result<(), Error> { - match self.0.insert(arch.clone(), entrypoint) { - Some(_duplicate_key) => Err(hc_error!("Multiple entrypoints specified for {}", arch.0)), + pub fn insert(&mut self, arch: SupportedArch, entrypoint: String) -> Result<(), Error> { + match self.0.insert(arch, entrypoint) { + Some(_duplicate_key) => Err(hc_error!("Multiple entrypoints specified for {}", arch)), None => Ok(()), } } - pub fn iter(&self) -> impl Iterator { + pub fn iter(&self) -> impl Iterator { self.0.iter() } } @@ -63,13 +60,8 @@ impl ParseKdlNode for Entrypoints { let mut entrypoints = Entrypoints::new(); for entrypoint_spec in node.children()?.nodes() { // per RFD #0004, the value for "arch" is of type String - let arch = PluginArch( - entrypoint_spec - .get("arch")? - .value() - .as_string()? - .to_string(), - ); + let arch = + SupportedArch::from_str(entrypoint_spec.get("arch")?.value().as_string()?).ok()?; // per RFD #0004, the actual entrypoint is the first positional arg after "arch" and is // of type String let entrypoint = entrypoint_spec @@ -78,8 +70,8 @@ impl ParseKdlNode for Entrypoints { .value() .as_string()? .to_string(); - if let Err(e) = entrypoints.insert(arch.clone(), entrypoint) { - log::error!("Duplicate entrypoint detected for [{}]", arch.0); + if let Err(e) = entrypoints.insert(arch, entrypoint) { + log::error!("Duplicate entrypoint detected for [{}]", arch); return None; } } @@ -291,7 +283,7 @@ mod test { let mut expected = Entrypoints::new(); expected .insert( - PluginArch("aarch64-apple-darwin".to_owned()), + SupportedArch::Aarch64AppleDarwin, "./hc-mitre-affiliation".to_owned(), ) .unwrap(); @@ -320,19 +312,19 @@ mod test { let node = KdlNode::from_str(multiple_entrypoint).unwrap(); let mut expected = Entrypoints::new(); expected.insert( - PluginArch("aarch64-apple-darwin".to_owned()), + SupportedArch::Aarch64AppleDarwin, "./hc-mitre-affiliation".to_owned(), ); expected.insert( - PluginArch("x86_64-apple-darwin".to_owned()), + SupportedArch::X86_64AppleDarwin, "./hc-mitre-affiliation".to_owned(), ); expected.insert( - PluginArch("x86_64-unknown-linux-gnu".to_owned()), + SupportedArch::X86_64UnknownLinuxGnu, "./hc-mitre-affiliation".to_owned(), ); expected.insert( - PluginArch("x86_64-pc-windows-msvc".to_owned()), + SupportedArch::X86_64PcWindowsMsvc, "./hc-mitre-affiliation".to_owned(), ); assert_eq!(Entrypoints::parse_node(&node).unwrap(), expected) @@ -412,19 +404,19 @@ dependencies { let mut entrypoints = Entrypoints::new(); entrypoints.insert( - PluginArch("aarch64-apple-darwin".to_owned()), + SupportedArch::Aarch64AppleDarwin, "./hc-mitre-affiliation".to_owned(), ); entrypoints.insert( - PluginArch("x86_64-apple-darwin".to_owned()), + SupportedArch::X86_64AppleDarwin, "./hc-mitre-affiliation".to_owned(), ); entrypoints.insert( - PluginArch("x86_64-unknown-linux-gnu".to_owned()), + SupportedArch::X86_64UnknownLinuxGnu, "./hc-mitre-affiliation".to_owned(), ); entrypoints.insert( - PluginArch("x86_64-pc-windows-msvc".to_owned()), + SupportedArch::X86_64PcWindowsMsvc, "./hc-mitre-affiliation".to_owned(), ); diff --git a/hipcheck/src/plugin/supported_arch.rs b/hipcheck/src/plugin/supported_arch.rs new file mode 100644 index 00000000..9fa35de2 --- /dev/null +++ b/hipcheck/src/plugin/supported_arch.rs @@ -0,0 +1,44 @@ +use std::{fmt::Display, str::FromStr}; + +use crate::hc_error; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +/// Officially supported target triples, as of RFD #0004 +/// +/// NOTE: these architectures correspond to the offically supported Rust platforms +pub enum SupportedArch { + /// Used for macOS running on "Apple Silicon" running on a 64-bit ARM Instruction Set Architecture (ISA) + Aarch64AppleDarwin, + /// Used for macOS running on the Intel 64-bit ISA + X86_64AppleDarwin, + /// Used for Windows running on the Intel 64-bit ISA with the Microsoft Visual Studio Code toolchain for compilation + X86_64PcWindowsMsvc, + /// Used for Linux operating systems running on the Intel 64-bit ISA with a GNU toolchain for compilation + X86_64UnknownLinuxGnu, +} + +impl FromStr for SupportedArch { + type Err = crate::Error; + + fn from_str(s: &str) -> Result { + match s { + "aarch64-apple-darwin" => Ok(Self::Aarch64AppleDarwin), + "x86_64-apple-darwin" => Ok(Self::X86_64AppleDarwin), + "x86_64-pc-windows-msvc" => Ok(Self::X86_64PcWindowsMsvc), + "x86_64-unknown-linux-gnu" => Ok(Self::X86_64UnknownLinuxGnu), + _ => Err(hc_error!("Error parsing arch '{}'", s)), + } + } +} + +impl Display for SupportedArch { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let target_triple = match self { + SupportedArch::Aarch64AppleDarwin => "aarch64-apple-darwin", + SupportedArch::X86_64AppleDarwin => "x86_64-apple-darwin", + SupportedArch::X86_64PcWindowsMsvc => "x86_64-pc-windows-msvc", + SupportedArch::X86_64UnknownLinuxGnu => "x86_64-unknown-linux-gnu", + }; + write!(f, "{}", target_triple) + } +}