@@ -4,7 +4,7 @@ use crate::domain::scanresult::package_type::PackageType;
44use crate :: domain:: scanresult:: severity:: Severity ;
55use crate :: domain:: scanresult:: vulnerability:: Vulnerability ;
66use crate :: domain:: scanresult:: weak_hash:: WeakHash ;
7- use semver :: Version ;
7+ use std :: cmp :: Ordering ;
88use std:: collections:: { HashMap , HashSet } ;
99use std:: fmt:: Debug ;
1010use std:: hash:: { Hash , Hasher } ;
@@ -13,7 +13,7 @@ use std::sync::{Arc, RwLock};
1313pub 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}
0 commit comments