,
+}
+
+
+
+
+#[cfg(test)]
+mod tests {
+
+ use super::*;
+
+ #[test]
+ fn test_header() {
+
+ let header: Header = Header {
+ keys: vec![
+ Key {
+ value: "USERNAME".to_string(),
+ key: "qualysrs".to_string()
+ },
+ Key {
+ value: "TITLE".to_string(),
+ key: "VA - CAF".to_string()
+ }
+ ]
+ };
+
+ assert_eq!(
+ Scan { ips: vec![], headers: vec![header], value: "scan/1670238615.73708".to_string() },
+ from_str("
+
+
+
+ ").unwrap()
+ );
+
+ }
+
+
+ #[test]
+ fn test_ip() {
+
+ let xml = from_str(include_str!("..//files/ip_vuln.xml")).unwrap();
+
+ let vuln: Vuln = Vuln {
+ number: "38739".to_string(),
+ severity: Some("3".to_string()),
+ cveid: None,
+ standard_severity: None,
+
+ title: "Deprecated SSH Cryptographic Settings".to_string(),
+ last_update: Some("2021-05-26T11:40:40Z".to_string()),
+ cvss_base: Some("6.4".to_string()),
+ cvss_temporal: Some("4.7".to_string()),
+ cvss3_base: Some("6.5".to_string()),
+ cvss3_temporal: Some("5.3".to_string()),
+ cvss3_version: Some("3.1".to_string()),
+ pci_flag: Some("1".to_string()),
+ diagnosis: Some("The SSH [...] communicate.".to_string()),
+ consequence: Some("A man-in-the-middle [...].".to_string()),
+ instance: None,
+ vendor_reference_list: None,
+ cve_id_list: None,
+ bugtraq_id_list: None,
+ diagnosis_comment: None,
+ consequence_comment: None,
+ solution: Some("Avoid using [...] ".to_string()),
+ solution_comment: None,
+ compliance: None,
+ correlation: None,
+ result: Some("Type Namekey exchange diffie-hellman-group1-sha1".to_string()),
+ result_errors: None,
+ result_debug: None
+ };
+
+
+ let cat = Cat {
+ value: "General remote services".to_string(),
+ port: Some("22".to_string()),
+ protocol: Some("tcp".to_string()),
+ fqdn: None,
+ misc: None,
+ infos: vec![],
+ services: vec![],
+ practices: vec![],
+ vulns: vec![vuln],
+ };
+
+ let vulns = Vulns {
+ cats: vec![cat]
+ };
+
+ let ip: IP = IP {
+ value: "10.168.0.100".to_string(),
+ name: "No registered hostname".to_string(),
+
+ os: None,
+ os_cpe: None,
+ netbios_hostname: None,
+ infos: None,
+ services: None,
+ practices: None,
+ network: None,
+ vulns: Some(vulns)
+ };
+
+ let expected = Scan {
+ value: "scan/1670238615.73708".to_string(),
+ headers: vec![],
+ ips: vec![ip]
+ };
+
+ assert_eq!(expected, xml);
+ }
+
+ #[test]
+ fn test_parse() {
+ let reports = vec![
+ ("base", include_str!("../files/base.xml")),
+ ];
+
+ for (_name, report) in reports {
+ let _report = from_str(report);
+ // println!("report {:?}: {:?}", name, report);
+ assert!(true);
+ }
+ }
+
+}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..5d85457
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,70 @@
+use std::env;
+
+mod util;
+
+fn print_prg_info() {
+ let prg_info = format!("{} {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
+ let prg_authors = format!("(c) 2023 by {}", env!("CARGO_PKG_AUTHORS"));
+ let prg_description = format!("{}", env!("CARGO_PKG_DESCRIPTION"));
+ println!("{} {}", prg_info, prg_authors);
+ println!("{}", prg_description);
+ println!();
+}
+
+
+fn print_help() {
+ print_prg_info();
+ println!("Usage: qualysx [options]");
+ println!("Options:");
+ println!(" -h, --help\t\t\tPrint this help");
+ println!(" -v, --version\t\t\tPrint version information");
+ println!(" -x, --xml\t\t\tQualys xml to parse");
+ println!();
+}
+
+
+fn main() {
+ let args: Vec = env::args().collect();
+ let mut iter = args.iter();
+
+ let mut is_xml = false;
+ let mut xml: &str = "";
+
+ while let Some(arg) = iter.next() {
+ match arg.as_str() {
+ "-h" | "--help" => {
+ print_help();
+ std::process::exit(0);
+ }
+ "-v" | "--version" => {
+ print_prg_info();
+ std::process::exit(0);
+ }
+ "-x" | "--xml" => {
+ is_xml = true;
+ xml = iter.next().unwrap();
+ }
+ _ => {}
+ }
+ };
+
+ if !is_xml {
+ util::error("-x --xml is a mandatory parameter".to_string());
+ std::process::exit(1);
+
+ }
+
+ if !util::check_exist_file(xml) {
+ std::process::exit(1);
+ }
+
+ util::warn("Use with caution. You are responsible for your actions.".to_string());
+ util::warn("Developers assume no liability and are not responsible for any misuse or damage.".to_string());
+
+ let file: String = std::fs::read_to_string(xml).unwrap();
+
+ let scan: qualysx::Scan = qualysx::from_str(&file).unwrap();
+ let j = serde_json::to_string(&scan).unwrap();
+
+ println!("{}", j);
+}
\ No newline at end of file
diff --git a/src/util.rs b/src/util.rs
new file mode 100644
index 0000000..372f65a
--- /dev/null
+++ b/src/util.rs
@@ -0,0 +1,91 @@
+#![allow(dead_code)]
+#![allow(unused_variables)]
+
+use std::path::Path;
+use std::io::{self,BufRead, BufReader};
+use std::fs::File;
+use colored::Colorize;
+use atty::Stream;
+
+//////////////////////////
+// FILE LOCAL UTILITIES //
+//////////////////////////
+
+pub fn check_exist_file(filename: &str) -> bool {
+ if !Path::new(filename).is_file() {
+ error(format!("{} not found", filename));
+ return false;
+ }
+ true
+}
+
+pub fn read_file_to_vec(filename: &str) -> std::io::Result> {
+ let file = File::open(filename)?;
+ let reader = BufReader::new(file);
+ let mut lines = Vec::new();
+ for line in reader.lines() {
+ lines.push(line?);
+ }
+ Ok(lines)
+}
+
+
+pub fn read_file(path: &str) -> Result {
+ let file_path = Path::new(path);
+ File::open(file_path)
+}
+
+//////////////////////////
+// FILE REMOTE UTILITIES //
+//////////////////////////
+
+pub fn dwnld_from_url(url: &str) -> Result> {
+ let resp = reqwest::blocking::get(url)?;
+ Ok(resp)
+}
+
+pub fn get_stdio_lines() -> Vec {
+ let stdin = io::stdin();
+ let lines = stdin.lock().lines();
+ let mut lines_vec = Vec::new();
+ lines.into_iter().for_each(|line| match line {
+ Ok(line) => {
+ if line.starts_with("CVE") {
+ lines_vec.push(line);
+ }
+ }
+ Err(_) => {
+ error("Error reading line from stdin".to_string());
+ dbg!();
+ std::process::exit(1);
+ }
+ });
+ lines_vec
+}
+
+pub fn check_for_stdin() {
+ if atty::is(Stream::Stdin) {
+ //print_help();
+ std::process::exit(0);
+ }
+}
+
+pub fn write_vec_as_stdout(input: &[String]) {
+ input.iter().for_each(|x| println!("{x}"));
+}
+
+pub fn debug(msg: String) {
+ eprintln!("{} {}", "[DBG]".blue(), msg);
+}
+
+pub fn info(msg: String) {
+ eprintln!("{} {}", "[INF]".blue(), msg);
+}
+
+pub fn warn(msg: String) {
+ eprintln!("{} {}", "[WRN]".yellow(), msg);
+}
+
+pub fn error(msg: String) {
+ eprintln!("{} {}", "[ERR]".magenta (), msg);
+}
\ No newline at end of file