scanner: Add post-scan progress analysis

This commit is contained in:
David Wendt 2021-09-11 11:28:45 -04:00 committed by Mike Welsh
parent d08d2ec783
commit f7a4593ab4
5 changed files with 128 additions and 19 deletions

101
scanner/src/analyze.rs Normal file
View File

@ -0,0 +1,101 @@
//! Post-scan analysis
use crate::cli_options::AnalyzeOpt;
use crate::file_results::{FileResults, Step};
use std::cmp::max;
use std::fs::File;
/// Generate and print statistics related to a scan's results
pub fn analyze(results: impl Iterator<Item = FileResults>) {
let mut total = 0;
let mut start = 0;
let mut read = 0;
let mut decompress = 0;
let mut parse = 0;
let mut execute = 0;
let mut complete = 0;
for result in results {
total += 1;
match result.progress {
Step::Start => start += 1,
Step::Read => read += 1,
Step::Decompress => decompress += 1,
Step::Parse => parse += 1,
Step::Execute => execute += 1,
Step::Complete => complete += 1,
}
}
println!("Scanned {} swf files.", total);
let digits = max(
(start as f64).log10().ceil() as usize,
max(
(read as f64).log10().ceil() as usize,
max(
(decompress as f64).log10().ceil() as usize,
max(
(parse as f64).log10().ceil() as usize,
max(
(execute as f64).log10().ceil() as usize,
(complete as f64).log10().ceil() as usize,
),
),
),
),
) + 4;
println!();
if start > 0 {
println!(
"{:>digits$} movies panicked or crashed the scanner",
start,
digits = digits
);
}
println!(
"{:>digits$} movies failed when reading",
read,
digits = digits
);
println!(
"{:>digits$} movies failed to decompress",
decompress,
digits = digits
);
println!("{:>digits$} movies failed to parse", parse, digits = digits);
println!(
"{:>digits$} movies failed to execute",
execute,
digits = digits
);
println!(
"{:>digits$} movies completed without errors",
complete,
digits = digits
);
}
pub fn analyze_main(opt: AnalyzeOpt) -> Result<(), std::io::Error> {
let file = File::open(opt.input_path)?;
let reader = csv::Reader::from_reader(file);
analyze(reader.into_deserialize::<FileResults>().map(|r| {
match r {
Ok(fr) => fr,
Err(e) => {
// Treat unparseable CSV rows as a scanner panic
FileResults {
error: Some(format!("{}", e)),
..FileResults::default()
}
}
}
}));
Ok(())
}

View File

@ -15,6 +15,9 @@ pub enum Mode {
/// Scan an entire directory for SWF files
Scan(ScanOpt),
/// Analyze a previously executed scan and compile statistics on it
Analyze(AnalyzeOpt),
/// Execute a single SWF file and generate a machine-readable report
ExecuteReport(ExecuteReportOpt),
}
@ -34,6 +37,13 @@ pub struct ScanOpt {
pub ignore: Vec<String>,
}
#[derive(Clap, Debug)]
pub struct AnalyzeOpt {
/// The CSV file to reanalyze
#[clap(name = "input", parse(from_os_str))]
pub input_path: PathBuf,
}
#[derive(Clap, Debug)]
pub struct ExecuteReportOpt {
/// The single SWF file to parse and run

View File

@ -8,13 +8,13 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::fmt;
use std::fmt::Write;
#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum AvmType {
Avm1,
Avm2,
}
#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum Compression {
None,
Zlib,
@ -32,7 +32,7 @@ impl From<swf::Compression> for Compression {
}
/// A particular step in the scanner process.
#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum Step {
/// Nothing has been done yet.
///
@ -57,7 +57,7 @@ pub enum Step {
}
/// The result of a single scan.
#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct FileResults {
/// The file name scanned (including path).
pub name: String,

View File

@ -1,8 +1,10 @@
use crate::analyze::analyze_main;
use crate::cli_options::{Mode, Opt};
use crate::execute::execute_report_main;
use crate::scan::scan_main;
use clap::Clap;
mod analyze;
mod cli_options;
mod execute;
mod file_results;
@ -15,6 +17,7 @@ fn main() -> Result<(), std::io::Error> {
match opt.mode {
Mode::Scan(scan_opt) => scan_main(scan_opt),
Mode::Analyze(analyze_opt) => analyze_main(analyze_opt),
Mode::ExecuteReport(exeute_report_opt) => {
if execute_report_main(exeute_report_opt).is_err() {
// Do nothing.

View File

@ -1,5 +1,6 @@
//! Main/scanner process impls
use crate::analyze::analyze;
use crate::cli_options::ScanOpt;
use crate::file_results::FileResults;
use crate::ser_bridge::SerBridge;
@ -119,8 +120,6 @@ pub fn scan_main(opt: ScanOpt) -> Result<(), std::io::Error> {
let binary_path = env::current_exe()?;
let to_scan = find_files(&opt.input_path, &opt.ignore);
let total = to_scan.len() as u64;
let mut good = 0;
let mut bad = 0;
let progress = ProgressBar::new(total);
let mut writer = csv::Writer::from_path(opt.output_path.clone())?;
@ -169,22 +168,18 @@ pub fn scan_main(opt: ScanOpt) -> Result<(), std::io::Error> {
result
})
.ser_bridge();
.ser_bridge()
.map(|result| {
if let Err(e) = writer.serialize(result.clone()) {
eprintln!("{}", e);
};
for result in result_iter {
if result.error.is_none() {
good += 1;
} else {
bad += 1;
}
result
});
writer.serialize(result)?;
}
progress.finish();
progress.finish_with_message(format!(
"Scanned {} swf files. {} successfully parsed, {} encountered errors",
total, good, bad
));
analyze(result_iter);
Ok(())
}