diff --git a/README.md b/README.md index e3a8010..e19aeaa 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,13 @@ Discover CVEs for software. - **Use case 1)** as a [Funtoo Linux] user I want to have awareness about CVEs on my system - **Use case 2)** as user I want to list CVEs for given package +- **Use case 3)** as a [Gentoo Linux] user I want to have awareness about CVEs on my system ## DISCLAIMER Running `vulner scan` doesn't guarantee that all CVEs present on your system will be -detected. It tries to map packages installed by the portage (funtoo package -manager) to a set of known NVD CPEs. It is possible that not all packages will -be successfully tagged. +detected. It tries to map packages installed by the [portage] to a set of known +NVD CPEs. It is possible that not all packages will be successfully tagged. For more info about false negatives and false positives check [docs/CAVEATS.md](docs/CAVEATS.md) @@ -61,5 +61,7 @@ ADR. [Funtoo Linux]: https://www.funtoo.org/ +[Gentoo Linux]: https://www.gentoo.org/ [CVE]: https://nvd.nist.gov/vuln [CPE]: https://nvd.nist.gov/products/cpe +[portage]: https://en.wikipedia.org/wiki/Portage_(software) diff --git a/crates/os-adapter/src/adapter.rs b/crates/os-adapter/src/adapter.rs index 9bd185a..94afbe3 100644 --- a/crates/os-adapter/src/adapter.rs +++ b/crates/os-adapter/src/adapter.rs @@ -14,13 +14,20 @@ use std::io; use std::str::FromStr; use std::vec::Vec; mod linux_funtoo; +mod linux_gentoo; +mod portage; -pub trait OsAdapter { +pub trait OsInfo { fn get_os(&self) -> &Os; fn get_os_flavor(&self) -> Option; +} + +pub trait OsPackages { fn get_all_catpkgs(&self) -> Result>, Box>; } +pub trait OsAdapter: OsInfo + OsPackages {} + pub fn get_adapter() -> Result, Box> { let os = Os::from_str(env::consts::OS)?; @@ -46,12 +53,16 @@ pub enum OsFlavor<'a> { #[derive(Debug, PartialEq)] pub enum LinuxDistro { Funtoo, + Gentoo, } pub fn get_supported_map() -> HashMap>> { HashMap::from([( Os::GnuLinux, - vec![OsFlavor::LinuxDistro(&LinuxDistro::Funtoo)], + vec![ + OsFlavor::LinuxDistro(&LinuxDistro::Funtoo), + OsFlavor::LinuxDistro(&LinuxDistro::Gentoo), + ], )]) } @@ -59,6 +70,7 @@ fn get_linux_adapter(id: LinuxDistro) -> Box { // https://refactoring.guru/design-patterns/factory-method match id { LinuxDistro::Funtoo => Box::new(linux_funtoo::Funtoo::new()), + LinuxDistro::Gentoo => Box::new(linux_gentoo::Gentoo::new()), } } @@ -89,6 +101,7 @@ impl FromStr for LinuxDistro { // ID of https://www.freedesktop.org/software/systemd/man/os-release.html match input { "funtoo" => Ok(LinuxDistro::Funtoo), + "gentoo" => Ok(LinuxDistro::Gentoo), _ => Err(io::Error::new(err_kind, err_msg)), } } @@ -107,6 +120,7 @@ mod tests { #[test] fn it_should_know_supported_distros() { assert!(LinuxDistro::from_str("funtoo").is_ok()); + assert!(LinuxDistro::from_str("gentoo").is_ok()); assert!(LinuxDistro::from_str("debian").is_err()); } } diff --git a/crates/os-adapter/src/adapter/linux_funtoo.rs b/crates/os-adapter/src/adapter/linux_funtoo.rs index aa4533b..c2067a8 100644 --- a/crates/os-adapter/src/adapter/linux_funtoo.rs +++ b/crates/os-adapter/src/adapter/linux_funtoo.rs @@ -5,16 +5,12 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use cpe_tag::package::{convert_to_pkg, Package}; -use std::collections::HashMap; -use std::error::Error; -use std::fs::read_dir; +use super::portage::Portage; use std::path::Path; pub struct Funtoo { os: super::Os, flavor: super::LinuxDistro, - pkg_dir: String, } impl Funtoo { @@ -22,12 +18,13 @@ impl Funtoo { Self { os: super::Os::GnuLinux, flavor: super::LinuxDistro::Funtoo, - pkg_dir: "/var/db/pkg/".to_owned(), } } } -impl super::OsAdapter for Funtoo { +impl super::OsAdapter for Funtoo {} + +impl super::OsInfo for Funtoo { fn get_os(&self) -> &super::Os { &self.os } @@ -35,73 +32,10 @@ impl super::OsAdapter for Funtoo { fn get_os_flavor(&self) -> Option { Some(super::OsFlavor::LinuxDistro(&self.flavor)) } - - fn get_all_catpkgs(&self) -> Result>, Box> { - let pkg_prefix_adapter: HashMap<&str, String> = - HashMap::from([("dev-libs", "lib".to_owned())]); - let skipped_dirs = vec!["virtual"]; - let mut all_catpkgs = HashMap::new(); - - log::info!("walking {} ...", &self.pkg_dir); - for category in read_dir(Path::new(&self.pkg_dir))? { - let category = category?; - let path = &category.path(); - - if !path.is_dir() { - continue; - } - - match category.file_name().into_string() { - Ok(ctgr) => { - if skipped_dirs.contains(&ctgr.as_str()) { - log::warn!("SKIPPING packages in {} ...", ctgr); - continue; - } - - log::debug!("collecting packages in {} ...", ctgr); - let pkgs = list_pkgs(path, pkg_prefix_adapter.get(ctgr.as_str()))?; - all_catpkgs.insert(ctgr, pkgs); - } - Err(os_path) => { - log::error!("skipping {:?}", os_path); - continue; - } - } - } - - Ok(all_catpkgs) - } } -fn list_pkgs(path: &Path, prefix: Option<&String>) -> Result, Box> { - let mut pkgs: Vec = vec![]; - - for pkg in read_dir(path)? { - let pkg = pkg?; - let path = &pkg.path(); - - if !path.is_dir() { - continue; - } - - match pkg.file_name().into_string() { - Ok(p) => { - if let Some(converted) = convert_to_pkg(&p) { - pkgs.push(converted); - } - - if let Some(prfx) = prefix { - if let Some(converted) = convert_to_pkg(&format!("{}{}", prfx, &p)) { - pkgs.push(converted); - } - } - } - Err(os_string) => { - log::error!("skipping {:?}", os_string); - continue; - } - } +impl Portage for Funtoo { + fn get_pkg_dir(&self) -> &Path { + Path::new("/var/db/pkg/") } - - Ok(pkgs) } diff --git a/crates/os-adapter/src/adapter/linux_gentoo.rs b/crates/os-adapter/src/adapter/linux_gentoo.rs new file mode 100644 index 0000000..3d4d380 --- /dev/null +++ b/crates/os-adapter/src/adapter/linux_gentoo.rs @@ -0,0 +1,41 @@ +/* + * SPDX-License-Identifier: MPL-2.0 + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +use super::portage::Portage; +use std::path::Path; + +pub struct Gentoo { + os: super::Os, + flavor: super::LinuxDistro, +} + +impl Gentoo { + pub fn new() -> Self { + Self { + os: super::Os::GnuLinux, + flavor: super::LinuxDistro::Funtoo, + } + } +} + +impl super::OsAdapter for Gentoo {} + +impl super::OsInfo for Gentoo { + fn get_os(&self) -> &super::Os { + &self.os + } + + fn get_os_flavor(&self) -> Option { + Some(super::OsFlavor::LinuxDistro(&self.flavor)) + } +} + +impl Portage for Gentoo { + fn get_pkg_dir(&self) -> &Path { + Path::new("/var/db/pkg/") + } +} diff --git a/crates/os-adapter/src/adapter/portage.rs b/crates/os-adapter/src/adapter/portage.rs new file mode 100644 index 0000000..c57ff12 --- /dev/null +++ b/crates/os-adapter/src/adapter/portage.rs @@ -0,0 +1,91 @@ +/* + * SPDX-License-Identifier: MPL-2.0 + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +use cpe_tag::package::{convert_to_pkg, Package}; +use std::collections::HashMap; +use std::error::Error; +use std::fs::read_dir; +use std::path::Path; + +pub trait Portage { + fn get_pkg_dir(&self) -> &Path; +} + +impl super::OsPackages for T +where + T: Portage, +{ + // https://riptutorial.com/rust/example/22917/inheritance-with-traits + fn get_all_catpkgs(&self) -> Result>, Box> { + let pkg_prefix_adapter: HashMap<&str, String> = + HashMap::from([("dev-libs", "lib".to_owned())]); + let skipped_dirs = vec!["virtual"]; + let mut all_catpkgs = HashMap::new(); + + log::info!("walking {:?} ...", &self.get_pkg_dir().as_os_str()); + for category in read_dir(&self.get_pkg_dir())? { + let category = category?; + let path = &category.path(); + + if !path.is_dir() { + continue; + } + + match category.file_name().into_string() { + Ok(ctgr) => { + if skipped_dirs.contains(&ctgr.as_str()) { + log::warn!("SKIPPING packages in {} ...", ctgr); + continue; + } + + log::debug!("collecting packages in {} ...", ctgr); + let pkgs = list_pkgs(path, pkg_prefix_adapter.get(ctgr.as_str()))?; + all_catpkgs.insert(ctgr, pkgs); + } + Err(os_path) => { + log::error!("skipping {:?}", os_path); + continue; + } + } + } + + Ok(all_catpkgs) + } +} + +fn list_pkgs(path: &Path, prefix: Option<&String>) -> Result, Box> { + let mut pkgs: Vec = vec![]; + + for pkg in read_dir(path)? { + let pkg = pkg?; + let path = &pkg.path(); + + if !path.is_dir() { + continue; + } + + match pkg.file_name().into_string() { + Ok(p) => { + if let Some(converted) = convert_to_pkg(&p) { + pkgs.push(converted); + } + + if let Some(prfx) = prefix { + if let Some(converted) = convert_to_pkg(&format!("{}{}", prfx, &p)) { + pkgs.push(converted); + } + } + } + Err(os_string) => { + log::error!("skipping {:?}", os_string); + continue; + } + } + } + + Ok(pkgs) +}