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

Implementing detectors #95

Draft
wants to merge 1 commit into
base: brock/no_recurse
Choose a base branch
from
Draft
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
12 changes: 12 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ tracing-subscriber.workspace = true
tracing-tree.workspace = true
petgraph.workspace = true
ethers-core.workspace = true
solang-parser.workspace = true

clap = { version = "4.1.4", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
Expand Down
41 changes: 41 additions & 0 deletions crates/cli/src/detectors/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use pyrometer::detector::{Detector, DetectorResult};
use pyrometer::Analyzer;
use pyrometer::reporter::{ReportFormat, get_reporter};

pub mod my_detector;
pub mod second_detector;

// Import other detector modules here

pub fn get_all_detectors() -> Vec<Box<dyn Detector>> {
vec![
Box::new(my_detector::MyDetector),
Box::new(second_detector::SecondDetector),
// Add more detectors here as you create them
// Box::new(your_detector::YourDetector),
// Box::new(his_detector::HisDetector),
]
}

pub fn find_detector(name: &str) -> Option<Box<dyn Detector>> {
get_all_detectors().into_iter().find(|d| d.name() == name)
}

pub fn run_detectors(analyzer: &Analyzer, detector_names: &[String]) -> Vec<DetectorResult> {
let mut results = Vec::new();

for name in detector_names {
if let Some(detector) = find_detector(name) {
results.extend(detector.run(analyzer));
} else {
println!("Unknown detector: {}", name);
}
}

results
}

pub fn report_results(results: &[DetectorResult], format: ReportFormat) {
let reporter = get_reporter(format);
reporter.report(results);
}
38 changes: 38 additions & 0 deletions crates/cli/src/detectors/my_detector.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// crates/cli/src/detectors/my_detector.rs
use pyrometer::detector::{Confidence, Detector, DetectorResult, Severity};
use pyrometer::Analyzer;
use solang_parser::pt::Loc;

pub struct MyDetector;

impl Detector for MyDetector {
fn name(&self) -> &'static str {
"MyDetector"
}

fn description(&self) -> String {
"Detects a specific pattern in the code".to_string()
}

fn severity(&self) -> Severity {
Severity::Medium
}

fn confidence(&self) -> Confidence {
Confidence::Medium
}

fn run(&self, analyzer: &Analyzer) -> Vec<DetectorResult> {
// Implement detection logic here
let mut results = vec![];
results.push(DetectorResult {
issue_name: "test_issue".to_string(),
location: Loc::File(0, 0, 100),
detector_name: self.name().to_string(),
severity: self.severity(),
confidence: self.confidence(),
message: "This is a test message".to_string(),
});
results
}
}
28 changes: 28 additions & 0 deletions crates/cli/src/detectors/second_detector.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// crates/cli/src/detectors/second_detector.rs
use pyrometer::detector::{Detector, DetectorResult, Severity, Confidence};
use pyrometer::Analyzer;

pub struct SecondDetector;

impl Detector for SecondDetector {
fn name(&self) -> &'static str {
"SecondDetector"
}

fn description(&self) -> String {
"Detects a second pattern in the code".to_string()
}

fn severity(&self) -> Severity {
Severity::Medium
}

fn confidence(&self) -> Confidence {
Confidence::Medium
}

fn run(&self, analyzer: &Analyzer) -> Vec<DetectorResult> {
// Implement detection logic here
vec![]
}
}
40 changes: 39 additions & 1 deletion crates/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use shared::{post_to_site, Search};
use shared::{GraphDot, USE_DEBUG_SITE};

use ariadne::sources;
use clap::{ArgAction, Parser, ValueHint};
use clap::{ArgAction, Parser, ValueHint, Subcommand};

use tracing::{error, trace};
use tracing_subscriber::{prelude::*, Registry};
Expand All @@ -22,10 +22,17 @@ use std::{
path::PathBuf,
};
use tokio::runtime::Runtime;
mod detectors;
use detectors::{get_all_detectors, run_detectors};
use detectors::my_detector::MyDetector;
use pyrometer::reporter::ReportFormat;
use crate::detectors::report_results;

