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

Add Substrate pallet support #136

Merged
merged 9 commits into from
Oct 30, 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
8 changes: 6 additions & 2 deletions apps/cargo-scout-audit/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 5 additions & 4 deletions apps/cargo-scout-audit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
[package]
authors = [
"Agustin Aon <agustin.aon@coinfabrik.com>",
"Agustin Losiggio <agustin.losiggio@coinfabrik.com>",
"Ariel Waissbein <ariel.waissbein@coinfabrik.com",
"Arturo Beccar-Varela <arturo.beccar@coinfabrik.com>",
"José García Crosta <jose.garcia.crosta@coinfabrik.com>",
"Víctor M. González <victor.gonzalez@coinfabrik.com>",
"Facundo Lerena <facundo.lerena@coinfabrik.com>",
"Agustin Losiggio <agustin.losiggio@coinfabrik.com>",
"Federico Pinho <federico.pinho@coinfabrik.com>",
"José García Crosta <jose.garcia.crosta@coinfabrik.com>",
"Ariel Waissbein <ariel.waissbein@coinfabrik.com",
]


Expand All @@ -21,7 +22,7 @@ license = "MIT"
name = "cargo-scout-audit"
readme = "../../README.md"
repository = "https://github.com/coinfabrik/scout-audit"
version = "0.2.18"
version = "0.2.19"

[lib]
path = "src/lib.rs"
Expand Down
4 changes: 4 additions & 0 deletions apps/cargo-scout-audit/src/scout/blockchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use strum_macros::{Display, EnumIter, EnumString};
pub enum BlockChain {
Ink,
Soroban,
SubstratePallet,
}

