Skip to content

Commit 297d121

Browse files
authored
Merge branch 'master' into refactor-forbid-use-of-unwrap-and-expect
2 parents cfc0c6b + 79c8155 commit 297d121

File tree

9 files changed

+75
-46
lines changed

9 files changed

+75
-46
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ repos:
44
hooks:
55
- id: fmt
66
- id: cargo-check
7+
always_run: true
78
- id: clippy
89

910
- repo: https://github.com/pre-commit/pre-commit-hooks

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ tokio = { version = "1.43.0", features = ["full"] }
3333
tower-lsp = "0.20.0"
3434
tracing = "0.1.41"
3535
tracing-subscriber = "0.3.19"
36+
version-compare = "0.2.0"
3637

3738
[dev-dependencies]
3839
rstest = "0.21.0"

Justfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ test:
77

88
fix:
99
cargo fix --allow-staged --allow-dirty
10+
cargo machete --fix
1011

1112
fmt:
1213
cargo fmt

src/domain/scanresult/package.rs

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::domain::scanresult::package_type::PackageType;
44
use crate::domain::scanresult::severity::Severity;
55
use crate::domain::scanresult::vulnerability::Vulnerability;
66
use crate::domain::scanresult::weak_hash::WeakHash;
7-
use semver::Version;
7+
use std::cmp::Ordering;
88
use std::collections::{HashMap, HashSet};
99
use std::fmt::Debug;
1010
use std::hash::{Hash, Hasher};
@@ -13,7 +13,7 @@ use std::sync::{Arc, RwLock};
1313
pub struct Package {
1414
package_type: PackageType,
1515
name: String,
16-
version: Version,
16+
version: String,
1717
path: String,
1818
found_in_layer: Arc<Layer>,
1919
vulnerabilities: RwLock<HashSet<WeakHash<Vulnerability>>>,
@@ -36,7 +36,7 @@ impl Package {
3636
pub(in crate::domain::scanresult) fn new(
3737
package_type: PackageType,
3838
name: String,
39-
version: Version,
39+
version: String,
4040
path: String,
4141
found_in_layer: Arc<Layer>,
4242
) -> Self {
@@ -59,7 +59,7 @@ impl Package {
5959
&self.name
6060
}
6161

62-
pub fn version(&self) -> &Version {
62+
pub fn version(&self) -> &String {
6363
&self.version
6464
}
6565

@@ -111,13 +111,13 @@ impl Package {
111111
.collect()
112112
}
113113

114-
pub fn suggested_fix_version(&self) -> Option<Version> {
114+
pub fn suggested_fix_version(&self) -> Option<String> {
115115
let vulnerabilities = self.vulnerabilities();
116116
if vulnerabilities.is_empty() {
117117
return None;
118118
}
119119

120-
let candidate_versions: Vec<Version> = vulnerabilities
120+
let candidate_versions: Vec<String> = vulnerabilities
121121
.iter()
122122
.filter_map(|vuln| vuln.fix_version().cloned())
123123
.collect::<HashSet<_>>()
@@ -137,7 +137,7 @@ impl Package {
137137
Severity::Unknown,
138138
];
139139

140-
let mut scores: HashMap<Version, HashMap<Severity, usize>> = HashMap::new();
140+
let mut scores: HashMap<String, HashMap<Severity, usize>> = HashMap::new();
141141

142142
for candidate in &candidate_versions {
143143
let mut score: HashMap<Severity, usize> = HashMap::new();
@@ -171,7 +171,16 @@ impl Package {
171171
}
172172

173173
// If scores are identical, lower version is better
174-
a.cmp(b)
174+
if version_compare::compare_to(a, b, version_compare::Cmp::Eq).unwrap_or(false) {
175+
return Ordering::Equal;
176+
}
177+
if version_compare::compare_to(a, b, version_compare::Cmp::Le).unwrap_or(false) {
178+
return Ordering::Less;
179+
}
180+
if version_compare::compare_to(a, b, version_compare::Cmp::Ge).unwrap_or(false) {
181+
return Ordering::Greater;
182+
}
183+
Ordering::Less
175184
});
176185

177186
sorted_candidates.first().cloned()
@@ -231,7 +240,6 @@ mod tests {
231240
use crate::domain::scanresult::vulnerability::Vulnerability;
232241
use chrono::NaiveDate;
233242
use rstest::{fixture, rstest};
234-
use semver::Version;
235243
use std::sync::Arc;
236244

237245
#[fixture]
@@ -249,7 +257,7 @@ mod tests {
249257
Arc::new(Package::new(
250258
PackageType::Os,
251259
"a_name".to_string(),
252-
Version::parse(version).unwrap(),
260+
version.to_string(),
253261
"a_path".to_string(),
254262
layer,
255263
))
@@ -266,7 +274,7 @@ mod tests {
266274
NaiveDate::from_ymd_opt(2023, 1, 1).unwrap(),
267275
None,
268276
false,
269-
fix_version.map(|v| Version::parse(v).unwrap()),
277+
fix_version.map(|v| v.to_string()),
270278
))
271279
}
272280

@@ -306,20 +314,41 @@ mod tests {
306314
a_vulnerability("CVE-2022-41724", Severity::Medium, Some("2.9.0")),
307315
a_vulnerability("CVE-2022-41725", Severity::Medium, Some("2.9.0")),
308316
], Some("2.9.0"))]
317+
#[case("handles_debian_version", "1.1.35-1.2+deb13u2", vec![
318+
a_vulnerability("CVE-1", Severity::High, Some("1.1.35-1.2+deb13u3")),
319+
a_vulnerability("CVE-2", Severity::High, Some("1.1.35-1.3")),
320+
], Some("1.1.35-1.2+deb13u3"))]
321+
#[case("chooses_lower_version_with_debian_tilde", "257.8-1~deb13u1", vec![
322+
a_vulnerability("CVE-1", Severity::High, Some("257.8-1~deb13u2")),
323+
a_vulnerability("CVE-2", Severity::High, Some("257.8-1~deb13u3")),
324+
], Some("257.8-1~deb13u2"))]
325+
#[case("handles_jre_and_android_versions", "31.1-jre", vec![
326+
a_vulnerability("CVE-1", Severity::High, Some("32.0.0-android")),
327+
], Some("32.0.0-android"))]
328+
#[case("handles_api_version", "31.0-api", vec![a_vulnerability("CVE-1", Severity::High, Some("31.1-api"))], Some("31.1-api"))]
329+
#[case("handles_build_metadata_version", "1.0.15-1+b3", vec![a_vulnerability("CVE-1", Severity::High, Some("1.0.15-2"))], Some("1.0.15-2"))]
330+
#[case("handles_simple_float_version", "2.6", vec![a_vulnerability("CVE-1", Severity::High, Some("2.7"))], Some("2.7"))]
331+
#[case("handles_revision_version", "1.7.0-5", vec![a_vulnerability("CVE-1", Severity::High, Some("1.7.0-6"))], Some("1.7.0-6"))]
332+
#[case("handles_date_based_version", "6.5+20250216-2", vec![a_vulnerability("CVE-1", Severity::High, Some("6.5+20250216-3"))], Some("6.5+20250216-3"))]
333+
#[case("handles_jenkins_version", "3107.v665000b_51092", vec![a_vulnerability("CVE-1", Severity::High, Some("3107.v665000b_51093"))], Some("3107.v665000b_51093"))]
334+
#[case("handles_dot_separated_version", "3206.3208", vec![a_vulnerability("CVE-1", Severity::High, Some("3206.3209"))], Some("3206.3209"))]
335+
#[case("handles_complex_debian_version", "2.12.7+dfsg+really2.9.14-2.1+deb13u1", vec![a_vulnerability("CVE-1", Severity::High, Some("2.12.7+dfsg+really2.9.14-2.1+deb13u2"))], Some("2.12.7+dfsg+really2.9.14-2.1+deb13u2"))]
309336
fn test_suggested_fix_version(
310337
#[case] _description: &str,
311338
#[case] version: &str,
312339
#[with(version)] package: Arc<Package>,
313340
#[case] vulnerabilities: Vec<Arc<Vulnerability>>,
314341
#[case] expected_fix: Option<&str>,
315342
) {
316-
assert_eq!(package.version(), &Version::parse(version).unwrap());
343+
assert_eq!(package.version(), &version);
317344

318345
for vuln in &vulnerabilities {
319346
package.add_vulnerability_found(vuln.clone());
320347
}
321348

322-
let expected = expected_fix.map(|v| Version::parse(v).unwrap());
323-
assert_eq!(package.suggested_fix_version(), expected);
349+
assert_eq!(
350+
package.suggested_fix_version(),
351+
expected_fix.map(|x| x.to_string())
352+
);
324353
}
325354
}

