scanner: Add post-scan progress analysis
This commit is contained in:
parent
d08d2ec783
commit
f7a4593ab4
|
@ -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(())
|
||||||
|
}
|
|
@ -15,6 +15,9 @@ pub enum Mode {
|
||||||
/// Scan an entire directory for SWF files
|
/// Scan an entire directory for SWF files
|
||||||
Scan(ScanOpt),
|
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
|
/// Execute a single SWF file and generate a machine-readable report
|
||||||
ExecuteReport(ExecuteReportOpt),
|
ExecuteReport(ExecuteReportOpt),
|
||||||
}
|
}
|
||||||
|
@ -34,6 +37,13 @@ pub struct ScanOpt {
|
||||||
pub ignore: Vec<String>,
|
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)]
|
#[derive(Clap, Debug)]
|
||||||
pub struct ExecuteReportOpt {
|
pub struct ExecuteReportOpt {
|
||||||
/// The single SWF file to parse and run
|
/// The single SWF file to parse and run
|
||||||
|
|
|
@ -8,13 +8,13 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub enum AvmType {
|
pub enum AvmType {
|
||||||
Avm1,
|
Avm1,
|
||||||
Avm2,
|
Avm2,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub enum Compression {
|
pub enum Compression {
|
||||||
None,
|
None,
|
||||||
Zlib,
|
Zlib,
|
||||||
|
@ -32,7 +32,7 @@ impl From<swf::Compression> for Compression {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A particular step in the scanner process.
|
/// A particular step in the scanner process.
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub enum Step {
|
pub enum Step {
|
||||||
/// Nothing has been done yet.
|
/// Nothing has been done yet.
|
||||||
///
|
///
|
||||||
|
@ -57,7 +57,7 @@ pub enum Step {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The result of a single scan.
|
/// The result of a single scan.
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct FileResults {
|
pub struct FileResults {
|
||||||
/// The file name scanned (including path).
|
/// The file name scanned (including path).
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
use crate::analyze::analyze_main;
|
||||||
use crate::cli_options::{Mode, Opt};
|
use crate::cli_options::{Mode, Opt};
|
||||||
use crate::execute::execute_report_main;
|
use crate::execute::execute_report_main;
|
||||||
use crate::scan::scan_main;
|
use crate::scan::scan_main;
|
||||||
use clap::Clap;
|
use clap::Clap;
|
||||||
|
|
||||||
|
mod analyze;
|
||||||
mod cli_options;
|
mod cli_options;
|
||||||
mod execute;
|
mod execute;
|
||||||
mod file_results;
|
mod file_results;
|
||||||
|
@ -15,6 +17,7 @@ fn main() -> Result<(), std::io::Error> {
|
||||||
|
|
||||||
match opt.mode {
|
match opt.mode {
|
||||||
Mode::Scan(scan_opt) => scan_main(scan_opt),
|
Mode::Scan(scan_opt) => scan_main(scan_opt),
|
||||||
|
Mode::Analyze(analyze_opt) => analyze_main(analyze_opt),
|
||||||
Mode::ExecuteReport(exeute_report_opt) => {
|
Mode::ExecuteReport(exeute_report_opt) => {
|
||||||
if execute_report_main(exeute_report_opt).is_err() {
|
if execute_report_main(exeute_report_opt).is_err() {
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
//! Main/scanner process impls
|
//! Main/scanner process impls
|
||||||
|
|
||||||
|
use crate::analyze::analyze;
|
||||||
use crate::cli_options::ScanOpt;
|
use crate::cli_options::ScanOpt;
|
||||||
use crate::file_results::FileResults;
|
use crate::file_results::FileResults;
|
||||||
use crate::ser_bridge::SerBridge;
|
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 binary_path = env::current_exe()?;
|
||||||
let to_scan = find_files(&opt.input_path, &opt.ignore);
|
let to_scan = find_files(&opt.input_path, &opt.ignore);
|
||||||
let total = to_scan.len() as u64;
|
let total = to_scan.len() as u64;
|
||||||
let mut good = 0;
|
|
||||||
let mut bad = 0;
|
|
||||||
let progress = ProgressBar::new(total);
|
let progress = ProgressBar::new(total);
|
||||||
let mut writer = csv::Writer::from_path(opt.output_path.clone())?;
|
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
|
result
|
||||||
})
|
})
|
||||||
.ser_bridge();
|
.ser_bridge()
|
||||||
|
.map(|result| {
|
||||||
|
if let Err(e) = writer.serialize(result.clone()) {
|
||||||
|
eprintln!("{}", e);
|
||||||
|
};
|
||||||
|
|
||||||
for result in result_iter {
|
result
|
||||||
if result.error.is_none() {
|
});
|
||||||
good += 1;
|
|
||||||
} else {
|
|
||||||
bad += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.serialize(result)?;
|
progress.finish();
|
||||||
}
|
|
||||||
|
|
||||||
progress.finish_with_message(format!(
|
analyze(result_iter);
|
||||||
"Scanned {} swf files. {} successfully parsed, {} encountered errors",
|
|
||||||
total, good, bad
|
|
||||||
));
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue