@@ -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 ( ) ;
@@ -168,7 +168,16 @@ impl Package {
168168 }
169169
170170 // If scores are identical, lower version is better
171- a. cmp ( b)
171+ if version_compare:: compare_to ( a, b, version_compare:: Cmp :: Eq ) . unwrap_or ( false ) {
172+ return Ordering :: Equal ;
173+ }
174+ if version_compare:: compare_to ( a, b, version_compare:: Cmp :: Le ) . unwrap_or ( false ) {
175+ return Ordering :: Less ;
176+ }
177+ if version_compare:: compare_to ( a, b, version_compare:: Cmp :: Ge ) . unwrap_or ( false ) {
178+ return Ordering :: Greater ;
179+ }
180+ Ordering :: Less
172181 } ) ;
173182
174183 sorted_candidates. first ( ) . cloned ( )
@@ -218,7 +227,6 @@ mod tests {
218227 use crate :: domain:: scanresult:: vulnerability:: Vulnerability ;
219228 use chrono:: NaiveDate ;
220229 use rstest:: { fixture, rstest} ;
221- use semver:: Version ;
222230 use std:: sync:: Arc ;
223231
224232 #[ fixture]
@@ -236,7 +244,7 @@ mod tests {
236244 Arc :: new ( Package :: new (
237245 PackageType :: Os ,
238246 "a_name" . to_string ( ) ,
239- Version :: parse ( version) . unwrap ( ) ,
247+ version. to_string ( ) ,
240248 "a_path" . to_string ( ) ,
241249 layer,
242250 ) )
@@ -253,7 +261,7 @@ mod tests {
253261 NaiveDate :: from_ymd_opt ( 2023 , 1 , 1 ) . unwrap ( ) ,
254262 None ,
255263 false ,
256- fix_version. map ( |v| Version :: parse ( v ) . unwrap ( ) ) ,
264+ fix_version. map ( |v| v . to_string ( ) ) ,
257265 ) )
258266 }
259267
@@ -293,20 +301,41 @@ mod tests {
293301 a_vulnerability( "CVE-2022-41724" , Severity :: Medium , Some ( "2.9.0" ) ) ,
294302 a_vulnerability( "CVE-2022-41725" , Severity :: Medium , Some ( "2.9.0" ) ) ,
295303 ] , Some ( "2.9.0" ) ) ]
304+ #[ case( "handles_debian_version" , "1.1.35-1.2+deb13u2" , vec![
305+ a_vulnerability( "CVE-1" , Severity :: High , Some ( "1.1.35-1.2+deb13u3" ) ) ,
306+ a_vulnerability( "CVE-2" , Severity :: High , Some ( "1.1.35-1.3" ) ) ,
307+ ] , Some ( "1.1.35-1.2+deb13u3" ) ) ]
308+ #[ case( "chooses_lower_version_with_debian_tilde" , "257.8-1~deb13u1" , vec![
309+ a_vulnerability( "CVE-1" , Severity :: High , Some ( "257.8-1~deb13u2" ) ) ,
310+ a_vulnerability( "CVE-2" , Severity :: High , Some ( "257.8-1~deb13u3" ) ) ,
311+ ] , Some ( "257.8-1~deb13u2" ) ) ]
312+ #[ case( "handles_jre_and_android_versions" , "31.1-jre" , vec![
313+ a_vulnerability( "CVE-1" , Severity :: High , Some ( "32.0.0-android" ) ) ,
314+ ] , Some ( "32.0.0-android" ) ) ]
315+ #[ case( "handles_api_version" , "31.0-api" , vec![ a_vulnerability( "CVE-1" , Severity :: High , Some ( "31.1-api" ) ) ] , Some ( "31.1-api" ) ) ]
316+ #[ 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" ) ) ]
317+ #[ case( "handles_simple_float_version" , "2.6" , vec![ a_vulnerability( "CVE-1" , Severity :: High , Some ( "2.7" ) ) ] , Some ( "2.7" ) ) ]
318+ #[ 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" ) ) ]
319+ #[ 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" ) ) ]
320+ #[ case( "handles_jenkins_version" , "3107.v665000b_51092" , vec![ a_vulnerability( "CVE-1" , Severity :: High , Some ( "3107.v665000b_51093" ) ) ] , Some ( "3107.v665000b_51093" ) ) ]
321+ #[ case( "handles_dot_separated_version" , "3206.3208" , vec![ a_vulnerability( "CVE-1" , Severity :: High , Some ( "3206.3209" ) ) ] , Some ( "3206.3209" ) ) ]
322+ #[ 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" ) ) ]
296323 fn test_suggested_fix_version (
297324 #[ case] _description : & str ,
298325 #[ case] version : & str ,
299326 #[ with( version) ] package : Arc < Package > ,
300327 #[ case] vulnerabilities : Vec < Arc < Vulnerability > > ,
301328 #[ case] expected_fix : Option < & str > ,
302329 ) {
303- assert_eq ! ( package. version( ) , & Version :: parse ( version) . unwrap ( ) ) ;
330+ assert_eq ! ( package. version( ) , & version) ;
304331
305332 for vuln in & vulnerabilities {
306333 package. add_vulnerability_found ( vuln. clone ( ) ) ;
307334 }
308335
309- let expected = expected_fix. map ( |v| Version :: parse ( v) . unwrap ( ) ) ;
310- assert_eq ! ( package. suggested_fix_version( ) , expected) ;
336+ assert_eq ! (
337+ package. suggested_fix_version( ) ,
338+ expected_fix. map( |x| x. to_string( ) )
339+ ) ;
311340 }
312341}
0 commit comments