Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow use of unknown arches through commandline #434

Merged
merged 1 commit into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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