impl BlockChain {
Expand All @@ -19,13 +20,15 @@ impl BlockChain {
match self {
BlockChain::Ink => "https://github.com/CoinFabrik/scout",
BlockChain::Soroban => "https://github.com/CoinFabrik/scout-soroban",
BlockChain::SubstratePallet => "https://github.com/CoinFabrik/scout-substrate",
}
}

pub fn get_toolchain(&self) -> &str {
match self {
BlockChain::Ink => INK_TOOLCHAIN,
BlockChain::Soroban => SOROBAN_TOOLCHAIN,
BlockChain::SubstratePallet => INK_TOOLCHAIN,
}
}

Expand All @@ -37,6 +40,7 @@ impl BlockChain {
.find_map(|p| match p.name.as_str() {
"soroban-sdk" => Some(BlockChain::Soroban),
"ink" => Some(BlockChain::Ink),
"frame-system" => Some(BlockChain::SubstratePallet),
_ => None,
})
.with_context(|| {
Expand Down
76 changes: 62 additions & 14 deletions apps/cargo-scout-audit/src/startup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::{
utils::{
config::{open_config_and_sync_detectors, profile_enabled_detectors},
detectors::{get_excluded_detectors, get_filtered_detectors, list_detectors},
detectors_info::{get_detectors_info, LintInfo},
detectors_info::{get_detectors_info, CustomLint, LintInfo},
print::{print_error, print_warning},
},
};
Expand All @@ -23,7 +23,12 @@ use cargo_metadata::{Metadata, MetadataCommand};
use clap::{Parser, Subcommand, ValueEnum};
use dylint::opts::{Check, Dylint, LibrarySelection, Operation};
use serde_json::{from_str, to_string_pretty, Value};
use std::{collections::HashMap, fs, io::Write, path::PathBuf};
use std::{
collections::{HashMap, HashSet},
fs,
io::Write,
path::PathBuf,
};
use tempfile::NamedTempFile;
use terminal_color_builder::OutputFormatter;

Expand Down Expand Up @@ -224,7 +229,7 @@ fn get_crate_from_finding(finding: &Value) -> Option<String> {
//In some cases, rustc (or dylint, or clipply, or whoever) has returned the
//package name where it should be returning the crate name. If you run into
//problems in the future, try removing the call to this function.
fn normalize_crate_name(s: String) -> String {
fn normalize_crate_name(s: &str) -> String {
let mut ret = String::new();
ret.reserve(s.len());
for c in s.chars() {
Expand All @@ -233,7 +238,7 @@ fn normalize_crate_name(s: String) -> String {
ret
}

fn get_crates(output: Vec<Value>) -> HashMap<String, bool> {
fn get_crates_from_output(output: &Vec<Value>) -> HashMap<String, bool> {
let mut ret = HashMap::<String, bool>::new();

for val in output {
Expand All @@ -244,11 +249,11 @@ fn get_crates(output: Vec<Value>) -> HashMap<String, bool> {
}
let message = message.unwrap();

let name = get_crate_from_finding(&val);
let name = get_crate_from_finding(val);
if name.is_none() {
continue;
}
let name = normalize_crate_name(name.unwrap());
let name = normalize_crate_name(&name.unwrap());
if let Some(previous) = ret.get(&name) {
if !previous {
continue;
Expand All @@ -262,6 +267,36 @@ fn get_crates(output: Vec<Value>) -> HashMap<String, bool> {
ret
}

fn get_crates_from_findings(findings: &Vec<String>) -> HashSet<String> {
let mut ret = HashSet::<String>::new();

for s in findings {
let value = from_str::<Value>(s).unwrap();
let krate = json_to_string(value.get("crate").unwrap());
ret.insert(krate);
}

ret
}

fn get_crates(
output: &Vec<Value>,
findings: &Vec<String>,
packages: &[crate::output::report::Package],
) -> HashMap<String, bool> {
let mut ret = get_crates_from_output(output);
let krates = get_crates_from_findings(findings);
for krate in krates {
ret.entry(krate).or_insert(true);
}
for package in packages.iter() {
ret.entry(normalize_crate_name(&package.name))
.or_insert(true);
}

ret
}

fn split_findings(
raw_findings: Vec<String>,
crates: &HashMap<String, bool>,
Expand Down Expand Up @@ -416,7 +451,7 @@ pub fn run_scout(mut opts: Scout) -> Result<Vec<Value>> {
)
})?;

let detectors_info = get_detectors_info(&detectors_paths)?;
let (detectors_info, custom_detectors) = get_detectors_info(&detectors_paths)?;

if opts.detectors_metadata {
let json = to_string_pretty(&detectors_info);
Expand All @@ -435,15 +470,22 @@ pub fn run_scout(mut opts: Scout) -> Result<Vec<Value>> {
capture_output
};

let (findings, (_successful_build, stdout)) = wrapper_function(|| {
let (findings, (_failed_build, stdout)) = wrapper_function(|| {
// Run dylint
run_dylint(detectors_paths.clone(), &opts, &metadata, inside_vscode)
.map_err(|err| anyhow!("Failed to run dylint.\n\n → Caused by: {}", err))
run_dylint(
detectors_paths.clone(),
&opts,
&metadata,
inside_vscode,
&custom_detectors,
)
.map_err(|err| anyhow!("Failed to run dylint.\n\n → Caused by: {}", err))
})?;

let output_string = temp_file_to_string(stdout)?;
//println!("{}", output_string);
let output = output_to_json(&output_string);
let crates = get_crates(output.clone());
let crates = get_crates(&output, &findings, &project_info.packages);

if crates.is_empty() && !inside_vscode {
let string = OutputFormatter::new()
Expand Down Expand Up @@ -531,12 +573,13 @@ fn do_report(
Ok(())
}

#[tracing::instrument(name = "RUN DYLINT", skip(detectors_paths, opts))]
#[tracing::instrument(name = "RUN DYLINT", skip(detectors_paths, opts, custom_detectors))]
fn run_dylint(
detectors_paths: Vec<PathBuf>,
opts: &Scout,
metadata: &Metadata,
inside_vscode: bool,
custom_detectors: &HashMap<String, CustomLint<'_>>,
) -> Result<(bool, NamedTempFile)> {
// Convert detectors paths to string
let detectors_paths: Vec<String> = detectors_paths
Expand Down Expand Up @@ -579,9 +622,14 @@ fn run_dylint(

crate::cleanup::clean_up_before_run(metadata);

let success = dylint::run(&options).is_err();
let failure = dylint::run(&options).is_err();
if !failure {
for (_, lint) in custom_detectors.iter() {
lint.call();
}
}

Ok((success, stdout_temp_file))
Ok((failure, stdout_temp_file))
}

#[tracing::instrument(name = "GENERATE REPORT", skip_all)]
Expand Down
1 change: 1 addition & 0 deletions apps/cargo-scout-audit/src/utils/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ fn get_config_file_path(bc: BlockChain) -> Result<PathBuf> {
let file_path = config_path.join(match bc {
BlockChain::Ink => "ink-config.json",
BlockChain::Soroban => "soroban-config.json",
BlockChain::SubstratePallet => "substrate-pallet-config.json",
});

Ok(file_path)
Expand Down
40 changes: 37 additions & 3 deletions apps/cargo-scout-audit/src/utils/detectors_info.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use anyhow::{anyhow, Result};
use libloading::{Library, Symbol};
use serde::Serialize;
use std::sync::Arc;
use std::{collections::HashMap, ffi::CString, path::PathBuf};

#[derive(Default, Debug, Clone)]
Expand All @@ -25,6 +26,11 @@ pub struct LintInfo {
pub vulnerability_class: String,
}

pub struct CustomLint<'lib> {
pub lib: Arc<Library>,
pub custom_detector: Symbol<'lib, CustomLintFunc>,
}

impl TryFrom<&RawLintInfo> for LintInfo {
type Error = anyhow::Error;

Expand All @@ -41,17 +47,37 @@ impl TryFrom<&RawLintInfo> for LintInfo {
}
}

impl<'lib> CustomLint<'lib> {
pub fn new(lib: Arc<Library>, custom_detector: Symbol<'lib, CustomLintFunc>) -> Self {
CustomLint {
lib,
custom_detector,
}
}

pub fn call(&self) {
unsafe {
(self.custom_detector)();
}
}
}

type LintInfoFunc = unsafe fn(info: &mut RawLintInfo);
type CustomLintFunc = unsafe fn();

#[tracing::instrument(level = "debug", skip_all)]
pub fn get_detectors_info(detectors_paths: &[PathBuf]) -> Result<HashMap<String, LintInfo>> {
pub fn get_detectors_info(
detectors_paths: &[PathBuf],
) -> Result<(HashMap<String, LintInfo>, HashMap<String, CustomLint<'_>>)> {
let mut lint_store = HashMap::new();
let mut custom_dectectors = HashMap::new();

for detector_path in detectors_paths {
let lib = unsafe {
Library::new(detector_path)
.map_err(|e| anyhow!("Failed to load library {}: {}", detector_path.display(), e))?
};
let lib = Arc::new(lib);

let lint_info_func: Symbol<LintInfoFunc> = unsafe {
lib.get(b"lint_info").map_err(|e| {
Expand All @@ -62,6 +88,8 @@ pub fn get_detectors_info(detectors_paths: &[PathBuf]) -> Result<HashMap<String,
)
})?
};
let custom_detector_func: Option<Symbol<CustomLintFunc>> =
unsafe { (*Arc::as_ptr(&lib)).get(b"custom_detector").ok() };

let mut raw_info = RawLintInfo::default();
unsafe { lint_info_func(&mut raw_info) };
Expand All @@ -74,8 +102,14 @@ pub fn get_detectors_info(detectors_paths: &[PathBuf]) -> Result<HashMap<String,
)
})?;

lint_store.insert(lint_info.id.clone(), lint_info);
let id = lint_info.id.clone();

lint_store.insert(id.clone(), lint_info);

if let Some(custom_detector_func) = custom_detector_func {
custom_dectectors.insert(id, CustomLint::new(lib, custom_detector_func));
}
}

Ok(lint_store)
Ok((lint_store, custom_dectectors))
}
9 changes: 5 additions & 4 deletions apps/cargo-scout-audit/tests/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,6 @@ mod tests {
assert!(result.is_ok(), "Scout should run");
let result = result.unwrap();

for finding in result.iter() {
dbg!(finding);
}
let findings = result
.iter()
.map(|value| {
Expand Down Expand Up @@ -289,7 +286,11 @@ mod tests {
let result = run_scout(scout_opts);

// Then
assert!(result.is_ok());
if result.is_err() {
let x = 0;
dbg!(result.err());
assert!(x == 1);
}
}
}
}
Loading