use std::collections::HashMap;
use crate::models::{Component, ComponentManager, ComponentType};
const CATALOGUE: &str = include_str!("data.yml");
#[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
pub struct Catalogue {
aliases: HashMap<String, String>,
catalogue: HashMap<String, ComponentType>,
}
impl Catalogue {
pub fn new() -> Self {
#[cfg(debug_assertions)]
let data: Self = serde_yaml::from_str(CATALOGUE).expect("Failed to load catalogue data");
#[cfg(not(debug_assertions))]
let data: Self = serde_yaml::from_str(CATALOGUE).unwrap_or_default();
log::debug!("Loaded Catalogue Data: {}", data.catalogue.len());
data
}
pub fn catalogue(&self, component: &mut Component) -> Result<bool, crate::KonarrError> {
let comp_purl = component.purl();
if let Some(comp) = self.catalogue.get(&comp_purl) {
if component.component_type != *comp {
log::debug!("Updating component type for: {}", comp_purl);
component.component_type = comp.clone();
return Ok(true);
}
} else {
let wildcards = vec![
format!("pkg:*/{}", component.name),
format!("pkg:{}/*", component.manager),
];
for wildcard in wildcards.iter() {
if let Some(comp) = self.catalogue.get(wildcard) {
if component.component_type != *comp {
log::debug!("Updating component type for: {}", comp_purl);
component.component_type = comp.clone();
return Ok(true);
}
}
}
}
Ok(false)
}
pub fn catalogue_old(component: &mut Component) -> Result<(), crate::KonarrError> {
if component.manager == ComponentManager::Apk || component.manager == ComponentManager::Deb
{
match component.name.to_lowercase().as_str() {
"alpine" | "alpine-linux" | "debian" | "debian-linux" | "ubuntu"
| "ubuntu-linux" | "redhat" | "fedora" | "centos" | "centos-linux" | "arch"
| "arch-linux" => {
component.component_type = ComponentType::OperatingSystem;
}
"python" | "python3" | "node" | "nodejs" | "ruby" | "rustc" | "rust" | "go"
| "java" | "javac" | "kotlinc" | "gcc" | "g++" | "gpp" | "dotnet" | "csharp"
| "c" | "cpp" | "php83" | "perl" | "bash" | "sh" => {
component.component_type = ComponentType::ProgrammingLanguage;
}
"apk" | "apk-tools" | "deb" | "dpkg" | "rpm" | "cargo" | "npm" | "pip"
| "composer" | "maven" | "nuget" | "gradle" | "gem" => {
component.component_type = ComponentType::PackageManager;
}
"openssl" | "libssl" | "libssl3" | "libcrypto" | "libcrypto3" | "libssl-dev"
| "libcrypto-dev" | "argon2-libs" | "ssl_client" => {
component.component_type = ComponentType::CryptographyLibrary;
}
"mysql" | "mariadb" | "postgresql" | "sqlite" | "mongodb" | "redis"
| "cassandra" => {
component.component_type = ComponentType::Database;
}
"curl" | "wget" | "git" | "grep" | "jq" | "nginx" => {
component.component_type = ComponentType::Application;
}
"apr" | "apr-util" | "busybox" | "busybox-binsh" => {
component.component_type = ComponentType::OperatingEnvironment;
}
_ => {
component.component_type = ComponentType::Library;
}
}
} else if component.manager == ComponentManager::Golang {
match component.purl().as_str() {
"pkg:golang/cloud.google.com/go" => {
component.component_type = ComponentType::Library;
}
"pkg:golang.org/x/crypto" => {
component.component_type = ComponentType::CryptographyLibrary;
}
_ => {
component.component_type = ComponentType::Library;
}
}
} else if component.manager == ComponentManager::Generic {
component.component_type = ComponentType::Application;
} else {
component.component_type = ComponentType::Library;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_wildcards() {
let mut catalogue = Catalogue::default();
catalogue.catalogue.insert(
"pkg:*/openssl".to_string(),
ComponentType::CryptographyLibrary,
);
let mut data = vec![
Component::from_purl("pkg:deb/debian/openssl").unwrap(),
Component::from_purl("pkg:apk/alpine/openssl").unwrap(),
Component::from_purl("pkg:rpm/fedora/openssl").unwrap(),
Component::from_purl("pkg:generic/openssl").unwrap(),
Component::from_purl("pkg:conan/openssl").unwrap(),
Component::from_purl("pkg:conan/openssl.org/openssl").unwrap(),
Component::from_purl("pkg:alpm/arch/openssl").unwrap(),
];
for (comp, _ver) in data.iter_mut() {
let _output = catalogue.catalogue(comp).unwrap();
assert_eq!(comp.component_type, ComponentType::CryptographyLibrary);
}
}
#[test]
fn test_catalogue() {
let catalogue = Catalogue::new();
let mut data = vec![
(
Component::from_purl("pkg:apk/alpine").unwrap(),
ComponentType::OperatingSystem,
),
(
Component::from_purl("pkg:deb/debian/openssl").unwrap(),
ComponentType::CryptographyLibrary,
),
(
Component::from_purl("pkg:deb/debian").unwrap(),
ComponentType::OperatingSystem,
),
(
Component::from_purl("pkg:apk/alpine/python3").unwrap(),
ComponentType::ProgrammingLanguage,
),
(
Component::from_purl("pkg:apk/alpine/openjdk8").unwrap(),
ComponentType::ProgrammingLanguage,
),
];
for ((comp, _ver), expected) in data.iter_mut() {
let _output = catalogue.catalogue(comp).unwrap();
assert_eq!(&comp.component_type, expected);
}
}
}