diff --git a/rust/models/src/result.rs b/rust/models/src/result.rs index 4c6db539c..5afa69202 100644 --- a/rust/models/src/result.rs +++ b/rust/models/src/result.rs @@ -109,7 +109,7 @@ pub struct VulnerablePackage { pub fixed_version: FixedVersion, } -#[cfg_attr(feature = "serde_support", derive(serde::Serialize))] +#[cfg_attr(feature = "serde_support", derive(serde::Serialize), serde(untagged))] #[derive(Debug)] pub enum FixedVersion { Single { diff --git a/rust/notus/data/debian_10_advisory_parse_err.notus b/rust/notus/data/debian_10_advisory_parse_err.notus new file mode 100644 index 000000000..944463f20 --- /dev/null +++ b/rust/notus/data/debian_10_advisory_parse_err.notus @@ -0,0 +1,87 @@ +{ + "version": "1.3", + "package_type": "deb", + "advisories": [ + { + "oid": "1.3.6.1.4.1.25623.1.1.7.2.2023.10089729899100", + "fixed_packages": [ + { + "name": "gitlab-ce", + "range": { + "start": "?", + "end": "=" + } + }, + { + "name": "gitlab-ce", + "range": { + "start": "16.0.0", + "end": "16.0.7" + } + }, + { + "name": "gitlab-ce", + "range": { + "start": "16.1.0", + "end": "16.1.2" + } + } + ] + }, + { + "oid": "1.3.6.1.4.1.25623.1.1.7.2.2023.0988598199100", + "fixed_packages": [ + { + "name": "grafana", + "full_version": "8.5.24", + "specifier": ">=" + }, + { + "name": "grafana", + "range": { + "start": "9.0.0", + "end": "9.2.17" + } + }, + { + "name": "grafana", + "range": { + "start": "9.3.0", + "end": "9.3.13" + } + }, + { + "name": "grafana", + "range": { + "start": "9.4.0", + "end": "9.4.9" + } + }, + { + "name": "grafana8", + "full_version": "8.5.24", + "specifier": ">=" + }, + { + "name": "grafana9", + "full_version": "9.2.17", + "specifier": ">=" + }, + { + "name": "grafana9", + "range": { + "start": "9.3.0", + "end": "9.3.13" + } + }, + { + "name": "grafana9", + "range": { + "start": "9.4.0", + "end": "9.4.9" + } + } + ] + } + ] +} \ No newline at end of file diff --git a/rust/notus/data/debian_10_json_parse_err.notus b/rust/notus/data/debian_10_json_parse_err.notus new file mode 100644 index 000000000..30061fa9c --- /dev/null +++ b/rust/notus/data/debian_10_json_parse_err.notus @@ -0,0 +1,87 @@ +{ + "version": "1.3", + "package_type": "deb", + "advisories": [ + { + "oid": "1.3.6.1.4.1.25623.1.1.7.2.2023.10089729899100", + "fixed_packages": [ + { + "name": "gitlab-ce", + "range": { + "start": "15.11.0", + "en": "15.11.11" + } + }, + { + "name": "gitlab-ce", + "range": { + "start": "16.0.0", + "end": "16.0.7" + } + }, + { + "name": "gitlab-ce", + "range": { + "start": "16.1.0", + "end": "16.1.2" + } + } + ] + }, + { + "oid": "1.3.6.1.4.1.25623.1.1.7.2.2023.0988598199100", + "fixed_packages": [ + { + "name": "grafana", + "full_version": "8.5.24", + "specifier": ">=" + }, + { + "name": "grafana", + "range": { + "start": "9.0.0", + "end": "9.2.17" + } + }, + { + "name": "grafana", + "range": { + "start": "9.3.0", + "end": "9.3.13" + } + }, + { + "name": "grafana", + "range": { + "start": "9.4.0", + "end": "9.4.9" + } + }, + { + "name": "grafana8", + "full_version": "8.5.24", + "specifier": ">=" + }, + { + "name": "grafana9", + "full_version": "9.2.17", + "specifier": ">=" + }, + { + "name": "grafana9", + "range": { + "start": "9.3.0", + "end": "9.3.13" + } + }, + { + "name": "grafana9", + "range": { + "start": "9.4.0", + "end": "9.4.9" + } + } + ] + } + ] +} \ No newline at end of file diff --git a/rust/notus/src/advisory.rs b/rust/notus/src/advisory.rs index a20f89609..8a5853dfd 100644 --- a/rust/notus/src/advisory.rs +++ b/rust/notus/src/advisory.rs @@ -2,7 +2,10 @@ use std::collections::HashMap; use models::{FixedPackage, FixedVersion, Specifier}; -use crate::packages::{deb::Deb, ebuild::EBuild, rpm::Rpm, slack::Slack, Package}; +use crate::{ + error::Error, + packages::{deb::Deb, ebuild::EBuild, rpm::Rpm, slack::Slack, Package}, +}; pub type Advisories
= HashMap ,
advisories: Vec
diff --git a/rust/notus/src/error.rs b/rust/notus/src/error.rs
index 0e173a82e..885ca0125 100644
--- a/rust/notus/src/error.rs
+++ b/rust/notus/src/error.rs
@@ -1,20 +1,38 @@
-use std::fmt::Display;
+use std::{fmt::Display, io};
-#[derive(PartialEq, PartialOrd, Debug)]
-pub enum NotusError {
- InvalidOS,
- JSONParseError,
- UnsupportedVersion(String),
- NoLoader,
+use models::FixedPackage;
+
+#[derive(Debug)]
+pub enum Error {
+ // The directory containing the notus advisories does not exist
+ MissingAdvisoryDir(String),
+ // The given notus advisory directory is a file
+ AdvisoryDirIsFile(String),
+ // There are no corresponding notus files for the given Operating System
+ UnknownOs(String),
+ // General error while loading notus advisories
+ LoadAdvisoryError(String, io::Error),
+ // Unable to parse notus advisory file due to a JSON error
+ JSONParseError(String, serde_json::Error),
+ // The version of the notus advisory file is not supported
+ UnsupportedVersion(String, String, String),
+ // Unable to parse a given package
+ PackageParseError(String),
+ // Unable to parse a package in the notus advisory file
+ AdvisoryParseError(String, FixedPackage),
}
-impl Display for NotusError {
- fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+impl Display for Error {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
- NotusError::InvalidOS => todo!(),
- NotusError::JSONParseError => todo!(),
- NotusError::UnsupportedVersion(_) => todo!(),
- NotusError::NoLoader => todo!(),
+ Error::UnknownOs(path) => write!(f, "the File {path} was not found, that is either due to a typo or missing notus advisories for the corresponding OS"),
+ Error::JSONParseError(path, json_err) => write!(f, "unable to parse Notus file {path}. The corresponding parse error was: {json_err}"),
+ Error::UnsupportedVersion(path, version1, version2) => write!(f, "the version of the parsed advisory file {path} is {version1}. This version is currently not supported, the version {version2} is required"),
+ Error::MissingAdvisoryDir(path) => write!(f, "The directory {path}, which should contain the notus advisories does not exist"),
+ Error::AdvisoryDirIsFile(path) => write!(f, "The given notus advisory directory {path} is a file"),
+ Error::LoadAdvisoryError(path, err) => write!(f, "Unable to load advisories from {path}: {err}"),
+ Error::PackageParseError(pkg) => write!(f, "Unable to parse the given package {pkg}"),
+ Error::AdvisoryParseError(path, pkg) => write!(f, "Unable to parse fixed package information {:?} in the advisories {path}", pkg),
}
}
}
diff --git a/rust/notus/src/lib.rs b/rust/notus/src/lib.rs
index 2ce655d65..f44143ba7 100644
--- a/rust/notus/src/lib.rs
+++ b/rust/notus/src/lib.rs
@@ -6,4 +6,5 @@ pub mod loader;
pub mod packages;
pub mod advisory;
+pub mod error;
pub mod notus;
diff --git a/rust/notus/src/loader/json.rs b/rust/notus/src/loader/json.rs
index 84c6ce908..c072d1ae2 100644
--- a/rust/notus/src/loader/json.rs
+++ b/rust/notus/src/loader/json.rs
@@ -4,14 +4,17 @@
use std::{
fs::File,
- io::{Error, ErrorKind, Read},
+ io::{self, Read},
path::Path,
};
use models::Advisories;
+use crate::error::Error;
+
use super::AdvisoriesLoader;
+#[derive(Debug)]
pub struct JSONAdvisoriesLoader
where
P: AsRef ,
- ) -> NotusResults {
+ fn parse , advisories: &Advisories ) -> NotusResults {
let mut results: NotusResults = HashMap::new();
for package in packages {
- match P::from_full_name(&package) {
- Some(package) => match advisories.get(&package.get_name()) {
- Some(advisories) => {
- for advisory in advisories {
- if advisory.is_vulnerable(&package) {
- let vul_pkg = VulnerablePackage {
- name: package.get_name(),
- installed_version: package.get_version(),
- fixed_version: advisory.get_fixed_version(),
- };
- match results.get_mut(&advisory.get_oid()) {
- Some(vul_pkgs) => {
- vul_pkgs.push(vul_pkg);
- }
- None => {
- results.insert(advisory.get_oid(), vec![vul_pkg]);
- }
+ match advisories.get(&package.get_name()) {
+ Some(advisories) => {
+ for advisory in advisories {
+ if advisory.is_vulnerable(package) {
+ let vul_pkg = VulnerablePackage {
+ name: package.get_name(),
+ installed_version: package.get_version(),
+ fixed_version: advisory.get_fixed_version(),
+ };
+ match results.get_mut(&advisory.get_oid()) {
+ Some(vul_pkgs) => {
+ vul_pkgs.push(vul_pkg);
+ }
+ None => {
+ results.insert(advisory.get_oid(), vec![vul_pkg]);
}
}
}
}
- // No advisory for package
- None => continue,
- },
- // Unable to parse user input
- None => continue, // TODO: Some Error handling, at least Logging
+ }
+ // No advisory for package
+ None => continue,
}
}
results
}
- pub fn scan(&mut self, os: &str, packages: Vec ,
+ ) -> Result