Skip to content

Commit 2c13a74

Browse files
committed
refactor: ubuntu ecosystem tweaks using regex
- introduced lazy_regex to help remove complexity from ubuntu ecosystem parsing - added test cases to ensure coverage or formats in the osv datastore
1 parent 18e0ca2 commit 2c13a74

File tree

2 files changed

+113
-83
lines changed

2 files changed

+113
-83
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ reqwest = { version = "0.12", features = ["json"], optional = true }
2121
tokio = { version = "1", features = ["full"], optional = true }
2222
thiserror = { version = "2.0", optional = true }
2323
url = { version = "2.3.1", optional = true }
24+
lazy-regex = "3.4.1"
2425

2526
[dev-dependencies]
2627
comfy-table = "7.1.1"

src/schema.rs

Lines changed: 112 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use chrono::{DateTime, Utc};
2+
use lazy_regex::regex_switch;
23
use serde::de::{self, Visitor};
34
use serde::{Deserialize, Deserializer, Serialize, Serializer};
5+
46
/// Package identifies the code library or command that
57
/// is potentially affected by a particular vulnerability.
68
#[derive(Debug, Serialize, Deserialize, Clone)]
@@ -67,6 +69,8 @@ pub enum Ecosystem {
6769
SwiftURL,
6870
Ubuntu {
6971
version: String,
72+
metadata: Option<String>,
73+
fips: Option<String>,
7074
pro: bool,
7175
lts: bool,
7276
},
@@ -145,25 +149,29 @@ impl Serialize for Ecosystem {
145149
}
146150
Ecosystem::SwiftURL => serializer.serialize_str("SwiftURL"),
147151
Ecosystem::Ubuntu {
148-
version: v,
149-
pro: true,
150-
lts: true,
151-
} => serializer.serialize_str(&format!("Ubuntu:Pro:{}:LTS", v)),
152-
Ecosystem::Ubuntu {
153-
version: v,
154-
pro: true,
155-
lts: false,
156-
} => serializer.serialize_str(&format!("Ubuntu:Pro:{}", v)),
157-
Ecosystem::Ubuntu {
158-
version: v,
159-
pro: false,
160-
lts: true,
161-
} => serializer.serialize_str(&format!("Ubuntu:{}:LTS", v)),
162-
Ecosystem::Ubuntu {
163-
version: v,
164-
pro: false,
165-
lts: false,
166-
} => serializer.serialize_str(&format!("Ubuntu:{}", v)),
152+
version,
153+
pro,
154+
lts,
155+
metadata,
156+
fips,
157+
} => {
158+
let mut parts: Vec<String> = vec!["Ubuntu".to_string()];
159+
if *pro {
160+
parts.push("Pro".to_string());
161+
}
162+
if let Some(stream) = fips {
163+
parts.push(stream.clone());
164+
}
165+
parts.push(version.clone());
166+
if *lts {
167+
parts.push("LTS".to_string());
168+
}
169+
if let Some(meta) = metadata {
170+
parts.push(meta.clone());
171+
}
172+
let serialized = parts.join(":");
173+
serializer.serialize_str(&serialized)
174+
}
167175
Ecosystem::UVI => serializer.serialize_str("UVI"),
168176
}
169177
}
@@ -176,7 +184,7 @@ impl<'de> Deserialize<'de> for Ecosystem {
176184
{
177185
struct EcosystemVisitor;
178186

179-
impl<'de> Visitor<'de> for EcosystemVisitor {
187+
impl Visitor<'_> for EcosystemVisitor {
180188
type Value = Ecosystem;
181189

182190
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
@@ -257,67 +265,19 @@ impl<'de> Deserialize<'de> for Ecosystem {
257265
value.strip_prefix("SUSE:").map(|v| v.to_string()),
258266
)),
259267
"SwiftURL" => Ok(Ecosystem::SwiftURL),
260-
_ if value.starts_with("Ubuntu:Pro:") => {
261-
value.strip_prefix("Ubuntu:Pro:").map_or(
262-
Err(de::Error::unknown_variant(value, &["Ecosystem"])),
263-
|v| {
264-
let parts: Vec<&str> = v.split(':').collect();
265-
match parts.as_slice() {
266-
[_, ver, "LTS"] => Ok(Ecosystem::Ubuntu {
267-
version: ver.to_string(),
268-
pro: true,
269-
lts: true,
270-
}),
271-
[ver, "LTS"] => Ok(Ecosystem::Ubuntu {
272-
version: ver.to_string(),
273-
pro: true,
274-
lts: true,
275-
}),
276-
[ver, "LTS", ..] => Ok(Ecosystem::Ubuntu {
277-
version: ver.to_string(),
278-
pro: true,
279-
lts: true,
280-
}),
281-
[ver] => Ok(Ecosystem::Ubuntu {
282-
version: ver.to_string(),
283-
pro: true,
284-
lts: false,
285-
}),
286-
_ => Err(de::Error::unknown_variant(
287-
value,
288-
&["Ecosystem", "Ubuntu:Pro:YY.MM:(LTS?)"],
289-
)),
268+
_ if value.starts_with("Ubuntu:") => {
269+
regex_switch!(value,
270+
r#"^Ubuntu(?::Pro)?(?::(?<fips>FIPS(?:-preview|-updates)?))?:(?<version>\d+\.\d+)(?::LTS)?(?::for:(?<specialized>.+))?$"# => {
271+
Ecosystem::Ubuntu {
272+
version: version.to_string(),
273+
metadata: (!specialized.is_empty()).then_some(specialized.to_string()),
274+
fips: (!fips.is_empty()).then_some(fips.to_string()),
275+
pro: value.contains(":Pro"),
276+
lts: value.contains(":LTS"),
290277
}
291-
},
292-
)
293-
}
294-
_ if value.starts_with("Ubuntu:") => value.strip_prefix("Ubuntu:").map_or(
295-
Err(de::Error::unknown_variant(value, &["Ecosystem"])),
296-
|v| {
297-
let parts: Vec<&str> = v.split(':').collect();
298-
match parts.as_slice() {
299-
[ver, "LTS"] => Ok(Ecosystem::Ubuntu {
300-
version: ver.to_string(),
301-
pro: false,
302-
lts: true,
303-
}),
304-
[ver, "LTS", ..] => Ok(Ecosystem::Ubuntu {
305-
version: ver.to_string(),
306-
pro: true,
307-
lts: true,
308-
}),
309-
[ver] => Ok(Ecosystem::Ubuntu {
310-
version: ver.to_string(),
311-
pro: false,
312-
lts: false,
313-
}),
314-
_ => Err(de::Error::unknown_variant(
315-
value,
316-
&["Ecosystem", "Ubuntu:YY.MM:(?LTS)"],
317-
)),
318278
}
319-
},
320-
),
279+
).ok_or(de::Error::unknown_variant(value, &["Ecosystem"]))
280+
}
321281
"UVI" => Ok(Ecosystem::UVI),
322282
_ => Err(de::Error::unknown_variant(value, &["Ecosystem"])),
323283
}
@@ -734,6 +694,8 @@ mod tests {
734694
version: "20.04".to_string(),
735695
pro: true,
736696
lts: true,
697+
fips: None,
698+
metadata: None,
737699
};
738700
let as_json = serde_json::json!(ubuntu);
739701
assert_eq!(as_json, serde_json::json!("Ubuntu:Pro:20.04:LTS"));
@@ -742,6 +704,8 @@ mod tests {
742704
version: "20.04".to_string(),
743705
pro: true,
744706
lts: false,
707+
fips: None,
708+
metadata: None,
745709
};
746710
let as_json = serde_json::json!(ubuntu);
747711
assert_eq!(as_json, serde_json::json!("Ubuntu:Pro:20.04"));
@@ -750,6 +714,8 @@ mod tests {
750714
version: "20.04".to_string(),
751715
pro: false,
752716
lts: true,
717+
fips: None,
718+
metadata: None,
753719
};
754720
let as_json = serde_json::json!(ubuntu);
755721
assert_eq!(as_json, serde_json::json!("Ubuntu:20.04:LTS"));
@@ -758,6 +724,8 @@ mod tests {
758724
version: "20.04".to_string(),
759725
pro: false,
760726
lts: false,
727+
fips: None,
728+
metadata: None,
761729
};
762730
let as_json = serde_json::json!(ubuntu);
763731
assert_eq!(as_json, serde_json::json!("Ubuntu:20.04"));
@@ -769,7 +737,9 @@ mod tests {
769737
Ecosystem::Ubuntu {
770738
version: "20.04".to_string(),
771739
pro: true,
772-
lts: true
740+
lts: true,
741+
fips: None,
742+
metadata: None,
773743
}
774744
);
775745

@@ -780,7 +750,9 @@ mod tests {
780750
Ecosystem::Ubuntu {
781751
version: "20.04".to_string(),
782752
pro: true,
783-
lts: false
753+
lts: false,
754+
fips: None,
755+
metadata: None,
784756
}
785757
);
786758

@@ -791,7 +763,9 @@ mod tests {
791763
Ecosystem::Ubuntu {
792764
version: "20.04".to_string(),
793765
pro: false,
794-
lts: true
766+
lts: true,
767+
fips: None,
768+
metadata: None,
795769
}
796770
);
797771

@@ -802,7 +776,62 @@ mod tests {
802776
Ecosystem::Ubuntu {
803777
version: "20.04".to_string(),
804778
pro: false,
805-
lts: false
779+
lts: false,
780+
fips: None,
781+
metadata: None,
782+
}
783+
);
784+
785+
let json_str = r#""Ubuntu:22.04:LTS:for:NVIDIA:BlueField""#;
786+
let ubuntu: Ecosystem = serde_json::from_str(json_str).unwrap();
787+
assert_eq!(
788+
ubuntu,
789+
Ecosystem::Ubuntu {
790+
version: "22.04".to_string(),
791+
pro: false,
792+
lts: true,
793+
fips: None,
794+
metadata: Some("NVIDIA:BlueField".to_string()),
795+
}
796+
);
797+
798+
let json_str = r#""Ubuntu:Pro:FIPS-preview:22.04:LTS""#;
799+
let ubuntu: Ecosystem = serde_json::from_str(json_str).unwrap();
800+
assert_eq!(
801+
ubuntu,
802+
Ecosystem::Ubuntu {
803+
version: "22.04".to_string(),
804+
pro: true,
805+
lts: true,
806+
fips: Some("FIPS-preview".to_string()),
807+
metadata: None,
808+
}
809+
);
810+
811+
let json_str = r#""Ubuntu:Pro:FIPS-updates:18.04:LTS""#;
812+
let ubuntu: Ecosystem = serde_json::from_str(json_str).unwrap();
813+
assert_eq!(
814+
ubuntu,
815+
Ecosystem::Ubuntu {
816+
version: "18.04".to_string(),
817+
pro: true,
818+
lts: true,
819+
fips: Some("FIPS-updates".to_string()),
820+
metadata: None,
821+
}
822+
);
823+
824+
let json_str = r#""Ubuntu:Pro:FIPS:16.04:LTS""#;
825+
let ubuntu: Ecosystem = serde_json::from_str(json_str).unwrap();
826+
827+
assert_eq!(
828+
ubuntu,
829+
Ecosystem::Ubuntu {
830+
version: "16.04".to_string(),
831+
pro: true,
832+
lts: true,
833+
fips: Some("FIPS".to_string()),
834+
metadata: None,
806835
}
807836
);
808837
}

0 commit comments

Comments
 (0)