#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
#[command(subcommand)]
command: Option<Commands>,
/// The path to the solidity file to process
#[clap(value_hint = ValueHint::FilePath, value_name = "PATH")]
pub path: String,
Expand Down Expand Up @@ -115,6 +122,16 @@ struct Args {
pub debug_stack: bool,
}

#[derive(Subcommand, Debug)]
enum Commands {
Detect {
// --detectors MyDetector,SecondDetector | --detectors="MyDetector,SecondDetector"
#[clap(long, value_delimiter = ',')]
detectors: Option<Vec<String>>,
},
ListDetectors,
}

pub fn subscriber() {
tracing_subscriber::Registry::default()
.with(tracing_subscriber::filter::EnvFilter::from_default_env())
Expand Down Expand Up @@ -569,4 +586,25 @@ fn main() {
// }
// println!();
// });

match args.command {
Some(Commands::Detect { detectors }) => {
// add in * stuff
let detector_names = detectors.unwrap_or_else(|| {
println!("No detectors specified. Running all detectors...");
get_all_detectors().iter().map(|d| d.name().to_string()).collect()
});

println!("Running detectors: {:?}", detector_names);
let results = run_detectors(&analyzer, &detector_names);
report_results(&results, ReportFormat::Stdout);
},
Some(Commands::ListDetectors) => {
println!("Available detectors:");
for detector in get_all_detectors() {
println!("- {} : {}", detector.name(), detector.description());
}
},
None => (),
}
}
1 change: 1 addition & 0 deletions crates/pyrometer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ serde_json = "1"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
reqwest = { version = "0.12", features = ["json"] }
colored = "2.0"



Expand Down
88 changes: 88 additions & 0 deletions crates/pyrometer/src/detector.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
use crate::Analyzer;
use serde::{Deserialize, Serialize};
use solang_parser::pt::Loc;

/// Represents the severity of an issue detected in the code.
///
/// The severity levels are ordered from least to most severe:
/// Informational < Low < Medium < High
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum Severity {
/// Informational issues are not problematic but might be of interest.
Informational,
/// Low severity issues pose minimal risk.
Low,
/// Medium severity issues pose moderate risk and should be addressed.
Medium,
/// High severity issues pose significant risk and require attention.
High,
}

/// Indicates the level of confidence in the accuracy of a detected issue.
///
/// The confidence levels are ordered from least to most certain:
/// Low < Medium < High < Certain
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum Confidence {
/// Low confidence suggests a high chance of false positives.
Low,
/// Medium confidence indicates a moderate level of certainty.
Medium,
/// High confidence suggests a low chance of false positives.
High,
/// Certain confidence indicates that the issue is definitely present.
Certain,
}

/// Represents the result of a detector finding an issue in the code.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DetectorResult {
/// The name or title of the detected issue.
pub issue_name: String,
/// The name of the detector that found the issue.
pub detector_name: String,
/// The location in the source code where the issue was detected.
pub location: Loc,
/// A detailed description of the detected issue.
pub message: String,
/// A list of details about the issue.
// pub details: Vec<DetectorDetail>,
/// The severity level of the detected issue.
pub severity: Severity,
/// The confidence level in the accuracy of the detected issue.
pub confidence: Confidence,
}


impl DetectorResult {}

pub struct DetectorDetail {
pub loc: Loc,
pub description: String,
}

/// Defines the interface for implementing a code pattern detector.
pub trait Detector {
/// Returns the name of the detector.
fn name(&self) -> &'static str;

/// Provides a description of what the detector looks for.
fn description(&self) -> String;

/// Returns the severity level of issues found by this detector.
fn severity(&self) -> Severity;

/// Returns the confidence level of issues found by this detector.
fn confidence(&self) -> Confidence;

/// Executes the detector on the provided analyzer and returns a list of detected issues.
///
/// # Arguments
///
/// * `analyzer` - A reference to the Analyzer containing the code to be analyzed.
///
/// # Returns
///
/// A vector of `DetectorResult` instances, each representing a detected issue.
fn run(&self, analyzer: &Analyzer) -> Vec<DetectorResult>;
}
2 changes: 2 additions & 0 deletions crates/pyrometer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@ mod analyzer;
mod analyzer_backend;
mod builtin_fns;
pub mod graph_backend;
pub mod detector;
pub mod reporter;

pub use analyzer::*;
Loading