Skip to content

Commit 0cf71f4

Browse files
authored
Test 6.1.35: Add validation for contradicting remediations in CSAF documents (#19)
1 parent b159259 commit 0cf71f4

File tree

13 files changed

+835
-54
lines changed

13 files changed

+835
-54
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
use crate::csaf::csaf2_0::schema::{CategoryOfTheRemediation, CommonSecurityAdvisoryFramework, ProductGroup, ProductTree, Remediation, Vulnerability};
2+
use crate::csaf::csaf2_1::schema::CategoryOfTheRemediation as Remediation21;
3+
use crate::csaf::getter_traits::{CsafTrait, ProductGroupTrait, ProductTreeTrait, RemediationTrait, VulnerabilityTrait};
4+
use std::ops::Deref;
5+
6+
impl RemediationTrait for Remediation {
7+
8+
/// Normalizes the remediation categories from CSAF 2.0 to those of CSAF 2.1.
9+
///
10+
/// # Explanation
11+
/// In CSAF 2.1, the list of remediation categories was expanded, making it a superset of those
12+
/// in CSAF 2.0. This function ensures that the remediation category from a CSAF 2.0 remediation
13+
/// object is converted into the corresponding category defined in CSAF 2.1.
14+
///
15+
/// # Returns
16+
/// A CSAF 2.1 `CategoryOfTheRemediation` that corresponds to the remediation category of the
17+
/// current object.
18+
fn get_category(&self) -> Remediation21 {
19+
match self.category {
20+
CategoryOfTheRemediation::Workaround => Remediation21::Workaround,
21+
CategoryOfTheRemediation::Mitigation => Remediation21::Mitigation,
22+
CategoryOfTheRemediation::VendorFix => Remediation21::VendorFix,
23+
CategoryOfTheRemediation::NoFixPlanned => Remediation21::NoFixPlanned,
24+
CategoryOfTheRemediation::NoneAvailable => Remediation21::NoneAvailable,
25+
}
26+
}
27+
28+
fn get_product_ids(&self) -> Option<Vec<&String>> {
29+
self.product_ids.as_ref().map(|p| (*p).iter().map(|x| x.deref()).collect())
30+
}
31+
32+
fn get_group_ids(&self) -> Option<Vec<&String>> {
33+
self.group_ids.as_ref().map(|g| (*g).iter().map(|x| x.deref()).collect())
34+
}
35+
}
36+
37+
impl VulnerabilityTrait for Vulnerability {
38+
type RemediationType = Remediation;
39+
40+
fn get_remediations(&self) -> Vec<Self::RemediationType> {
41+
self.remediations.clone()
42+
}
43+
}
44+
45+
impl CsafTrait for CommonSecurityAdvisoryFramework {
46+
type VulnerabilityType = Vulnerability;
47+
type ProductTreeType = ProductTree;
48+
49+
fn get_product_tree(&self) -> Option<Self::ProductTreeType> {
50+
self.product_tree.clone()
51+
}
52+
53+
fn get_vulnerabilities(&self) -> Vec<Self::VulnerabilityType> {
54+
self.vulnerabilities.clone()
55+
}
56+
}
57+
58+
impl ProductTreeTrait for ProductTree {
59+
type ProductGroupType = ProductGroup;
60+
61+
fn get_product_groups(&self) -> Vec<Self::ProductGroupType> {
62+
self.product_groups.clone()
63+
}
64+
}
65+
66+
impl ProductGroupTrait for ProductGroup {
67+
fn get_group_id(&self) -> &String {
68+
self.group_id.deref()
69+
}
70+
71+
fn get_product_ids(&self) -> Vec<&String> {
72+
self.product_ids.iter().map(|x| x.deref()).collect()
73+
}
74+
}

csaf-lib/src/csaf/csaf2_0/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ pub mod loader;
22
mod product_helper;
33
pub mod schema;
44
pub mod validation;
5+
pub mod getter_implementations;

csaf-lib/src/csaf/csaf2_0/validation.rs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,25 @@ use crate::csaf::helpers::find_duplicates;
66

77
impl Validatable<CommonSecurityAdvisoryFramework> for CommonSecurityAdvisoryFramework {
88
fn presets(&self) -> HashMap<ValidationPreset, Vec<&str>> {
9+
let basic_tests = Vec::from(["6.1.1", "6.1.2"]);
10+
// More tests may be added in extend() here later
11+
let extended_tests: Vec<&str> = basic_tests.clone();
12+
// extended_tests.extend(["foo"].iter());
13+
let full_tests: Vec<&str> = extended_tests.clone();
14+
// full_tests.extend(["bar"].iter());
915
HashMap::from([
10-
(ValidationPreset::Basic, Vec::from(["6.1.1", "6.1.2"])),
11-
(ValidationPreset::Extended, Vec::from(["6.1.1", "6.1.2"])),
12-
(ValidationPreset::Full, Vec::from(["6.1.1", "6.1.2"])),
16+
(ValidationPreset::Basic, basic_tests),
17+
(ValidationPreset::Extended, extended_tests),
18+
(ValidationPreset::Full, full_tests),
1319
])
1420
}
1521

1622
fn tests(&self) -> HashMap<&str, Test<CommonSecurityAdvisoryFramework>> {
17-
HashMap::<&str, Test<CommonSecurityAdvisoryFramework>>::from([
18-
("6.1.1", test_6_01_01_missing_definition_of_product_id),
19-
("6.1.2", test_6_01_02_multiple_definition_of_product_id),
20-
]
21-
as [(&str, Test<CommonSecurityAdvisoryFramework>); 2])
23+
type CsafTest = Test<CommonSecurityAdvisoryFramework>;
24+
HashMap::from([
25+
("6.1.1", test_6_01_01_missing_definition_of_product_id as CsafTest),
26+
("6.1.2", test_6_01_02_multiple_definition_of_product_id as CsafTest),
27+
])
2228
}
2329

2430
fn doc(&self) -> &CommonSecurityAdvisoryFramework {

csaf-lib/src/csaf/csaf2_1/csaf_json_schema.json

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -247,13 +247,20 @@
247247
"minLength": 1
248248
}
249249
},
250-
"purl": {
251-
"title": "package URL representation",
252-
"description": "The package URL (purl) attribute refers to a method for reliably identifying and locating software packages external to this specification.",
253-
"type": "string",
254-
"format": "uri",
255-
"pattern": "^pkg:[A-Za-z\\.\\-\\+][A-Za-z0-9\\.\\-\\+]*\\/.+",
256-
"minLength": 7
250+
"purls": {
251+
"title": "List of package URLs",
252+
"description": "Contains a list of package URLs (purl).",
253+
"type": "array",
254+
"minItems": 1,
255+
"uniqueItems": true,
256+
"items": {
257+
"title": "package URL representation",
258+
"description": "The package URL (purl) attribute refers to a method for reliably identifying and locating software packages external to this specification.",
259+
"type": "string",
260+
"format": "uri",
261+
"pattern": "^pkg:[A-Za-z\\.\\-\\+][A-Za-z0-9\\.\\-\\+]*\\/.+",
262+
"minLength": 7
263+
}
257264
},
258265
"sbom_urls": {
259266
"title": "List of SBOM URLs",
@@ -581,6 +588,36 @@
581588
"tlp"
582589
],
583590
"properties": {
591+
"sharing_group": {
592+
"title": "Sharing Group",
593+
"description": "Contains information about the group this document is intended to be shared with.",
594+
"type": "object",
595+
"required": [
596+
"id"
597+
],
598+
"properties": {
599+
"id": {
600+
"title": "Sharing Group ID",
601+
"description": "Provides the unique ID for the sharing group.",
602+
"type": "string",
603+
"pattern": "^(([0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12})|([0]{8}-([0]{4}-){3}[0]{12})|([f]{8}-([f]{4}-){3}[f]{12}))$"
604+
},
605+
"name": {
606+
"title": "Sharing Group Name",
607+
"description": "Contains a human-readable name for the sharing group.",
608+
"type": "string",
609+
"minLength": 1,
610+
"examples": [
611+
"Customer A",
612+
"ISAC members",
613+
"NIS2 regulated important entities in Germany, sector water",
614+
"Pre-Sharing group for advisory discussion",
615+
"Users of Product A",
616+
"US Federal Civilian Authorities"
617+
]
618+
}
619+
}
620+
},
584621
"text": {
585622
"title": "Textual description",
586623
"description": "Provides a textual description of additional constraints.",
@@ -1328,9 +1365,11 @@
13281365
"description": "Specifies the category which this remediation belongs to.",
13291366
"type": "string",
13301367
"enum": [
1368+
"fix_planned",
13311369
"mitigation",
13321370
"no_fix_planned",
13331371
"none_available",
1372+
"optional_patch",
13341373
"vendor_fix",
13351374
"workaround"
13361375
]
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
use crate::csaf::csaf2_1::schema::ProductTree;
2+
use crate::csaf::csaf2_1::schema::{CategoryOfTheRemediation, CommonSecurityAdvisoryFramework, ProductGroup, Remediation, Vulnerability};
3+
use crate::csaf::getter_traits::{CsafTrait, ProductGroupTrait, ProductTreeTrait, RemediationTrait, VulnerabilityTrait};
4+
use std::ops::Deref;
5+
6+
impl RemediationTrait for Remediation {
7+
fn get_category(&self) -> CategoryOfTheRemediation {
8+
self.category.clone()
9+
}
10+
11+
fn get_product_ids(&self) -> Option<Vec<&String>> {
12+
self.product_ids.as_ref().map(|p| (*p).iter().map(|x| x.deref()).collect())
13+
}
14+
15+
fn get_group_ids(&self) -> Option<Vec<&String>> {
16+
self.group_ids.as_ref().map(|g| (*g).iter().map(|x| x.deref()).collect())
17+
}
18+
}
19+
20+
impl VulnerabilityTrait for Vulnerability {
21+
type RemediationType = Remediation;
22+
23+
fn get_remediations(&self) -> Vec<Self::RemediationType> {
24+
self.remediations.clone()
25+
}
26+
}
27+
28+
impl CsafTrait for CommonSecurityAdvisoryFramework {
29+
type VulnerabilityType = Vulnerability;
30+
type ProductTreeType = ProductTree;
31+
32+
fn get_product_tree(&self) -> Option<Self::ProductTreeType> {
33+
self.product_tree.clone()
34+
}
35+
36+
fn get_vulnerabilities(&self) -> Vec<Self::VulnerabilityType> {
37+
self.vulnerabilities.clone()
38+
}
39+
}
40+
41+
impl ProductTreeTrait for ProductTree {
42+
type ProductGroupType = ProductGroup;
43+
44+
fn get_product_groups(&self) -> Vec<Self::ProductGroupType> {
45+
self.product_groups.clone()
46+
}
47+
}
48+
49+
impl ProductGroupTrait for ProductGroup {
50+
fn get_group_id(&self) -> &String {
51+
self.group_id.deref()
52+
}
53+
54+
fn get_product_ids(&self) -> Vec<&String> {
55+
self.product_ids.iter().map(|x| x.deref()).collect()
56+
}
57+
}

csaf-lib/src/csaf/csaf2_1/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ pub mod loader;
22
mod product_helper;
33
pub mod schema;
44
pub mod validation;
5+
pub mod getter_implementations;

0 commit comments

Comments
 (0)