use std::collections::HashMap;
use regex::Regex;
use crate::analysis::analyzer::Analyzer;
use crate::analysis::result::url_analysis_result::UrlAnalysisResult;
use crate::output::output::Output;
use crate::result::manager_stats::ManagerStats;
use crate::result::status::Status;
use crate::result::visited_url::VisitedUrl;
use crate::utils;
pub const SUPER_TABLE_ANALYSIS_STATS: &str = "analysis-stats";
pub struct AnalysisManager {
analyzers: Vec<Box<dyn Analyzer>>,
stats: ManagerStats,
}
impl AnalysisManager {
pub fn new() -> Self {
Self {
analyzers: Vec::new(),
stats: ManagerStats::new(),
}
}
pub fn register_analyzer(&mut self, analyzer: Box<dyn Analyzer>) {
self.analyzers.push(analyzer);
}
pub fn auto_activate_analyzers(&mut self) {
self.analyzers.retain(|a| a.should_be_activated());
}
pub fn filter_analyzers_by_regex(&mut self, filter_regex: &str) {
let pattern = utils::extract_pcre_regex_pattern(filter_regex);
if let Ok(re) = Regex::new(&pattern) {
self.analyzers.retain(|a| re.is_match(a.get_name()));
}
}
pub fn analyze_visited_url(
&mut self,
visited_url: &VisitedUrl,
body: Option<&str>,
headers: Option<&HashMap<String, String>>,
status: &Status,
) -> Vec<(String, UrlAnalysisResult)> {
let mut results = Vec::new();
for analyzer in &mut self.analyzers {
if let Some(result) = analyzer.analyze_visited_url(visited_url, body, headers) {
let name = analyzer.get_name().to_string();
status.add_url_analysis_result(
&visited_url.uq_id,
crate::result::status::UrlAnalysisResultEntry {
analysis_name: name.clone(),
result: result.clone(),
},
);
results.push((name, result));
}
}
results
}
pub fn run_analyzers(&mut self, status: &Status, output: &mut dyn Output) {
if status.get_number_of_working_visited_urls() == 0 {
let error_message =
"The analysis has been suspended because no working URL could be found. Please check the URL/domain.";
output.add_error(error_message);
status.add_critical_to_summary("analysis-manager-error", error_message);
return;
}
self.analyzers.sort_by_key(|a| a.get_order());
for analyzer in &mut self.analyzers {
analyzer.analyze(status, output);
}
if !self.analyzers.is_empty() {
let mut all_exec_times: HashMap<String, f64> = HashMap::new();
let mut all_exec_counts: HashMap<String, usize> = HashMap::new();
for analyzer in &self.analyzers {
for (key, time) in analyzer.get_exec_times() {
*all_exec_times.entry(key.clone()).or_insert(0.0) += time;
}
for (key, count) in analyzer.get_exec_counts() {
*all_exec_counts.entry(key.clone()).or_insert(0) += count;
}
}
let super_table = self.stats.get_super_table(
SUPER_TABLE_ANALYSIS_STATS,
"Analysis stats",
"No analysis stats",
Some(&all_exec_times),
Some(&all_exec_counts),
);
let mut super_table = super_table;
status.configure_super_table_url_stripping(&mut super_table);
output.add_super_table(&super_table);
status.add_super_table_at_end(super_table);
}
}
pub fn get_analyzers(&self) -> &[Box<dyn Analyzer>] {
&self.analyzers
}
pub fn has_analyzer(&self, name: &str) -> bool {
self.analyzers.iter().any(|a| a.get_name() == name)
}
pub fn get_extra_columns(&self) -> Vec<crate::extra_column::ExtraColumn> {
self.analyzers
.iter()
.filter_map(|a| a.show_analyzed_visited_url_result_as_column())
.collect()
}
pub fn get_analysis_column_values(
&self,
analysis_results: &[(String, UrlAnalysisResult)],
) -> HashMap<String, String> {
let mut result = HashMap::new();
for analyzer in &self.analyzers {
if let Some(extra_col) = analyzer.show_analyzed_visited_url_result_as_column() {
let analyzer_name = analyzer.get_name();
if let Some((_, url_result)) = analysis_results.iter().find(|(name, _)| name == analyzer_name) {
let colorized = url_result.to_colorized_string(true);
if !colorized.is_empty() {
result.insert(extra_col.name.clone(), colorized);
}
}
}
}
result
}
}
impl Default for AnalysisManager {
fn default() -> Self {
Self::new()
}
}