Skip to content

Commit

Permalink
Merge pull request #136 from CoinFabrik/substrate-pallets
Browse files Browse the repository at this point in the history
Add Substrate pallet support
  • Loading branch information
Helios-vmg authored Oct 30, 2024
2 parents 3222649 + 1dd9227 commit ee885d0
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 27 deletions.
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);
}
}
}
}

0 comments on commit ee885d0

Please sign in to comment.