src/domain/scanresult/scan_result.rs

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ use crate::domain::scanresult::severity::Severity;
1414
use crate::domain::scanresult::vulnerability::Vulnerability;
1515
use chrono::{DateTime, NaiveDate, Utc};
1616
use itertools::Itertools;
17-
use semver::Version;
1817
use std::collections::HashMap;
1918
use std::sync::Arc;
2019

@@ -110,7 +109,7 @@ impl ScanResult {
110109
&mut self,
111110
package_type: PackageType,
112111
name: String,
113-
version: Version,
112+
version: String,
114113
path: String,
115114
found_in_layer: Arc<Layer>,
116115
) -> Arc<Package> {
@@ -141,7 +140,7 @@ impl ScanResult {
141140
disclosure_date: NaiveDate,
142141
solution_date: Option<NaiveDate>,
143142
exploitable: bool,
144-
fix_version: Option<Version>,
143+
fix_version: Option<String>,
145144
) -> Arc<Vulnerability> {
146145
self.vulnerabilities
147146
.entry(cve.clone())
@@ -314,7 +313,7 @@ mod tests {
314313
let package = scan_result.add_package(
315314
PackageType::Os,
316315
"musl".to_string(),
317-
Version::parse("1.2.3").unwrap(),
316+
"1.2.3".to_string(),
318317
"/lib/ld-musl-x86_64.so.1".to_string(),
319318
layer.clone(),
320319
);
@@ -337,7 +336,7 @@ mod tests {
337336
Utc::now().naive_utc().date(),
338337
None,
339338
false,
340-
Some(Version::parse("1.2.4").unwrap()),
339+
Some("1.2.4".to_string()),
341340
);
342341

343342
assert_eq!(scan_result.vulnerabilities().len(), 1);
@@ -358,7 +357,7 @@ mod tests {
358357
let package = scan_result.add_package(
359358
PackageType::Os,
360359
"musl".to_string(),
361-
Version::parse("1.2.3").unwrap(),
360+
"1.2.3".to_string(),
362361
"/lib/ld-musl-x86_64.so.1".to_string(),
363362
layer.clone(),
364363
);
@@ -368,7 +367,7 @@ mod tests {
368367
Utc::now().naive_utc().date(),
369368
None,
370369
false,
371-
Some(Version::parse("1.2.4").unwrap()),
370+
Some("1.2.4".to_string()),
372371
);
373372

374373
package.add_vulnerability_found(vuln.clone());
@@ -463,7 +462,7 @@ mod tests {
463462
Utc::now().naive_utc().date(),
464463
None,
465464
false,
466-
Some(Version::parse("1.2.4").unwrap()),
465+
Some("1.2.4".to_string()),
467466
);
468467

469468
vuln.add_accepted_risk(risk.clone());
@@ -489,7 +488,7 @@ mod tests {
489488
let package = scan_result.add_package(
490489
PackageType::Os,
491490
"musl".to_string(),
492-
Version::parse("1.2.3").unwrap(),
491+
"1.2.3".to_string(),
493492
"/lib/ld-musl-x86_64.so.1".to_string(),
494493
layer.clone(),
495494
);
@@ -572,13 +571,13 @@ mod tests {
572571
let package = scan_result.add_package(
573572
PackageType::Os,
574573
"musl".to_string(),
575-
Version::parse("1.2.3").unwrap(),
574+
"1.2.3".to_string(),
576575
"/path".to_string(),
577576
layer.clone(),
578577
);
579578
assert_eq!(package.package_type(), &PackageType::Os);
580579
assert_eq!(package.name(), "musl");
581-
assert_eq!(package.version(), &Version::parse("1.2.3").unwrap());
580+
assert_eq!(package.version(), "1.2.3");
582581
assert_eq!(package.path(), "/path");
583582
assert!(format!("{:?}", package).contains("musl"));
584583
assert_eq!(package.clone(), package);
@@ -590,15 +589,15 @@ mod tests {
590589
now.naive_utc().date(),
591590
Some(now.naive_utc().date()),
592591
true,
593-
Some(Version::parse("1.2.4").unwrap()),
592+
Some("1.2.4".to_string()),
594593
);
595594
assert_eq!(vuln.cve(), "CVE-1");
596595
assert_eq!(vuln.severity(), Severity::High);
597596
assert_eq!(vuln.disclosure_date(), now.naive_utc().date());
598597
assert_eq!(vuln.solution_date(), Some(now.naive_utc().date()));
599598
assert!(vuln.exploitable());
600599
assert!(vuln.fixable());
601-
assert_eq!(vuln.fix_version(), Some(&Version::parse("1.2.4").unwrap()));
600+
assert_eq!(vuln.fix_version(), Some(&"1.2.4".to_string()));
602601
assert!(format!("{:?}", vuln).contains("CVE-1"));
603602

604603
// AcceptedRisk
@@ -673,14 +672,14 @@ mod tests {
673672
let pkg = scan_result.add_package(
674673
PackageType::Os,
675674
"pkg".to_string(),
676-
Version::parse("1.0.0").unwrap(),
675+
"1.0.0".to_string(),
677676
"/path".to_string(),
678677
layer.clone(),
679678
);
680679
let pkg2 = scan_result.add_package(
681680
PackageType::Os,
682681
"pkg".to_string(),
683-
Version::parse("1.0.0").unwrap(),
682+
"1.0.0".to_string(),
684683
"/path".to_string(),
685684
layer.clone(),
686685
);

src/domain/scanresult/vulnerability.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use crate::domain::scanresult::package::Package;
44
use crate::domain::scanresult::severity::Severity;
55
use crate::domain::scanresult::weak_hash::WeakHash;
66
use chrono::NaiveDate;
7-
use semver::Version;
87
use std::collections::HashSet;
98
use std::fmt::Debug;
109
use std::hash::{Hash, Hasher};
@@ -16,7 +15,7 @@ pub struct Vulnerability {
1615
disclosure_date: NaiveDate,
1716
solution_date: Option<NaiveDate>,
1817
exploitable: bool,
19-
fix_version: Option<Version>,
18+
fix_version: Option<String>,
2019
found_in_packages: RwLock<HashSet<WeakHash<Package>>>,
2120
accepted_risks: RwLock<HashSet<WeakHash<AcceptedRisk>>>,
2221
}
@@ -41,7 +40,7 @@ impl Vulnerability {
4140
disclosure_date: NaiveDate,
4241
solution_date: Option<NaiveDate>,
4342
exploitable: bool,
44-
fix_version: Option<Version>,
43+
fix_version: Option<String>,
4544
) -> Self {
4645
Self {
4746
cve,
@@ -79,7 +78,7 @@ impl Vulnerability {
7978
self.fix_version.is_some()
8079
}
8180

82-
pub fn fix_version(&self) -> Option<&Version> {
81+
pub fn fix_version(&self) -> Option<&String> {
8382
self.fix_version.as_ref()
8483
}
8584

0 commit comments

Comments
 (0)