diff --git a/Cargo.lock b/Cargo.lock index 0bb06e66..0f9ef22e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -420,9 +420,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.19" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7be5744db7978a28d9df86a214130d106a89ce49644cbc4e3f0c22c3fba30615" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" dependencies = [ "clap_builder", "clap_derive", @@ -440,9 +440,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.19" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5fbc17d3ef8278f55b282b2a2e75ae6f6c7d4bb70ed3d0382375104bfafdb4b" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" dependencies = [ "anstream", "anstyle", @@ -1030,6 +1030,17 @@ dependencies = [ "slab", ] +[[package]] +name = "fuzz" +version = "0.1.0" +dependencies = [ + "clap", + "hipcheck-sdk", + "serde", + "serde_json", + "tokio", +] + [[package]] name = "generic-array" version = "0.14.7" diff --git a/Cargo.toml b/Cargo.toml index 65235efb..bb2bb375 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,9 +22,10 @@ members = [ "sdk/rust", "hipcheck-sdk-macros", "plugins/git", - "plugins/github_api", + "plugins/github_api", "plugins/npm_dependencies", "plugins/activity", + "plugins/fuzz" ] # Make sure Hipcheck is run with `cargo run`. diff --git a/plugins/fuzz/Cargo.toml b/plugins/fuzz/Cargo.toml new file mode 100644 index 00000000..8d917e3c --- /dev/null +++ b/plugins/fuzz/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "fuzz" +version = "0.1.0" +license = "Apache-2.0" +edition = "2021" +publish = false + +[dependencies] +clap = { version = "4.5.20", features = ["derive"] } +hipcheck-sdk = { version = "0.1.0", path = "../../sdk/rust", features = ["macros"] } +serde = { version = "1.0.210", features = ["derive"] } +serde_json = "1.0.128" +tokio = { version = "1.40.0", features = ["rt"] } + +[dev-dependencies] +hipcheck-sdk = { path = "../../sdk/rust", features = ["macros", "mock_engine"] } diff --git a/plugins/fuzz/src/main.rs b/plugins/fuzz/src/main.rs new file mode 100644 index 00000000..ec3beadf --- /dev/null +++ b/plugins/fuzz/src/main.rs @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: Apache-2.0 + +use clap::Parser; +use hipcheck_sdk::{prelude::*, types::Target}; +use serde_json::Value; +use std::result::Result as StdResult; + +/// Returns whether the target's remote repo uses Google's OSS fuzzing +#[query(default)] +async fn fuzz(engine: &mut PluginEngine, key: Target) -> Result { + if let Some(remote) = &key.remote { + engine.query("mitre/github_api", remote.clone()).await + } else { + Err(Error::UnexpectedPluginQueryInputFormat) + } +} + +#[derive(Parser, Debug)] +struct Args { + #[arg(long)] + port: u16, +} + +#[derive(Clone, Debug)] +struct FuzzAnalysisPlugin {} + +impl Plugin for FuzzAnalysisPlugin { + const PUBLISHER: &'static str = "mitre"; + const NAME: &'static str = "fuzz"; + + fn set_config(&self, _config: Value) -> StdResult<(), ConfigError> { + Ok(()) + } + + fn default_policy_expr(&self) -> Result { + Ok("(eq $ #t)".to_owned()) + } + + fn explain_default_query(&self) -> Result> { + Ok(Some("Does the target repo do fuzzing".to_owned())) + } + + queries! {} +} + +#[tokio::main(flavor = "current_thread")] +async fn main() -> Result<()> { + let args = Args::try_parse().unwrap(); + PluginServer::register(FuzzAnalysisPlugin {}) + .listen(args.port) + .await +} + +#[cfg(test)] +mod test { + use super::*; + use hipcheck_sdk::types::{KnownRemote, LocalGitRepo, RemoteGitRepo}; + + fn target() -> Target { + let local = LocalGitRepo { + path: "/home/users/me/.cache/hipcheck/clones/github/mitre/hipcheck/".to_string(), + git_ref: "main".to_string(), + }; + let known_remote = Some(KnownRemote::GitHub { + owner: "mitre".to_owned(), + repo: "hipcheck".to_owned(), + }); + let remote = Some(RemoteGitRepo { + url: "https://github.com/mitre/hipcheck".parse().unwrap(), + known_remote, + }); + Target { + specifier: "hipcheck".to_owned(), + local, + remote, + package: None, + } + } + + fn mock_responses() -> StdResult { + let target = target(); + let known_remote = target.remote.as_ref().unwrap().clone(); + let output = true; + Ok(MockResponses::new().insert("mitre/github_api", known_remote, Ok(output))?) + } + + #[tokio::test] + async fn test_fuzz() { + let target = target(); + let mut engine = PluginEngine::mock(mock_responses().unwrap()); + let output = fuzz(&mut engine, target).await.unwrap(); + let result: bool = serde_json::from_value(output).unwrap(); + let expected = true; + + assert_eq!(result, expected); + } +} diff --git a/plugins/github_api/src/data.rs b/plugins/github_api/src/data.rs index bacf759b..d33222d9 100644 --- a/plugins/github_api/src/data.rs +++ b/plugins/github_api/src/data.rs @@ -23,7 +23,7 @@ impl<'a> GitHub<'a> { } pub fn fuzz_check(&self, repo_uri: Rc) -> Result { - search_code_request(&self.agent, repo_uri).context("unable to search fuzzing information; please check the HC_GITHUB_TOKEN system environment variable") + search_code_request(&self.agent, repo_uri).context("unable to search fuzzing information; please ensure the provided system environment variable exists and contains a valid GitHub API token") } pub fn get_reviews_for_pr(&self) -> Result> { diff --git a/sdk/rust/src/mock.rs b/sdk/rust/src/mock.rs index d94c48f3..3a2f612e 100644 --- a/sdk/rust/src/mock.rs +++ b/sdk/rust/src/mock.rs @@ -1,6 +1,4 @@ -use serde::Serialize; - -use crate::{Error, JsonValue, QueryTarget, Result}; +use crate::{JsonValue, QueryTarget, Result}; use std::collections::HashMap; #[derive(Default)] @@ -32,13 +30,16 @@ impl MockResponses { ) -> Result where T: TryInto>, - V: Serialize, - W: Into, + V: serde::Serialize, + W: serde::Serialize, { let query_target: QueryTarget = query_target.try_into().map_err(|e| e.into())?; let query_value: JsonValue = - serde_json::to_value(query_value).map_err(Error::InvalidJsonInQueryKey)?; - let query_response = query_response.map(|v| v.into()); + serde_json::to_value(query_value).map_err(crate::Error::InvalidJsonInQueryKey)?; + let query_response = match query_response { + Ok(v) => serde_json::to_value(v).map_err(crate::Error::InvalidJsonInQueryKey), + Err(e) => Err(e), + }; self.inner_insert(query_target, query_value, query_response) } }