Skip to content
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
2 changes: 2 additions & 0 deletions src/confidence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ mod tests {
engine_confidences: confidence_list,
evidence_chain: vec![],
source: "test".into(),
start_line: None,
end_line: None,
}
}

Expand Down
6 changes: 6 additions & 0 deletions src/engines/ir_engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ fn detect_missing_access_control(
}],
evidence_chain: vec![],
source: "ir_access_control".to_string(),
start_line: Some(func.start_line),
end_line: Some(func.end_line),
});
} else if has_external_call(&func.source) {
output.candidates.push(Candidate {
Expand Down Expand Up @@ -232,6 +234,8 @@ fn detect_dangerous_delegatecall(
}],
evidence_chain: vec![],
source: "ir_delegatecall".to_string(),
start_line: Some(func.start_line),
end_line: Some(func.end_line),
});
}

Expand Down Expand Up @@ -410,6 +414,8 @@ fn detect_price_manipulation(ir: &RawIr, filepath: &str, contest_name: &str) ->
}],
evidence_chain: vec![],
source: "ir_oracle".to_string(),
start_line: Some(func.start_line),
end_line: Some(func.end_line),
});
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/engines/sa_engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ pub fn classify(
}],
evidence_chain: vec![],
source: "sa_hard".to_string(),
start_line: Some(finding.start_line),
end_line: Some(finding.end_line),
});
} else if config::is_soft(&finding.slug) {
let vuln_type = config::slug_to_type(&finding.slug).unwrap_or(VulnType::Other);
Expand Down
2 changes: 2 additions & 0 deletions src/engines/symbolic_engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@ pub fn verify(
}],
evidence_chain: evidence,
source: format!("symbolic_{}", candidate.source),
start_line: Some(candidate.start_line),
end_line: Some(candidate.end_line),
});
}
Some(Disposition::Killed) => {
Expand Down
2 changes: 2 additions & 0 deletions src/engines/taint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,8 @@ fn analyze_function(
}],
evidence_chain: vec![format!("{} -> {}", tv.source, sink)],
source: "taint".to_string(),
start_line: Some(func.start_line),
end_line: Some(func.end_line),
});
}
TaintSink::StorageWrite | TaintSink::TransferValue => {
Expand Down
3 changes: 3 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,7 @@ pub enum AnalysisError {

#[error("provide --contest <path> or --batch")]
MissingCommand,

#[error("report generation failed: {0}")]
Report(String),
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ pub mod path_utils;
pub mod pipeline;
pub mod postprocess;
pub mod prompts;
pub mod report;
pub mod score;
pub mod types;
35 changes: 19 additions & 16 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use vuln_analyzer::confidence::ConfidenceScorer;
use vuln_analyzer::error::AnalysisError;
use vuln_analyzer::llm::LlmClient;
use vuln_analyzer::pipeline::{self, PipelineConfig};
use vuln_analyzer::report::OutputFormat;

#[derive(Parser)]
#[command(
Expand Down Expand Up @@ -63,6 +64,14 @@ struct Cli {

#[arg(long, help = "Path to confidence weights JSON file")]
confidence_weights: Option<String>,

#[arg(
long,
default_value = "legacy",
value_enum,
help = "Output format: legacy, json, html"
)]
output_format: OutputFormat,
}

#[derive(Clone, clap::ValueEnum)]
Expand All @@ -71,20 +80,6 @@ enum LogFormat {
Json,
}

fn write_results(
results: &[pipeline::ContestResult],
output_dir: Option<&Path>,
) -> Result<(), vuln_analyzer::error::AnalysisError> {
match output_dir {
Some(dir) => pipeline::write_output_to_dir(results, dir),
None => {
let json = pipeline::serialize_predictions(results)?;
println!("{json}");
Ok(())
}
}
}

#[tokio::main]
async fn main() -> Result<()> {
dotenvy::dotenv().ok();
Expand Down Expand Up @@ -159,7 +154,11 @@ async fn main() -> Result<()> {
let dataset_dir = PathBuf::from(cli.dataset.as_deref().unwrap_or(&default_dataset));
let r = pipeline::run_batch(&dataset_dir, &client, &config, &cancel).await?;
if !cli.score {
write_results(&r, config.output_dir.as_deref())?;
vuln_analyzer::report::write_report(
&r,
cli.output_format,
config.output_dir.as_deref(),
)?;
}
log_batch_summary(&r, start.elapsed());
r
Expand All @@ -168,7 +167,11 @@ async fn main() -> Result<()> {
let r = pipeline::run_contest(&contest_path, &client, &config, &cancel, None).await?;
let results = vec![r];
if !cli.score {
write_results(&results, config.output_dir.as_deref())?;
vuln_analyzer::report::write_report(
&results,
cli.output_format,
config.output_dir.as_deref(),
)?;
}
results
} else {
Expand Down
3 changes: 3 additions & 0 deletions src/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ pub struct PipelineStats {
#[derive(Debug)]
pub struct ContestResult {
pub contest_name: String,
pub contest_path: PathBuf,
pub predictions: Vec<Prediction>,
pub elapsed: std::time::Duration,
pub stats: PipelineStats,
Expand Down Expand Up @@ -237,6 +238,7 @@ pub async fn run_contest(
config.scorer.apply(&mut deduped);
return Ok(ContestResult {
contest_name,
contest_path: contest_path.to_path_buf(),
predictions: deduped,
elapsed: start.elapsed(),
stats,
Expand Down Expand Up @@ -397,6 +399,7 @@ pub async fn run_contest(

Ok(ContestResult {
contest_name,
contest_path: contest_path.to_path_buf(),
predictions: deduped,
elapsed: start.elapsed(),
stats,
Expand Down
6 changes: 5 additions & 1 deletion src/postprocess/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ pub fn validate_finding(
Some(Value::String(s)) => s.parse::<f64>().unwrap_or(0.7),
_ => 0.7,
};
let normalized = if raw_confidence > 1.5 {
let normalized = if raw_confidence > 1.0 {
raw_confidence / 10.0
} else {
raw_confidence.clamp(0.0, 1.0)
Expand Down Expand Up @@ -140,6 +140,8 @@ pub fn validate_finding(
}],
evidence_chain: vec![],
source: String::new(),
start_line: None,
end_line: None,
})
}

Expand Down Expand Up @@ -427,6 +429,8 @@ mod tests {
engine_confidences: vec![],
evidence_chain: vec![],
source: String::new(),
start_line: None,
end_line: None,
}
}
}
Loading
Loading