Skip to content

Commit e571d26

Browse files
authored
Test 6.1.37 and improved Trait efficiency (#29)
* Switch date-time handling to strings and add RFC3339 validation. Replaced `chrono::DateTime` with plain simple `String` for all date-time fields to enable format checking. Introduced a new RFC3339 format validation test to ensure compliance and prevent invalid date-time strings. Updated related schemas and test cases accordingly. * Refactor getter methods to return references and iterators Updated getter methods across the CSAF library to return references and iterator-based types instead of cloning data. This improves performance and reduces unnecessary allocations while maintaining compatibility with existing functionality.
1 parent 8e07a62 commit e571d26

File tree

14 files changed

+745
-321
lines changed

14 files changed

+745
-321
lines changed

Cargo.lock

Lines changed: 40 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

csaf-lib/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ regress = "0.10.1"
88
serde = { version = "1.0", features = ["derive"] }
99
serde_json = "1.0"
1010
chrono = { version = "0.4.38", features = ["serde"] }
11+
regex = "1.11.1"
1112

1213
[build-dependencies]
1314
schemars = "0.8.21"

csaf-lib/build.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ fn main() -> Result<(), BuildError> {
3232

3333
fn build(input: &str, output: &str) -> Result<(), BuildError> {
3434
let content = fs::read_to_string(input)?;
35-
let schema = serde_json::from_str::<schemars::schema::RootSchema>(&content)?;
35+
let mut schema_value = serde_json::from_str(&content)?;
36+
// Recursively search for "format": "date-time" and replace with something else
37+
remove_datetime_formats(&mut schema_value);
38+
let schema = serde_json::from_value::<schemars::schema::RootSchema>(schema_value)?;
3639

3740
let mut type_space = TypeSpace::new(TypeSpaceSettings::default().with_struct_builder(true));
3841
type_space.add_root_schema(schema)?;
@@ -43,3 +46,23 @@ fn build(input: &str, output: &str) -> Result<(), BuildError> {
4346
out_file.push(output);
4447
Ok(fs::write(out_file, content)?)
4548
}
49+
50+
fn remove_datetime_formats(value: &mut serde_json::Value) {
51+
if let serde_json::Value::Object(map) = value {
52+
if let Some(format) = map.get("format") {
53+
if format.as_str() == Some("date-time") {
54+
// Remove the format property entirely
55+
map.remove("format");
56+
}
57+
}
58+
59+
// Recursively process all values in the object
60+
for (_, v) in map.iter_mut() {
61+
remove_datetime_formats(v);
62+
}
63+
} else if let serde_json::Value::Array(arr) = value {
64+
for item in arr.iter_mut() {
65+
remove_datetime_formats(item);
66+
}
67+
}
68+
}

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

Lines changed: 139 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
use crate::csaf::csaf2_0::schema::{Branch, CategoryOfTheRemediation, CommonSecurityAdvisoryFramework, FullProductNameT, ProductGroup, ProductStatus, ProductTree, Relationship, Remediation, Threat, Vulnerability};
2-
use crate::csaf::csaf2_1::schema::CategoryOfTheRemediation as Remediation21;
3-
use crate::csaf::getter_traits::{BranchTrait, CsafTrait, FullProductNameTrait, MetricTrait, ProductGroupTrait, ProductStatusTrait, ProductTreeTrait, RelationshipTrait, RemediationTrait, ThreatTrait, VulnerabilityTrait};
1+
use crate::csaf::csaf2_0::schema::{Branch, CategoryOfTheRemediation, CommonSecurityAdvisoryFramework, DocumentGenerator, DocumentLevelMetaData, Flag, FullProductNameT, Involvement, ProductGroup, ProductStatus, ProductTree, Relationship, Remediation, Revision, Threat, Tracking, Vulnerability};
2+
use crate::csaf::csaf2_1::schema::{CategoryOfTheRemediation as Remediation21};
3+
use crate::csaf::getter_traits::{BranchTrait, CsafTrait, DocumentTrait, FlagTrait, FullProductNameTrait, GeneratorTrait, InvolvementTrait, MetricTrait, ProductGroupTrait, ProductStatusTrait, ProductTreeTrait, RelationshipTrait, RemediationTrait, RevisionTrait, ThreatTrait, TrackingTrait, VulnerabilityTrait};
44
use std::ops::Deref;
55

66
impl RemediationTrait for Remediation {
7-
87
/// Normalizes the remediation categories from CSAF 2.0 to those of CSAF 2.1.
98
///
109
/// # Explanation
@@ -25,58 +24,72 @@ impl RemediationTrait for Remediation {
2524
}
2625
}
2726

28-
fn get_product_ids(&self) -> Option<Vec<&String>> {
29-
self.product_ids.as_ref().map(|p| (*p).iter().map(|x| x.deref()).collect())
27+
fn get_product_ids(&self) -> Option<impl Iterator<Item = &String> + '_> {
28+
self.product_ids.as_ref().map(|p| (*p).iter().map(|x| x.deref()))
29+
}
30+
31+
fn get_group_ids(&self) -> Option<impl Iterator<Item = &String> + '_> {
32+
self.group_ids.as_ref().map(|g| (*g).iter().map(|x| x.deref()))
3033
}
3134

32-
fn get_group_ids(&self) -> Option<Vec<&String>> {
33-
self.group_ids.as_ref().map(|g| (*g).iter().map(|x| x.deref()).collect())
35+
fn get_date(&self) -> &Option<String> {
36+
&self.date
3437
}
3538
}
3639

3740
impl ProductStatusTrait for ProductStatus {
38-
fn get_first_affected(&self) -> Option<Vec<&String>> {
39-
self.first_affected.as_ref().map(|p| (*p).iter().map(|x| x.deref()).collect())
41+
fn get_first_affected(&self) -> Option<impl Iterator<Item = &String> + '_> {
42+
self.first_affected.as_ref().map(|p| (*p).iter().map(|x| x.deref()))
4043
}
4144

42-
fn get_first_fixed(&self) -> Option<Vec<&String>> {
43-
self.first_fixed.as_ref().map(|p| (*p).iter().map(|x| x.deref()).collect())
45+
fn get_first_fixed(&self) -> Option<impl Iterator<Item = &String> + '_> {
46+
self.first_fixed.as_ref().map(|p| (*p).iter().map(|x| x.deref()))
4447
}
4548

46-
fn get_fixed(&self) -> Option<Vec<&String>> {
47-
self.fixed.as_ref().map(|p| (*p).iter().map(|x| x.deref()).collect())
49+
fn get_fixed(&self) -> Option<impl Iterator<Item = &String> + '_> {
50+
self.fixed.as_ref().map(|p| (*p).iter().map(|x| x.deref()))
4851
}
4952

50-
fn get_known_affected(&self) -> Option<Vec<&String>> {
51-
self.known_affected.as_ref().map(|p| (*p).iter().map(|x| x.deref()).collect())
53+
fn get_known_affected(&self) -> Option<impl Iterator<Item = &String> + '_> {
54+
self.known_affected.as_ref().map(|p| (*p).iter().map(|x| x.deref()))
5255
}
5356

54-
fn get_known_not_affected(&self) -> Option<Vec<&String>> {
55-
self.known_not_affected.as_ref().map(|p| (*p).iter().map(|x| x.deref()).collect())
57+
fn get_known_not_affected(&self) -> Option<impl Iterator<Item = &String> + '_> {
58+
self.known_not_affected.as_ref().map(|p| (*p).iter().map(|x| x.deref()))
5659
}
5760

58-
fn get_last_affected(&self) -> Option<Vec<&String>> {
59-
self.last_affected.as_ref().map(|p| (*p).iter().map(|x| x.deref()).collect())
61+
fn get_last_affected(&self) -> Option<impl Iterator<Item = &String> + '_> {
62+
self.last_affected.as_ref().map(|p| (*p).iter().map(|x| x.deref()))
6063
}
6164

62-
fn get_recommended(&self) -> Option<Vec<&String>> {
63-
self.recommended.as_ref().map(|p| (*p).iter().map(|x| x.deref()).collect())
65+
fn get_recommended(&self) -> Option<impl Iterator<Item = &String> + '_> {
66+
self.recommended.as_ref().map(|p| (*p).iter().map(|x| x.deref()))
6467
}
6568

66-
fn get_under_investigation(&self) -> Option<Vec<&String>> {
67-
self.under_investigation.as_ref().map(|p| (*p).iter().map(|x| x.deref()).collect())
69+
fn get_under_investigation(&self) -> Option<impl Iterator<Item = &String> + '_> {
70+
self.under_investigation.as_ref().map(|p| (*p).iter().map(|x| x.deref()))
6871
}
6972
}
7073

7174
impl MetricTrait for () {
72-
fn get_products(&self) -> Vec<&String> {
73-
panic!("Metrics are not implemented in CSAF 2.0")
75+
//noinspection RsConstantConditionIf
76+
fn get_products(&self) -> impl Iterator<Item = &String> + '_ {
77+
// This construction is required to satisfy compiler checks
78+
// and still panic if this is ever called (as this would be a clear error!).
79+
if true {
80+
panic!("Metrics are not implemented in CSAF 2.0");
81+
}
82+
std::iter::empty()
7483
}
7584
}
7685

7786
impl ThreatTrait for Threat {
78-
fn get_product_ids(&self) -> Option<Vec<&String>> {
79-
self.product_ids.as_ref().map(|p| (*p).iter().map(|x| x.deref()).collect())
87+
fn get_product_ids(&self) -> Option<impl Iterator<Item = &String> + '_> {
88+
self.product_ids.as_ref().map(|p| (*p).iter().map(|x| x.deref()))
89+
}
90+
91+
fn get_date(&self) -> &Option<String> {
92+
&self.date
8093
}
8194
}
8295

@@ -86,35 +99,117 @@ impl VulnerabilityTrait for Vulnerability {
8699
// Metrics are not implemented in CSAF 2.0
87100
type MetricType = ();
88101
type ThreatType = Threat;
102+
type FlagType = Flag;
103+
type InvolvementType = Involvement;
89104

90-
fn get_remediations(&self) -> Vec<Self::RemediationType> {
91-
self.remediations.clone()
105+
fn get_remediations(&self) -> &Vec<Self::RemediationType> {
106+
&self.remediations
92107
}
93108

94-
fn get_product_status(&self) -> Option<Self::ProductStatusType> {
95-
self.product_status.clone()
109+
fn get_product_status(&self) -> &Option<Self::ProductStatusType> {
110+
&self.product_status
96111
}
97112

98-
fn get_metrics(&self) -> Option<Vec<Self::MetricType>> {
113+
fn get_metrics(&self) -> &Option<Vec<Self::MetricType>> {
99114
// Metrics are not implemented in CSAF 2.0
100-
None
115+
&None
116+
}
117+
118+
fn get_threats(&self) -> &Vec<Self::ThreatType> {
119+
&self.threats
120+
}
121+
122+
fn get_release_date(&self) -> &Option<String> {
123+
&self.release_date
124+
}
125+
126+
fn get_discovery_date(&self) -> &Option<String> {
127+
&self.discovery_date
128+
}
129+
130+
fn get_flags(&self) -> &Option<Vec<Self::FlagType>> {
131+
&self.flags
101132
}
102133

103-
fn get_threats(&self) -> Vec<Self::ThreatType> {
104-
self.threats.clone()
134+
fn get_involvements(&self) -> &Option<Vec<Self::InvolvementType>> {
135+
&self.involvements
136+
}
137+
}
138+
139+
impl FlagTrait for Flag {
140+
fn get_date(&self) -> &Option<String> {
141+
&self.date
142+
}
143+
}
144+
145+
impl InvolvementTrait for Involvement {
146+
fn get_date(&self) -> &Option<String> {
147+
&self.date
105148
}
106149
}
107150

108151
impl CsafTrait for CommonSecurityAdvisoryFramework {
109152
type VulnerabilityType = Vulnerability;
110153
type ProductTreeType = ProductTree;
154+
type DocumentType = DocumentLevelMetaData;
155+
156+
fn get_product_tree(&self) -> &Option<Self::ProductTreeType> {
157+
&self.product_tree
158+
}
111159

112-
fn get_product_tree(&self) -> Option<Self::ProductTreeType> {
113-
self.product_tree.clone()
160+
fn get_vulnerabilities(&self) -> &Vec<Self::VulnerabilityType> {
161+
&self.vulnerabilities
114162
}
115163

116-
fn get_vulnerabilities(&self) -> Vec<Self::VulnerabilityType> {
117-
self.vulnerabilities.clone()
164+
fn get_document(&self) -> &Self::DocumentType {
165+
&self.document
166+
}
167+
}
168+
169+
impl DocumentTrait for DocumentLevelMetaData {
170+
type TrackingType = Tracking;
171+
172+
fn get_tracking(&self) -> &Self::TrackingType {
173+
&self.tracking
174+
}
175+
}
176+
177+
impl TrackingTrait for Tracking {
178+
type GeneratorType = DocumentGenerator;
179+
type RevisionType = Revision;
180+
181+
fn get_current_release_date(&self) -> &String {
182+
&self.current_release_date
183+
}
184+
185+
fn get_initial_release_date(&self) -> &String {
186+
&self.initial_release_date
187+
}
188+
189+
fn get_generator(&self) -> &Option<Self::GeneratorType> {
190+
&self.generator
191+
}
192+
193+
fn get_revision_history(&self) -> &Vec<Self::RevisionType> {
194+
&self.revision_history
195+
}
196+
}
197+
198+
impl GeneratorTrait for DocumentGenerator {
199+
fn get_date(&self) -> &Option<String> {
200+
&self.date
201+
}
202+
}
203+
204+
impl RevisionTrait for Revision {
205+
fn get_date(&self) -> &String {
206+
&self.date
207+
}
208+
fn get_number(&self) -> &String {
209+
&self.number
210+
}
211+
fn get_summary(&self) -> &String {
212+
&self.summary
118213
}
119214
}
120215

@@ -149,8 +244,8 @@ impl BranchTrait for Branch {
149244
self.branches.as_ref().map(|branches| branches.deref())
150245
}
151246

152-
fn get_product(&self) -> Option<&Self::FullProductNameType> {
153-
self.product.as_ref()
247+
fn get_product(&self) -> &Option<Self::FullProductNameType> {
248+
&self.product
154249
}
155250
}
156251

@@ -159,8 +254,8 @@ impl ProductGroupTrait for ProductGroup {
159254
self.group_id.deref()
160255
}
161256

162-
fn get_product_ids(&self) -> Vec<&String> {
163-
self.product_ids.iter().map(|x| x.deref()).collect()
257+
fn get_product_ids(&self) -> impl Iterator<Item = &String> + '_ {
258+
self.product_ids.iter().map(|x| x.deref())
164259
}
165260
}
166261

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ mod tests {
2323
};
2424

2525
fn mock_document() -> CommonSecurityAdvisoryFramework {
26+
let now = chrono::Utc::now().to_string();
2627
let metadata: DocumentLevelMetaData = DocumentLevelMetaData::builder()
2728
.title("Test")
2829
.category("csaf_base")
@@ -36,13 +37,13 @@ mod tests {
3637
.tracking(
3738
Tracking::builder()
3839
.id("test")
39-
.current_release_date(chrono::Utc::now())
40-
.initial_release_date(chrono::Utc::now())
40+
.current_release_date(now.clone())
41+
.initial_release_date(now.clone())
4142
.status("final")
4243
.version("1")
4344
.revision_history(vec![Revision::builder()
4445
.number("1")
45-
.date(chrono::Utc::now())
46+
.date(now.clone())
4647
.summary("test")
4748
.try_into()
4849
.unwrap()]),

0 commit comments

Comments
 (0)