diff --git a/apps/cargo-scout-audit/Cargo.lock b/apps/cargo-scout-audit/Cargo.lock index 51246971..e5942cda 100644 --- a/apps/cargo-scout-audit/Cargo.lock +++ b/apps/cargo-scout-audit/Cargo.lock @@ -414,7 +414,7 @@ dependencies = [ [[package]] name = "cargo-scout-audit" -version = "0.2.7" +version = "0.2.8" dependencies = [ "ansi_term", "anyhow", diff --git a/apps/cargo-scout-audit/Cargo.toml b/apps/cargo-scout-audit/Cargo.toml index bbbe88d1..62717072 100644 --- a/apps/cargo-scout-audit/Cargo.toml +++ b/apps/cargo-scout-audit/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cargo-scout-audit" -version = "0.2.7" +version = "0.2.9" edition = "2021" authors = [ "Agustin Aon ", @@ -16,6 +16,7 @@ description = "Scout is an extensible open-source tool intended to assist Ink! a readme = "../../README.md" keywords = ["auditor", "security", "ink", "soroban", "smart-contracts"] categories = ["development-tools", "command-line-utilities"] +repository = "https://github.com/coinfabrik/scout-audit" [lib] path = "src/lib.rs" @@ -47,8 +48,3 @@ tempfile = "3.8" toml = { version = "0.8.0" } tera = {version = "=1.19.1", features=["builtins"]} webbrowser = "=0.8.12" -pulldown-cmark = "0.10.0" - -[dev-dependencies] -colored = "2.0.0" - diff --git a/apps/cargo-scout-audit/src/startup.rs b/apps/cargo-scout-audit/src/startup.rs index 84fc460c..8ed99bf5 100644 --- a/apps/cargo-scout-audit/src/startup.rs +++ b/apps/cargo-scout-audit/src/startup.rs @@ -133,13 +133,23 @@ pub struct ProjectInfo { pub workspace_root: PathBuf, } -pub fn run_scout(opts: Scout) -> Result<()> { +pub fn run_scout(mut opts: Scout) -> Result<()> { let opt_child = run_scout_in_nightly()?; if let Some(mut child) = opt_child { child.wait()?; return Ok(()); } + // If the target is not set to wasm32-unknown-unknown, set it + let target_args_flag = "--target=wasm32-unknown-unknown".to_string(); + let no_default = "--no-default-features".to_string(); + let z_build_std = "-Zbuild-std=std,core,alloc".to_string(); + + if !opts.args.iter().any(|x| x.contains("--target=")) { + opts.args + .extend([target_args_flag, no_default, z_build_std]) + } + // Validations if opts.filter.is_some() && opts.exclude.is_some() { panic!("You can't use `--exclude` and `--filter` at the same time."); diff --git a/apps/cargo-scout-audit/tests/README.md b/apps/cargo-scout-audit/tests/README.md deleted file mode 100644 index 272ce747..00000000 --- a/apps/cargo-scout-audit/tests/README.md +++ /dev/null @@ -1,39 +0,0 @@ -# Acceptance Criteria for Tests in Rust - -This document provides the acceptance criteria for writing and executing tests in Rust. The examples are drawn from the provided files: `test.rs` (which contains the logic to perform the tests) and `test-configuration.yaml` (which contains the paths and configurations for the tests). - -## Acceptance Criteria for Adding Examples and Ensuring Tests Work - -### 1. Valid YAML Structure - -The `test-configuration.yaml` file must be a valid YAML file that follows the specific structure of `detectors`. Each `detector` should have a unique name and include a `DetectorConfig` object. - -### 2. Warning Message - -Each `DetectorConfig` must include a `warning_message`. This message is expected to appear in the output when a test against a vulnerable path is executed. The `warning_message` should be specific and unique to the vulnerability that the detector is designed to identify. - -### 3. Examples - -Each `DetectorConfig` must also include at least one `Example` object. An `Example` consists of a `vulnerable_path` and a `remediated_path`. - -### 4. Path Specification - -The `vulnerable_path` and `remediated_path` in each `Example` must point to valid `Cargo.toml` files in the project. These paths are relative to the directory where the tests are executed from. - -- `vulnerable_path`: This is the path to the project file of the code that is expected to contain the vulnerability. -- `remediated_path`: This is the path to the project file of the code that has been remediated and is expected to be free of the vulnerability. - -### 5. Path Validation - -All paths specified in the `vulnerable_path` and `remediated_path` must be valid and the referenced `Cargo.toml` files should exist. If a path is invalid or the referenced file does not exist, the test will fail. - -### 6. Successful Command Execution - -Each `Example` will be tested by executing the `cargo scout-audit` command with the provided paths. The test must be able to execute this command successfully. If the command execution fails, the test will fail. - -### 7. Proper Linter Output Validation - -Each `Example` will be tested by running our Rust linter using the `cargo scout-audit` command. The output generated by this linter is then compared against the `warning_message` specified in the `DetectorConfig`. - -- If the `Example` is a `vulnerable_path`, the linter output (specifically, the `stderr` or error stream) should contain the `warning_message`. If the linter output does not contain the expected warning, the test will fail. -- If the `Example` is a `remediated_path`, the linter output should not contain the `warning_message`. If the linter output still contains the warning, indicating a detected vulnerability, the test will fail. diff --git a/apps/cargo-scout-audit/tests/integration_tests/detectors.rs b/apps/cargo-scout-audit/tests/integration_tests/detectors.rs deleted file mode 100644 index 7bc3a95c..00000000 --- a/apps/cargo-scout-audit/tests/integration_tests/detectors.rs +++ /dev/null @@ -1,149 +0,0 @@ -use std::collections::HashMap; -use std::io::Read; -use std::path::PathBuf; - -use cargo_scout_audit::startup::{run_scout, OutputFormat, Scout}; -use colored::Colorize; -use configuration::Configuration; -use scout_audit_internal::DetectorImpl; -use serde::{Deserialize, Serialize}; - -mod configuration; -mod utils; - -#[derive(Debug, Serialize, Deserialize)] -pub struct Detectors { - detectors: HashMap, -} - -#[derive(Debug, Serialize, Deserialize)] -struct DetectorConfig { - warning_message: String, - testcases: Vec, -} - -#[derive(Debug, Serialize, Deserialize)] -struct Testcase { - vulnerable_path: Option, - remediated_path: Option, -} - -/// Test that all detectors run successfully on testcases and -/// that lint messages match the expected ones. -/// -/// The following environment variable can be used: -/// -/// - `INTEGRATION_TESTS_TO_RUN`: comma separated list of integration tests to run. -/// If not set, all integration tests are run. -#[test] -fn test() { - // Get environment variable to determine integration tests to run - let integration_tests_to_run = std::env::var("INTEGRATION_TESTS_TO_RUN") - .ok() - .map(|e| e.split(',').map(|s| s.to_string()).collect::>()); - let mut ran_integration_tests = - vec![false; integration_tests_to_run.as_ref().map_or(0, |v| v.len())]; - - // Get the configuration - let detectors_config = Configuration::build() - .unwrap_or_else(|_| panic!("{}", "Failed to get the configuration".red().to_string())); - - // Run all integration tests - for detector_config in detectors_config.detectors.iter() { - let detector_name = detector_config.detector.to_string(); - let lint_message = detector_config.detector.get_lint_message(); - - if let Some(integration_tests_to_run) = &integration_tests_to_run { - let integration_tests_to_run_i = integration_tests_to_run - .iter() - .position(|t| t == &detector_name); - match integration_tests_to_run_i { - Some(i) => ran_integration_tests[i] = true, - None => continue, - }; - } - - println!("\n{} {}", "Testing detector:".bright_cyan(), detector_name); - for example in detector_config.testcases.iter() { - if let Some(vulnerable_path) = &example.vulnerable_path { - execute_and_validate_testcase(&detector_name, lint_message, vulnerable_path, true); - } - if let Some(remediated_path) = &example.remediated_path { - execute_and_validate_testcase(&detector_name, lint_message, remediated_path, false); - } - } - } - - // If integration tests to run were specified, check that all of them were run - if let Some(integration_tests_to_run) = &integration_tests_to_run { - let panic_exit = ran_integration_tests.iter().any(|t| !t); - for (i, ran_integration_test) in ran_integration_tests.iter().enumerate() { - if !ran_integration_test { - println!( - "{} {}", - "Error: integration test not found:".bright_red(), - integration_tests_to_run[i] - ); - } - } - if panic_exit { - panic!(); - } - } -} - -fn execute_and_validate_testcase( - detector_name: &str, - lint_message: &str, - path: &str, - is_vulnerable: bool, -) { - print!("{} {}", "Running testcase:".green(), path); - let start_time = std::time::Instant::now(); - - // Create tempfile for storing the output - let mut tempfile = tempfile::NamedTempFile::new().expect("Failed to create tempfile"); - - // Run scout - let scout_config = Scout { - output_format: OutputFormat::Text, - output_path: Some(PathBuf::from(tempfile.path())), - local_detectors: Some(get_detectors_path()), - manifest_path: Some(PathBuf::from(path.to_string())), - filter: Some(detector_name.to_string()), - verbose: true, - ..Default::default() - }; - run_scout(scout_config).unwrap(); - - // Read output - let mut output = String::new(); - tempfile - .read_to_string(&mut output) - .expect("Failed to read tempfile"); - - let end_time = std::time::Instant::now(); - - assert!( - output.contains(lint_message) == is_vulnerable, - "\n\n{}\n\n{}\n\n", - if is_vulnerable { - "Error: vulnerability not found on a vulnerable path".red() - } else { - "Error: vulnerability found on a non vulnerable path".red() - }, - output - ); - - println!( - " - {} {} secs.", - "Elapsed time:".bright_purple(), - end_time.duration_since(start_time).as_millis() as f64 / 1000.0 - ); -} - -fn get_detectors_path() -> PathBuf { - utils::get_repository_root_path() - .expect("Failed to get detectors path") - .join("detectors") -} diff --git a/apps/cargo-scout-audit/tests/integration_tests/detectors/configuration.rs b/apps/cargo-scout-audit/tests/integration_tests/detectors/configuration.rs deleted file mode 100644 index 942d80f9..00000000 --- a/apps/cargo-scout-audit/tests/integration_tests/detectors/configuration.rs +++ /dev/null @@ -1,111 +0,0 @@ -use std::path::{Path, PathBuf}; - -use anyhow::{anyhow, bail}; -use itertools::Itertools; -use scout_audit_internal::{InkDetector as Detector, IntoEnumIterator}; - -use super::utils; - -#[derive(Debug)] -pub struct Configuration { - pub detectors: Vec, -} - -#[derive(Debug)] -pub struct DetectorConfiguration { - pub detector: Detector, - pub testcases: Vec, -} - -#[derive(Debug)] -pub struct Testcase { - pub vulnerable_path: Option, - pub remediated_path: Option, -} - -impl Configuration { - pub fn build() -> anyhow::Result { - // Get all testcases folders - let cargo_scout_audit_path = utils::get_cargo_scout_audit_path()?; - let testcases_root_path = cargo_scout_audit_path - .parent() - .ok_or(anyhow!("Failed to find testcases path"))? - .parent() - .ok_or(anyhow!("Failed to find testcases path"))? - .join("test-cases"); - let testcases_paths: Vec = std::fs::read_dir(&testcases_root_path)? - .filter_map(|r| r.ok().map(|f| f.path())) - .filter(|r| r.is_dir()) - .collect(); - - Self::validate_all_detectors_found(testcases_paths)?; - - // Find all testcases for each detector - let mut detectors_config = Vec::new(); - for detector in Detector::iter() { - let detector_name = detector.to_string(); - let testcases_root_path = testcases_root_path.join(detector_name); - let testcases_paths: Vec = std::fs::read_dir(testcases_root_path)? - .filter_map(|r| r.ok().map(|f| f.path())) - .filter(|r| r.is_dir()) - .collect(); - - let mut testcases = Vec::new(); - for testcase_path in testcases_paths { - let vulnerable_path = testcase_path.join("vulnerable-example"); - let remediated_path = testcase_path.join("remediated-example"); - - let testcase = Testcase { - vulnerable_path: if vulnerable_path.exists() { - Some( - vulnerable_path - .join("Cargo.toml") - .to_string_lossy() - .to_string(), - ) - } else { - None - }, - remediated_path: if Path::new(&remediated_path).exists() { - Some( - remediated_path - .join("Cargo.toml") - .to_string_lossy() - .to_string(), - ) - } else { - None - }, - }; - testcases.push(testcase); - } - - detectors_config.push(DetectorConfiguration { - detector, - testcases, - }); - } - - Ok(Configuration { - detectors: detectors_config, - }) - } - - fn validate_all_detectors_found(testcases_paths: T) -> anyhow::Result<()> - where - T: IntoIterator, - { - let count = testcases_paths - .into_iter() - .sorted() - .zip(Detector::iter().map(|d| d.to_string()).sorted()) - .filter(|(p, d)| p.file_name().unwrap().to_string_lossy() != *d) - .count(); - - if count > 0 { - bail!("Testcases don't match detectors defined in scout-audit-internal.") - } - - Ok(()) - } -} diff --git a/apps/cargo-scout-audit/tests/integration_tests/detectors/utils.rs b/apps/cargo-scout-audit/tests/integration_tests/detectors/utils.rs deleted file mode 100644 index f5365a3b..00000000 --- a/apps/cargo-scout-audit/tests/integration_tests/detectors/utils.rs +++ /dev/null @@ -1,14 +0,0 @@ -use std::path::PathBuf; - -use cargo_metadata::MetadataCommand; - -pub fn get_cargo_scout_audit_path() -> cargo_metadata::Result { - Ok(MetadataCommand::new().exec()?.workspace_root.into()) -} - -pub fn get_repository_root_path() -> cargo_metadata::Result { - let mut path = get_cargo_scout_audit_path()?; - path.pop(); - path.pop(); - Ok(path) -} diff --git a/apps/cargo-scout-audit/tests/integration_tests/main.rs b/apps/cargo-scout-audit/tests/integration_tests/main.rs deleted file mode 100644 index 4235583f..00000000 --- a/apps/cargo-scout-audit/tests/integration_tests/main.rs +++ /dev/null @@ -1 +0,0 @@ -mod detectors;