Skip to content

Commit

Permalink
feat: allow use of unknown arches through commandline
Browse files Browse the repository at this point in the history
  • Loading branch information
j-lanson committed Sep 23, 2024
1 parent 7f5351d commit d0197bf
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 68 deletions.
10 changes: 10 additions & 0 deletions hipcheck/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,18 @@ use pathbuf::pathbuf;
use tonic_build::compile_protos;

fn main() -> Result<()> {
// Compile the Hipcheck gRPC protocol spec to an .rs file
let root = env!("CARGO_MANIFEST_DIR");
let path = pathbuf![root, "proto", "hipcheck", "v1", "hipcheck.proto"];
compile_protos(path)?;

// Make the target available as a compile-time env var for plugin arch
// resolution
println!(
"cargo:rustc-env=TARGET={}",
std::env::var("TARGET").unwrap()
);
println!("cargo:rerun-if-changed-env=TARGET");

Ok(())
}
11 changes: 7 additions & 4 deletions hipcheck/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
error::Context,
error::Result,
hc_error,
plugin::SupportedArch,
plugin::Arch,
session::pm,
shell::{color_choice::ColorChoice, verbosity::Verbosity},
source,
Expand All @@ -19,7 +19,10 @@ use crate::{
use clap::{Parser as _, ValueEnum};
use hipcheck_macros as hc;
use pathbuf::pathbuf;
use std::path::{Path, PathBuf};
use std::{
path::{Path, PathBuf},
str::FromStr,
};
use url::Url;

/// Automatated supply chain risk assessment of software packages.
Expand Down Expand Up @@ -417,8 +420,8 @@ pub struct CheckArgs {
#[clap(subcommand)]
command: Option<CheckCommand>,

#[arg(long = "arch")]
pub arch: Option<SupportedArch>,
#[arg(long = "arch", value_parser = Arch::from_str)]
pub arch: Option<Arch>,

#[arg(short = 't', long = "target")]
pub target_type: Option<TargetType>,
Expand Down
9 changes: 5 additions & 4 deletions hipcheck/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use crate::{
cache::plugin::HcPluginCache,
hc_error,
plugin::{
get_plugin_key, retrieve_plugins, try_get_current_arch, Plugin, PluginManifest,
PluginResponse, QueryResult,
get_current_arch, get_plugin_key, retrieve_plugins, Plugin, PluginManifest, PluginResponse,
QueryResult,
},
policy::PolicyFile,
util::fs::{find_file_by_name, read_string},
Expand Down Expand Up @@ -230,7 +230,8 @@ pub fn start_plugins(
/* jitter_percent */ 10,
)?;

let current_arch = try_get_current_arch()?;
let current_arch = get_current_arch();
println!("CURRENT ARCH: {}", current_arch);

// retrieve, verify and extract all required plugins
let required_plugin_names = retrieve_plugins(&policy_file.plugins.0, plugin_cache)?;
Expand All @@ -248,7 +249,7 @@ pub fn start_plugins(
let contents = read_string(&plugin_kdl)?;
let plugin_manifest = PluginManifest::from_str(contents.as_str())?;
let entrypoint = plugin_manifest
.get_entrypoint(current_arch)
.get_entrypoint(&current_arch)
.ok_or_else(|| {
hc_error!(
"Could not find {} entrypoint for {}/{} {}",
Expand Down
2 changes: 1 addition & 1 deletion hipcheck/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ fn main() -> ExitCode {
/// Run the `check` command.
fn cmd_check(args: &CheckArgs, config: &CliConfig) -> ExitCode {
// Before we do any analysis, set the user-provided arch
if let Some(arch) = args.arch {
if let Some(arch) = &args.arch {
if let Err(e) = try_set_arch(arch) {
Shell::print_error(&e, Format::Human);
return ExitCode::FAILURE;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::{fmt::Display, result::Result as StdResult, str::FromStr, sync::OnceLoc
/// Officially supported target triples, as of RFD #0004
///
/// NOTE: these architectures correspond to the offically supported Rust platforms
pub enum SupportedArch {
pub enum KnownArch {
/// 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
Expand All @@ -18,22 +18,34 @@ pub enum SupportedArch {
X86_64PcWindowsMsvc,
/// Used for Linux operating systems running on the Intel 64-bit ISA with a GNU toolchain for compilation
X86_64UnknownLinuxGnu,
/// Used for Linux operating systems running on a 64-bit ARM ISA
Aarch64UnknownLinuxGnu,
}

pub const DETECTED_ARCH: Option<SupportedArch> = {
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Arch {
Known(KnownArch),
Unknown(String),
}

pub const DETECTED_ARCH_STR: &str = env!("TARGET");

pub const DETECTED_ARCH: Option<KnownArch> = {
if cfg!(target_arch = "x86_64") {
if cfg!(target_os = "macos") {
Some(SupportedArch::X86_64AppleDarwin)
Some(KnownArch::X86_64AppleDarwin)
} else if cfg!(target_os = "linux") {
Some(SupportedArch::X86_64UnknownLinuxGnu)
Some(KnownArch::X86_64UnknownLinuxGnu)
} else if cfg!(target_os = "windows") {
Some(SupportedArch::X86_64PcWindowsMsvc)
Some(KnownArch::X86_64PcWindowsMsvc)
} else {
None
}
} else if cfg!(target_arch = "aarch64") {
if cfg!(target_os = "macos") {
Some(SupportedArch::Aarch64AppleDarwin)
Some(KnownArch::Aarch64AppleDarwin)
} else if cfg!(target_os = "linux") {
Some(KnownArch::Aarch64UnknownLinuxGnu)
} else {
None
}
Expand All @@ -42,34 +54,24 @@ pub const DETECTED_ARCH: Option<SupportedArch> = {
}
};

pub static USER_PROVIDED_ARCH: OnceLock<SupportedArch> = OnceLock::new();
pub static USER_PROVIDED_ARCH: OnceLock<Arch> = OnceLock::new();

/// Get the target architecture for plugins. If the user provided a target,
/// return that. Otherwise, if the `hc` binary was compiled for a supported
/// architecture, return that. Otherwise return None.
pub fn get_current_arch() -> Option<SupportedArch> {
pub fn get_current_arch() -> Arch {
if let Some(arch) = USER_PROVIDED_ARCH.get() {
Some(*arch)
} else if DETECTED_ARCH.is_some() {
DETECTED_ARCH
arch.clone()
} else if let Some(known_arch) = DETECTED_ARCH {
Arch::Known(known_arch)
} else {
None
Arch::Unknown(DETECTED_ARCH_STR.to_owned())
}
}

/// Like `get_current_arch()`, but returns an error message suggesting the
/// user specifies a target on the CLI
pub fn try_get_current_arch() -> Result<SupportedArch> {
if let Some(arch) = get_current_arch() {
Ok(arch)
} else {
Err(hc_error!("Could not resolve the current machine to one of the Hipcheck supported architectures. Please specify --arch on the commandline."))
}
}

pub fn try_set_arch(arch: SupportedArch) -> Result<()> {
let set_arch = USER_PROVIDED_ARCH.get_or_init(|| arch);
if *set_arch == arch {
pub fn try_set_arch(arch: &Arch) -> Result<()> {
let set_arch = USER_PROVIDED_ARCH.get_or_init(|| arch.clone());
if set_arch == arch {
Ok(())
} else {
Err(hc_error!(
Expand All @@ -80,12 +82,13 @@ pub fn try_set_arch(arch: SupportedArch) -> Result<()> {
}
}

impl FromStr for SupportedArch {
impl FromStr for KnownArch {
type Err = crate::Error;

fn from_str(s: &str) -> StdResult<Self, Self::Err> {
match s {
"aarch64-apple-darwin" => Ok(Self::Aarch64AppleDarwin),
"aarch64-unknown-linux-gnu" => Ok(Self::Aarch64UnknownLinuxGnu),
"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),
Expand All @@ -94,14 +97,36 @@ impl FromStr for SupportedArch {
}
}

impl Display for SupportedArch {
impl Display for KnownArch {
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",
KnownArch::Aarch64AppleDarwin => "aarch64-apple-darwin",
KnownArch::Aarch64UnknownLinuxGnu => "aarch64-unknown-linux-gnu",
KnownArch::X86_64AppleDarwin => "x86_64-apple-darwin",
KnownArch::X86_64PcWindowsMsvc => "x86_64-pc-windows-msvc",
KnownArch::X86_64UnknownLinuxGnu => "x86_64-unknown-linux-gnu",
};
write!(f, "{}", target_triple)
}
}

impl FromStr for Arch {
type Err = std::convert::Infallible;

fn from_str(s: &str) -> StdResult<Self, Self::Err> {
if let Ok(known_arch) = FromStr::from_str(s) {
Ok(Arch::Known(known_arch))
} else {
Ok(Arch::Unknown(s.to_owned()))
}
}
}

impl Display for Arch {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Arch::Known(known_arch) => known_arch.fmt(f),
Arch::Unknown(arch_str) => arch_str.fmt(f),
}
}
}
19 changes: 11 additions & 8 deletions hipcheck/src/plugin/download_manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ use crate::{
error::Error,
hc_error,
plugin::{
arch::Arch,
get_current_arch,
retrieval::{download_plugin, extract_plugin},
supported_arch::SupportedArch,
try_get_current_arch,
},
util::kdl::{extract_data, ParseKdlNode},
util::{
Expand All @@ -21,6 +21,9 @@ use kdl::{KdlDocument, KdlNode, KdlValue};
use std::{collections::HashSet, fmt::Display, io::Read, str::FromStr};
use url::Url;

#[cfg(test)]
use crate::plugin::arch::KnownArch;

// NOTE: the implementation in this crate was largely derived from RFD #0004

impl ParseKdlNode for url::Url {
Expand Down Expand Up @@ -225,7 +228,7 @@ pub struct DownloadManifestEntry {
/// but only a specific concrete version
pub version: PluginVersion,
/// The target architecture for a plugin
pub arch: SupportedArch,
pub arch: Arch,
/// The URL of the archive file to download containing the plugin executable artifact and
/// plugin manifest.
pub url: url::Url,
Expand All @@ -251,7 +254,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 = SupportedArch::from_str(node.get("arch")?.value().as_string()?).ok()?;
let arch = Arch::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
Expand Down Expand Up @@ -288,7 +291,7 @@ impl DownloadManifestEntry {
version: &PluginVersion,
downloaded_plugins: &'a mut HashSet<PluginId>,
) -> Result<&'a HashSet<PluginId>, Error> {
let current_arch = try_get_current_arch()?;
let current_arch = get_current_arch();

let plugin_id = PluginId::new(publisher.clone(), name.clone(), version.clone());

Expand Down Expand Up @@ -564,7 +567,7 @@ mod test {

let expected_entry = DownloadManifestEntry {
version: PluginVersion(version.to_string()),
arch: SupportedArch::from_str(arch).unwrap(),
arch: Arch::Known(KnownArch::from_str(arch).unwrap()),
url: Url::parse(url).unwrap(),
hash: HashWithDigest::new(
HashAlgorithm::try_from(hash_alg).unwrap(),
Expand Down Expand Up @@ -605,7 +608,7 @@ plugin version="0.1.0" arch="x86_64-apple-darwin" {
assert_eq!(
&DownloadManifestEntry {
version: PluginVersion("0.1.0".to_owned()),
arch: SupportedArch::Aarch64AppleDarwin,
arch: Arch::Known(KnownArch::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),
Expand All @@ -618,7 +621,7 @@ plugin version="0.1.0" arch="x86_64-apple-darwin" {
assert_eq!(
&DownloadManifestEntry {
version: PluginVersion("0.1.0".to_owned()),
arch: SupportedArch::X86_64AppleDarwin,
arch: Arch::Known(KnownArch::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),
Expand Down
4 changes: 2 additions & 2 deletions hipcheck/src/plugin/mod.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
// SPDX-License-Identifier: Apache-2.0

mod arch;
mod download_manifest;
mod manager;
mod plugin_id;
mod plugin_manifest;
mod retrieval;
mod supported_arch;
mod types;

use crate::error::Result;
pub use crate::plugin::{get_plugin_key, manager::*, plugin_id::PluginId, types::*};
pub use arch::{get_current_arch, try_set_arch, Arch};
pub use download_manifest::{ArchiveFormat, DownloadManifest, HashAlgorithm, HashWithDigest};
pub use plugin_manifest::{PluginManifest, PluginName, PluginPublisher, PluginVersion};
pub use retrieval::retrieve_plugins;
use serde_json::Value;
use std::collections::HashMap;
pub use supported_arch::{try_get_current_arch, try_set_arch, SupportedArch};
use tokio::sync::Mutex;

pub async fn initialize_plugins(
Expand Down
Loading

0 comments on commit d0197bf

Please sign in to comment.