#![forbid(unsafe_code)]
pub use crate::lib_utils::run_utils as utils;
pub use cyclonedx_bom;
pub mod files_proc {
pub mod model {
pub mod file_ident;
pub mod files_pending_proc;
pub mod input_file_type;
}
pub mod processor;
pub mod traits;
}
pub mod pdf {
pub mod font_config;
pub mod generator;
}
pub mod lib_utils {
#[cfg(feature = "cli")]
pub mod cli_args;
pub mod config;
pub mod env_vars;
pub mod errors;
pub mod run_utils;
}
use crate::files_proc::processor::DefaultFilesProcessor;
use crate::files_proc::traits::{FileSearchProvider, MultipleFilesProcProvider};
use crate::lib_utils::errors::Vex2PdfError;
use lib_utils::config::Config;
pub fn run(config: Config) -> Result<(), Vex2PdfError> {
let _ = DefaultFilesProcessor::new(config).find_files()?.process();
Ok(())
}
pub fn show_full_licenses() {
let main_license_text = r#"VEX2PDF is licensed under either MIT or Apache License, Version 2.0 at your option.
license text can be found under: https://gitlab.com/jurassicLizard/vex2pdf/-/blob/master/README.md#license"#;
println!("{main_license_text}");
println!();
println!("-----------------------------------------------------------------------------\n");
println!("This software makes use of Liberation Fonts licensed under SIL as follows : ");
println!();
let sil_license_text = include_bytes!("../external/fonts/liberation-fonts/LICENSE");
println!("{}", String::from_utf8_lossy(sil_license_text));
}
#[cfg(test)]
mod tests {
use cyclonedx_bom::models::bom::Bom;
use cyclonedx_bom::models::metadata::Metadata;
use cyclonedx_bom::models::tool::{Tool, Tools};
use cyclonedx_bom::models::vulnerability::{Vulnerabilities, Vulnerability};
use cyclonedx_bom::models::vulnerability_rating::{
Score, ScoreMethod, Severity, VulnerabilityRating, VulnerabilityRatings,
};
use cyclonedx_bom::prelude::{DateTime, NormalizedString, SpecVersion, UrnUuid};
use std::fs;
use std::io::BufReader;
fn create_sample_vex() -> Bom {
Bom {
spec_version: SpecVersion::V1_5,
version: 1,
serial_number: Some(
UrnUuid::new("urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79".to_string())
.expect("Unable to create urn:uuid"),
),
metadata: Some(Metadata {
timestamp: Some(DateTime::now().expect("failed to convert date")),
tools: Some(Tools::List(vec![Tool {
name: Some(NormalizedString::new("my_tool")),
..Tool::default()
}])),
..Metadata::default()
}),
vulnerabilities: Some(Vulnerabilities(vec![
Vulnerability {
bom_ref: None,
id: None,
vulnerability_source: None,
description: Some(
"Known vulnerability in library that allows unauthorized access"
.to_string(),
),
detail: Some(
"Detailed explanation of the vulnerability and its potential impact."
.to_string(),
),
recommendation: Some("Upgrade to version 1.2.4 or later".to_string()),
workaround: None,
proof_of_concept: None,
advisories: None,
created: None,
published: None,
updated: None,
rejected: None,
vulnerability_credits: None,
tools: None,
vulnerability_analysis: None,
vulnerability_targets: None,
vulnerability_ratings: Some(VulnerabilityRatings(vec![VulnerabilityRating {
score: Some(Score::from(8.1)),
severity: Some(Severity::High),
score_method: Some(ScoreMethod::CVSSv31),
vector: Some(NormalizedString::new(
"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H",
)),
vulnerability_source: None,
justification: None,
}])),
vulnerability_references: None,
cwes: None,
properties: None,
},
Vulnerability {
bom_ref: None,
id: None,
vulnerability_source: None,
description: Some("Component does not use the affected library".to_string()),
detail: Some(
"Detailed explanation of the vulnerability and its potential impact."
.to_string(),
),
recommendation: Some("Upgrade to version 1.2.3 or later".to_string()),
workaround: None,
proof_of_concept: None,
advisories: None,
created: None,
published: None,
updated: None,
rejected: None,
vulnerability_credits: None,
tools: None,
vulnerability_analysis: None,
vulnerability_targets: None,
vulnerability_ratings: Some(VulnerabilityRatings(vec![VulnerabilityRating {
score: Some(Score::from(6.5)),
severity: Some(Severity::High),
score_method: Some(ScoreMethod::CVSSv31),
vector: Some(NormalizedString::new(
"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L",
)),
vulnerability_source: None,
justification: None,
}])),
vulnerability_references: None,
cwes: None,
properties: None,
},
])),
..Bom::default()
}
}
#[test]
fn test_vex_serialization() {
let vex = create_sample_vex();
let mut output = Vec::<u8>::new();
vex.clone()
.output_as_json_v1_5(&mut output)
.expect("failed to read vex object");
let json_str = String::from_utf8(output).expect("failed to serialize json object");
println!("Serialized VEX: {}", json_str);
let parsed_json =
serde_json::from_str(&json_str).expect("serde failed to read json from string object");
let deserialization_result = Bom::parse_json_value(parsed_json);
match deserialization_result {
Ok(deserialized) => {
println!("Deserialized CycloneDX: {:?}", deserialized);
assert_eq!(vex.serial_number, deserialized.serial_number);
assert_eq!(vex.spec_version, deserialized.spec_version);
}
Err(err) => {
panic!("Deserialization failed: {:?}", err);
}
}
}
#[test]
fn test_vex_json_file_io() {
use std::io::Write;
let vex = create_sample_vex();
let mut output = Vec::<u8>::new();
vex.clone()
.output_as_json_v1_5(&mut output)
.expect("failed to read vex object");
let json_str = String::from_utf8(output).expect("failed to serialize json object");
let mut temp_file = std::env::temp_dir();
temp_file.push("test_vex.json");
let mut file = fs::File::create(&temp_file).expect("Failed to create temp file");
file.write_all(json_str.as_bytes())
.expect("Failed to write to temp file");
let content_reader =
BufReader::new(fs::File::open(&temp_file).expect("failed to open file"));
let loaded_vex: Bom = Bom::parse_from_json(content_reader).expect("Failed to parse JSON");
fs::remove_file(&temp_file).expect("Failed to remove temp file");
assert_eq!(vex.serial_number, loaded_vex.serial_number);
}
#[test]
fn test_vex_xml_file_io() {
use std::io::Write;
let vex = create_sample_vex();
let mut output = Vec::<u8>::new();
vex.clone()
.output_as_xml_v1_5(&mut output)
.expect("failed to read vex object");
let xml_str = String::from_utf8(output).expect("failed to serialize json object");
let mut temp_file = std::env::temp_dir();
temp_file.push("test_vex.xml");
let mut file = fs::File::create(&temp_file).expect("Failed to create temp file");
file.write_all(xml_str.as_bytes())
.expect("Failed to write to temp file");
let content_reader =
BufReader::new(fs::File::open(&temp_file).expect("failed to open file"));
let loaded_vex: Bom =
Bom::parse_from_xml_v1_5(content_reader).expect("Failed to parse JSON");
fs::remove_file(&temp_file).expect("Failed to remove temp file");
assert_eq!(vex.serial_number, loaded_vex.serial_number);
}
#[test]
fn test_generate_sample_file() {
let vex = create_sample_vex();
let mut output = Vec::<u8>::new();
vex.clone()
.output_as_json_v1_5(&mut output)
.expect("failed to read vex object");
let json_str = String::from_utf8(output).expect("failed to serialize json object");
fs::write("sample_vex.json", json_str).expect("Failed to write sample file");
println!("Sample VEX file created at sample_vex.json");
}
#[test]
fn test_novulns_msg_env_var_handling() {
use crate::lib_utils::env_vars::EnvVarNames;
use std::env;
let original = env::var(EnvVarNames::NoVulnsMsg.as_str()).ok();
env::remove_var(EnvVarNames::NoVulnsMsg.as_str());
assert_eq!(
env::var(EnvVarNames::NoVulnsMsg.as_str()).is_err(),
true,
"Env var should not exist initially"
);
env::set_var(EnvVarNames::NoVulnsMsg.as_str(), "false");
assert_eq!(
env::var(EnvVarNames::NoVulnsMsg.as_str()).unwrap(),
"false",
"Env var should be retrievable with correct value"
);
if let Some(val) = original {
env::set_var(EnvVarNames::NoVulnsMsg.as_str(), val);
} else {
env::remove_var(EnvVarNames::NoVulnsMsg.as_str());
}
}
#[test]
fn test_embedded_fonts_load_correctly() {
use crate::pdf::font_config::FontsDir;
FontsDir::build();
}
#[cfg(test)]
mod tests {
use crate::lib_utils::env_vars::EnvVarNames;
use std::env;
#[test]
fn test_env_var_behavior() {
{
let var = EnvVarNames::ProcessXml;
env::remove_var(var.as_str());
assert_eq!(
var.is_on(),
false,
"is_on() should return false when var not set"
);
}
{
let var = EnvVarNames::ProcessXml;
for value in &["true", "True", "TRUE", "yes", "YES", "1", "on", "ON"] {
env::set_var(var.as_str(), value);
assert_eq!(var.is_on(), true, "is_on() failed for value: {}", value);
env::remove_var(var.as_str()); }
}
{
let var = EnvVarNames::ProcessXml;
for value in &["false", "False", "FALSE", "no", "NO", "0", "off", "OFF"] {
env::set_var(var.as_str(), value);
assert_eq!(var.is_on(), false, "is_on() failed for value: {}", value);
env::remove_var(var.as_str()); }
}
{
let var = EnvVarNames::ProcessXml;
env::remove_var(var.as_str());
assert_eq!(
var.is_on_or_unset(),
true,
"is_on_or_unset() should return true when var not set"
);
}
{
let var = EnvVarNames::ProcessXml;
for value in &["true", "True", "TRUE", "yes", "YES", "1", "on", "ON"] {
env::set_var(var.as_str(), value);
assert_eq!(
var.is_on_or_unset(),
true,
"is_on_or_unset() failed for value: {}",
value
);
env::remove_var(var.as_str()); }
}
{
let var = EnvVarNames::ProcessXml;
for value in &["false", "False", "FALSE", "no", "NO", "0", "off", "OFF"] {
env::set_var(var.as_str(), value);
assert_eq!(
var.is_on_or_unset(),
false,
"is_on_or_unset() failed for value: {}",
value
);
env::remove_var(var.as_str()); }
}
}
#[test]
fn test_get_value() {
use std::env;
{
let var = EnvVarNames::ReportTitle;
env::remove_var(var.as_str());
assert_eq!(
var.get_value(),
None,
"Should return None for non-existent env var"
);
}
{
let var = EnvVarNames::PdfName;
let test_value = "Test PDF Name";
env::set_var(var.as_str(), test_value);
assert_eq!(
var.get_value(),
Some(test_value.to_string()),
"Should return the value of the env var"
);
env::remove_var(var.as_str()); }
{
let var = EnvVarNames::ReportTitle;
env::set_var(var.as_str(), "");
assert_eq!(
var.get_value(),
Some("".to_string()),
"Should return an empty string for empty env var"
);
env::remove_var(var.as_str()); }
}
}
}