From 30e058bab57dd6c95e315c169b1223403e976d14 Mon Sep 17 00:00:00 2001 From: Jose Garcia Crosta Date: Wed, 21 Feb 2024 14:55:05 -0300 Subject: [PATCH 1/4] Update soroban-version detector to get by string and table --- detectors/soroban-version/src/lib.rs | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/detectors/soroban-version/src/lib.rs b/detectors/soroban-version/src/lib.rs index acc10d23..3ecd43f2 100644 --- a/detectors/soroban-version/src/lib.rs +++ b/detectors/soroban-version/src/lib.rs @@ -68,10 +68,7 @@ impl EarlyLintPass for CheckSorobanVersion { } }; - let soroban_version = match toml - .get("dependencies") - .and_then(|d| d.get("soroban-sdk").and_then(|i| i.get("version"))) - { + let soroban_version = match find_soroban_version_in_dependencies(&toml) { Some(version) => version.to_string(), None => { cx.sess() @@ -91,7 +88,7 @@ impl EarlyLintPass for CheckSorobanVersion { } }; - let soroban_version = match VersionReq::parse(&soroban_version.replace('\"', "")) { + let soroban_version = match Version::parse(&soroban_version.replace('\"', "")) { Ok(version) => version, Err(e) => { cx.sess() @@ -101,12 +98,14 @@ impl EarlyLintPass for CheckSorobanVersion { } }; - if !soroban_version.matches(&req) { + if !soroban_version.eq(&req) { Detector::SorobanVersion.span_lint_and_help( cx, CHECK_SOROBAN_VERSION, rustc_span::DUMMY_SP, - &format!(r#"The latest Soroban version is {latest_version}, and your version is "{soroban_version}""#), + &format!( + r#"The latest Soroban version is {latest_version}, and your version is "{soroban_version}""# + ), ); } } @@ -132,3 +131,16 @@ fn get_version() -> Result { Err(_) => Err("Failed to get Soroban version from crates.io".to_string()), } } + +fn find_soroban_version_in_dependencies(toml: &toml::Value) -> Option { + toml.get("dependencies").and_then(|deps| { + deps.get("soroban-sdk").and_then(|v| match v { + toml::Value::String(s) => Some(s.clone()), + toml::Value::Table(t) => t + .get("version") + .and_then(|v| v.as_str()) + .map(|s| s.to_string()), + _ => None, + }) + }) +} From e56f85a9284951f020eaa524bb27f6cb6e2bf5bb Mon Sep 17 00:00:00 2001 From: Jose Garcia Crosta Date: Thu, 22 Feb 2024 19:07:28 -0300 Subject: [PATCH 2/4] Refactor soroban-version detector for workspace version support --- detectors/soroban-version/Cargo.toml | 2 - detectors/soroban-version/src/lib.rs | 94 +++++++++++++++++----------- 2 files changed, 58 insertions(+), 38 deletions(-) diff --git a/detectors/soroban-version/Cargo.toml b/detectors/soroban-version/Cargo.toml index 6acc66e4..60b26923 100644 --- a/detectors/soroban-version/Cargo.toml +++ b/detectors/soroban-version/Cargo.toml @@ -8,10 +8,8 @@ crate-type = ["cdylib"] [dependencies] dylint_linting = { workspace = true } -if_chain = { workspace = true } semver = "1.0.4" serde_json = "1.0" -toml = "0.8.8" ureq = { version = "2.7.1", features = ["json"] } scout-audit-internal = { workspace = true } diff --git a/detectors/soroban-version/src/lib.rs b/detectors/soroban-version/src/lib.rs index 3ecd43f2..f4ba013e 100644 --- a/detectors/soroban-version/src/lib.rs +++ b/detectors/soroban-version/src/lib.rs @@ -4,12 +4,13 @@ extern crate rustc_ast; extern crate rustc_hir; extern crate rustc_span; -use std::{env, fs, io::Error, path::Path}; +use std::{io::Error, process::Command}; use rustc_ast::Crate; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use scout_audit_internal::Detector; -use semver::*; +use semver::Version; +use serde_json::Value; dylint_linting::declare_early_lint! { /// ### What it does @@ -24,7 +25,7 @@ dylint_linting::declare_early_lint! { impl EarlyLintPass for CheckSorobanVersion { fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &Crate) { - let latest_version = match get_version() { + let latest_soroban_version = match get_latest_soroban_version() { Ok(version) => version, Err(e) => { cx.sess() @@ -34,51 +35,70 @@ impl EarlyLintPass for CheckSorobanVersion { } }; - let manifest_dir = match env::var("CARGO_MANIFEST_DIR") { - Ok(dir) => dir, + let cargo_metadata = match get_cargo_metadata() { + Ok(metadata) => metadata, Err(e) => { cx.sess() - .struct_warn(format!( - "Environment variable CARGO_MANIFEST_DIR not found: {}", - e - )) + .struct_warn(format!("Failed to get cargo metadata: {}", e)) .emit(); return; } }; - let cargo_toml_path = Path::new(&manifest_dir).join("Cargo.toml"); - let cargo_toml = match fs::read_to_string(cargo_toml_path) { - Ok(content) => content, - Err(e) => { + let cargo_toml_package_opt = match cargo_metadata["packages"].as_array() { + Some(packages) => packages.first(), + None => { cx.sess() - .struct_warn(format!("Unable to read Cargo.toml: {}", e)) + .struct_warn("Error parsing cargo metadata: packages not found") .emit(); return; } }; - let toml: toml::Value = match toml::from_str(&cargo_toml) { - Ok(value) => value, - Err(e) => { + let cargo_toml_package = match cargo_toml_package_opt { + Some(package) => package, + None => { + cx.sess() + .struct_warn("Error parsing cargo metadata: first package not found") + .emit(); + return; + } + }; + + let dependencies = match cargo_toml_package["dependencies"].as_array() { + Some(dependencies) => dependencies, + None => { cx.sess() - .struct_warn(format!("Error parsing Cargo.toml: {}", e)) + .struct_warn("Error parsing cargo metadata: dependencies not found") .emit(); return; } }; - let soroban_version = match find_soroban_version_in_dependencies(&toml) { - Some(version) => version.to_string(), + let current_dependency = match dependencies + .iter() + .find(|&dep| dep["name"].as_str().unwrap_or("") == "soroban-sdk") + { + Some(current_dependency) => current_dependency, None => { cx.sess() - .struct_warn("Soroban dependency not found in Cargo.toml") + .struct_warn("Soroban dependency not found in dependencies") .emit(); return; } }; - let req = match Version::parse(&latest_version.replace('\"', "")) { + let current_dependency_version = match current_dependency["req"].as_str() { + Some(version) => version, + None => { + cx.sess() + .struct_warn("Error parsing current Soroban version") + .emit(); + return; + } + }; + + let req = match Version::parse(&latest_soroban_version.replace('\"', "")) { Ok(version) => version, Err(e) => { cx.sess() @@ -88,7 +108,9 @@ impl EarlyLintPass for CheckSorobanVersion { } }; - let soroban_version = match Version::parse(&soroban_version.replace('\"', "")) { + let cleaned_version = current_dependency_version.replace(&['=', '^'][..], ""); + + let soroban_version = match Version::parse(&cleaned_version) { Ok(version) => version, Err(e) => { cx.sess() @@ -104,14 +126,14 @@ impl EarlyLintPass for CheckSorobanVersion { CHECK_SOROBAN_VERSION, rustc_span::DUMMY_SP, &format!( - r#"The latest Soroban version is {latest_version}, and your version is "{soroban_version}""# + r#"The latest Soroban version is {latest_soroban_version}, and your version is "{soroban_version}""# ), ); } } } -fn get_version() -> Result { +fn get_latest_soroban_version() -> Result { let response = ureq::get("https://crates.io/api/v1/crates/soroban-sdk") .set("User-Agent", "Scout/1.0") .call(); @@ -132,15 +154,15 @@ fn get_version() -> Result { } } -fn find_soroban_version_in_dependencies(toml: &toml::Value) -> Option { - toml.get("dependencies").and_then(|deps| { - deps.get("soroban-sdk").and_then(|v| match v { - toml::Value::String(s) => Some(s.clone()), - toml::Value::Table(t) => t - .get("version") - .and_then(|v| v.as_str()) - .map(|s| s.to_string()), - _ => None, - }) - }) +fn get_cargo_metadata() -> Result { + let output = Command::new("cargo") + .args(["metadata", "--format-version=1", "--no-deps"]) + .output(); + + match output { + Ok(output) if output.status.success() => serde_json::from_slice(&output.stdout) + .map_err(|e| format!("Error parsing cargo metadata: {}", e)), + Ok(output) => Err(String::from_utf8_lossy(&output.stderr).into_owned()), + Err(e) => Err(e.to_string()), + } } From 7c177276a6cc5b5129a093e2e14c0e765b1a1400 Mon Sep 17 00:00:00 2001 From: aweil Date: Mon, 26 Feb 2024 15:10:13 -0300 Subject: [PATCH 3/4] exclude soroban_version from listed detectors against dir detectors --- scripts/check-ci-detectors-to-test.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/check-ci-detectors-to-test.py b/scripts/check-ci-detectors-to-test.py index 53abe9d2..edef4571 100644 --- a/scripts/check-ci-detectors-to-test.py +++ b/scripts/check-ci-detectors-to-test.py @@ -13,12 +13,13 @@ def is_special_directory(directory): with open(args.gh_workflow_path, "r") as f: workflow = yaml.safe_load(f) - detectors_to_test = workflow["jobs"]["test"]["strategy"]["matrix"]["test"] + detectors_to_test = set()workflow["jobs"]["test"]["strategy"]["matrix"]["test"]) - detectors = [ f.name for f in os.scandir(args.detectors_path) if f.is_dir() and not is_special_directory(f.name) ] - detectors.sort() + detectors = set(f.name for f in os.scandir(args.detectors_path) if f.is_dir() and not is_special_directory(f.name)) + # detectors.sort() - if (detectors != detectors_to_test): + sym_diff = detectors ^ detectors_to_test + if len(sym_diff)!=0 and sym_diff!=set(['soroban-version']): print("Detectors to test in the workflow are not the same as the detectors in the detectors folder.") print("Detectors to test: ", detectors_to_test) print("Detectors in the folder: ", detectors) From 1f5304431a8b11b7842a291629c8d441991ceeaa Mon Sep 17 00:00:00 2001 From: aweil Date: Mon, 26 Feb 2024 15:11:35 -0300 Subject: [PATCH 4/4] exclude soroban_version from listed detectors against dir detectors --- scripts/check-ci-detectors-to-test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check-ci-detectors-to-test.py b/scripts/check-ci-detectors-to-test.py index edef4571..b1d4ad59 100644 --- a/scripts/check-ci-detectors-to-test.py +++ b/scripts/check-ci-detectors-to-test.py @@ -13,7 +13,7 @@ def is_special_directory(directory): with open(args.gh_workflow_path, "r") as f: workflow = yaml.safe_load(f) - detectors_to_test = set()workflow["jobs"]["test"]["strategy"]["matrix"]["test"]) + detectors_to_test = set(workflow["jobs"]["test"]["strategy"]["matrix"]["test"]) detectors = set(f.name for f in os.scandir(args.detectors_path) if f.is_dir() and not is_special_directory(f.name)) # detectors.sort()