use std::collections::{HashMap, HashSet}; use std::str::FromStr; use cvrf_xmlparser::{Severity, CUSA}; use lazy_static::lazy_static; use rpm_rs::rpm::{get_installed_packages, Package}; use rpm_rs::rpmio::rpmvercmp; use updateinfo_xmlparser::{RpmInfo, UpdateInfoDb}; use crate::analyzer::db::{CveDb, PackgeDb, SaDb}; use crate::cli::Cli; lazy_static! { pub static ref update_pkgs: PackgeDb = { let mut updatedb = UpdateInfoDb::new(); updatedb.load_xml("test/updateinfo.xml").unwrap(); let mut pkgdb = PackgeDb::new(); pkgdb.load_from_updateinfodb(&updatedb); pkgdb }; pub static ref sadb: SaDb = { let mut _sadb = SaDb::new(); _sadb.load_from_file("test/cvrf_sainfos.json").unwrap(); _sadb }; pub static ref cvedb: CveDb = sadb.get_cvedb(); } pub mod db; struct Reporter { // 已修复的安全更新 fixed: HashMap>, // 可用的安全更新 avaliable: HashMap>, } impl Reporter { // 新建一个空的 Reporter pub fn new() -> Self { let fixed = HashMap::new(); let avaliable = HashMap::new(); Self { fixed, avaliable } } // get fixed, 不可写 pub fn get_fixed(&self) -> &HashMap> { &self.fixed } // get avaliable, 只读 pub fn get_avaliable(&self) -> &HashMap> { &self.avaliable } // 添加包到已修复 pub fn add_to_fixed(&mut self, cusa: &CUSA, rpm: RpmInfo) { // 可以找到则直接 insert if let Some(rpms) = self.fixed.get_mut(cusa) { rpms.insert(rpm); } else { let mut rpms = HashSet::new(); rpms.insert(rpm); self.fixed.insert(cusa.clone(), rpms); } } // 添加包到可用更新 pub fn add_to_avaliable(&mut self, cusa: &CUSA, rpm: RpmInfo) { // 可以找到则直接 insert if let Some(rpms) = self.avaliable.get_mut(cusa) { rpms.insert(rpm); } else { let mut rpms = HashSet::new(); rpms.insert(rpm); self.avaliable.insert(cusa.clone(), rpms); } } } pub fn cuvat_run(cli: &Cli) -> crate::Result<()> { // 只报告 cve 相关 if cli.cves { return list_cves(cli); } // 只报告 sa 相关 if cli.sas { return list_sas(cli); } // 生成报告 if cli.report { return repoter(cli); } // 默认报告格式 summary(cli) } fn list_cves(cli: &Cli) -> crate::Result<()> { let reporter = _reporter()?; let avaliable = reporter.get_avaliable(); let severity = Severity::from_str(&cli.severity)?; let mut _cves = HashSet::new(); cli.sources.iter().for_each(|id| { _cves.insert(id); }); let mut cves = HashSet::new(); let mut msg = String::new(); // 仅针对可用更新 for (cusa, _) in avaliable { // 过滤 if cusa.severity() < &severity { continue; } cusa.cves().iter().for_each(|cve| { if let Some(_) = _cves.get(&cve.id) { let _ = cves.insert(cve.clone()); _cves.remove(&cve.id); } }); } for cve in &cves { if cli.info { msg = format!("{msg}\n\n{cve:#?}"); } else { msg = format!("{msg}\n{}", cve.id); } } if !_cves.is_empty() { msg = format!("{msg}\n\nUnaffected CVEs:\n{_cves:#?}") } println!("{msg}"); Ok(()) } fn list_sas(cli: &Cli) -> crate::Result<()> { let reporter = _reporter()?; let avaliable = reporter.get_avaliable(); let severity = Severity::from_str(&cli.severity)?; let mut _sas = HashSet::new(); cli.sources.iter().for_each(|id| { _sas.insert(id); }); let mut sas = HashSet::new(); let mut msg = String::new(); // 仅针对可用更新 for (cusa, _) in avaliable { // 过滤 if cusa.severity() < &severity { continue; } if let Some(_) = _sas.get(&cusa.id) { let _ = sas.insert(cusa.clone()); _sas.remove(&cusa.id); } } for sa in &sas { if cli.info { msg = format!("{msg}\n\n{sa:#?}"); } else { msg = format!("{msg}\n{}", sa.id); } } if !_sas.is_empty() { msg = format!("{msg}\n\nUnaffected SAs:\n{_sas:#?}") } println!("{msg}"); Ok(()) } // 最为详尽的报告,包括当前系统中软件包所涉及到,已修复和可用但未修复 // 的所有软件包的列表。 fn repoter(cli: &Cli) -> crate::Result<()> { let reporter = _reporter()?; let fixed = reporter.get_fixed(); let avaliable = reporter.get_avaliable(); let _ = fixed; let _ = avaliable; println!("TODO..."); Ok(()) } fn summary(cli: &Cli) -> crate::Result<()> { let reporter = _reporter()?; let avaliable = reporter.get_avaliable(); let severity = Severity::from_str(&cli.severity)?; let mut total = 0; let mut res = vec![0; 5]; let mut lists = vec![vec![]; 5]; let mut msg = String::new(); for (cusa, _) in avaliable { // 过滤 if cusa.severity() < &severity { continue; } let pos: usize = cusa.severity().into(); total += 1; res[pos] += 1; lists[pos].push(cusa); } // 如果需要列出详细的 sa if cli.list { msg = format!("当前系统未修复的公告列表(共 {total} 个):\n"); for sas in &lists { for sa in sas { msg = format!("{msg}\n{:>4}{}: {:?}", "", sa.id(), sa.severity()); } } //msg = format!("{msg}\n\n"); } else { msg = format!( "{msg} 本次安全检查共扫描出 {} 个漏洞: 致命漏洞:{:>3} 个 高危漏洞:{:>3} 个 中危漏洞:{:>3} 个 低危漏洞:{:>3} 个 ", total, res[4], res[3], res[2], res[1] ); } println!("{msg}"); Ok(()) } fn _reporter() -> crate::Result { let mut reporter = Reporter::new(); // 当前系统所有已安装的 rpm 包 let installed = get_installed_packages(); // installed 已被消费掉 // latest_installed 是所有最新版本软件包的 Vec let latest_installed: Vec = installed .into_iter() .map(|(_, pkgs)| { let mut latest = pkgs[0].clone(); for pkg in pkgs.into_iter() { latest = rpmdb_package_vercmp(latest, pkg); } latest }) .collect(); // 获取 sa 更新列表 for pkg in latest_installed { let name = pkg.name(); if let Some(updates) = update_pkgs.db().get(name) { for update in updates { // 因 updateinfo 里包含所有架构,故于此做个判断 // 是否考虑架构信息在不同的版本有所不同? if update.arch() != pkg.arch().unwrap() { continue; } let sainfo = sadb.db().get(update.sa()).unwrap(); // epoch 判断 match (update.epoch(), pkg.epoch()) { (Some(_), None) => { reporter.add_to_avaliable(&sainfo, update.clone()); } (None, Some(_)) => { reporter.add_to_fixed(&sainfo, update.clone()); } _ => {} } // evr 对比 if rpmvercmp(&update.evr(), &pkg.evr()) > 0 { reporter.add_to_avaliable(&sainfo, update.clone()); } else { reporter.add_to_fixed(&sainfo, update.clone()); } } } } Ok(reporter) } // 对比两个 rpm Package 的版本,返回最新的一个 fn rpmdb_package_vercmp(pa: Package, pb: Package) -> Package { // 首先进行 epoch 的比较 match (pa.epoch(), pb.epoch()) { (Some(_), None) => return pa, (None, Some(_)) => return pb, _ => {} // 继续往下对比 } if rpmvercmp(&pa.evr(), &pb.evr()) > 0 { return pa; } else { return pb; } }