@@ -6,7 +6,7 @@ use std::process::Command;
66use std:: time:: { Duration , Instant } ;
77
88use scanr_engine:: { EngineError , EngineType , ScanEngine , ScanInput , ScanMetadata , ScanResult } ;
9- use scanr_sca:: { Dependency as ScaDependency , ScaEngine } ;
9+ use scanr_sca:: { Dependency as ScaDependency , Ecosystem as ScaEcosystem , ScaEngine } ;
1010use serde:: Deserialize ;
1111use tempfile:: TempDir ;
1212use walkdir:: WalkDir ;
@@ -56,13 +56,6 @@ pub struct OsDependency {
5656 pub version : String ,
5757}
5858
59- #[ derive( Debug , Clone , PartialEq , Eq , PartialOrd , Ord ) ]
60- struct ContainerDependency {
61- ecosystem : String ,
62- name : String ,
63- version : String ,
64- }
65-
6659#[ derive( Debug ) ]
6760struct AcquiredImage {
6861 source_mode : ImageSourceMode ,
@@ -138,22 +131,27 @@ impl ScanEngine for ContainerEngine {
138131 let acquired = self . acquire_image ( input) ?;
139132 let rootfs = self . build_rootfs ( & acquired. image_extract_path ) ?;
140133 let distro = self . detect_distro ( & rootfs. path ) ;
141- let os_dependencies = self . extract_os_dependencies ( & rootfs. path , distro) ?;
142- let app_dependencies = self . discover_application_dependencies ( & rootfs. path ) ?;
143- let merged_dependencies = self . merge_dependencies ( & os_dependencies, & app_dependencies) ;
144-
145- let _ = & self . sca_engine ;
146- let _ = rootfs. path . as_path ( ) ;
134+ let dependencies = self . collect_all_dependencies ( & rootfs. path , distro) ?;
135+ let sca_result = self . resolve_with_sca_dependencies (
136+ dependencies,
137+ & acquired. target_display ,
138+ & rootfs. path ,
139+ ) ?;
140+ let mut findings = scanr_sca:: findings_from_scan_result ( & sca_result) ;
141+ for finding in & mut findings {
142+ finding. engine = EngineType :: Container ;
143+ finding. location = Some ( acquired. target_display . clone ( ) ) ;
144+ }
147145 let _ = acquired. source_mode ;
148146
149147 Ok ( ScanResult {
150- findings : Vec :: new ( ) ,
148+ findings,
151149 metadata : ScanMetadata {
152150 engine : EngineType :: Container ,
153151 engine_name : self . name ( ) . to_string ( ) ,
154- target : acquired. target_display ,
155- total_dependencies : merged_dependencies . len ( ) ,
156- total_vulnerabilities : 0 ,
152+ target : acquired. target_display . clone ( ) ,
153+ total_dependencies : sca_result . total_dependencies as usize ,
154+ total_vulnerabilities : sca_result . vulnerabilities . len ( ) ,
157155 } ,
158156 } )
159157 }
@@ -399,6 +397,33 @@ impl ContainerEngine {
399397 }
400398 }
401399
400+ fn collect_all_dependencies (
401+ & self ,
402+ rootfs_path : & Path ,
403+ distro : Distro ,
404+ ) -> Result < Vec < ScaDependency > , EngineError > {
405+ let os_dependencies = self . extract_os_dependencies ( rootfs_path, distro) ?;
406+ let app_dependencies = self . discover_application_dependencies ( rootfs_path) ?;
407+
408+ let mut merged = app_dependencies;
409+ for dependency in os_dependencies {
410+ let ecosystem = ecosystem_from_os_label ( & dependency. ecosystem ) . ok_or_else ( || {
411+ EngineError :: new ( format ! (
412+ "unsupported OS ecosystem mapping '{}'" ,
413+ dependency. ecosystem
414+ ) )
415+ } ) ?;
416+ merged. push ( ScaDependency {
417+ ecosystem,
418+ name : dependency. name ,
419+ version : dependency. version ,
420+ direct : false ,
421+ } ) ;
422+ }
423+
424+ Ok ( dedupe_sca_dependencies ( merged) )
425+ }
426+
402427 fn discover_application_dependencies (
403428 & self ,
404429 rootfs_path : & Path ,
@@ -440,30 +465,28 @@ impl ContainerEngine {
440465 manifests
441466 }
442467
443- fn merge_dependencies (
468+ fn resolve_with_sca_dependencies (
444469 & self ,
445- os_dependencies : & [ OsDependency ] ,
446- app_dependencies : & [ ScaDependency ] ,
447- ) -> Vec < ContainerDependency > {
448- let mut set = BTreeSet :: new ( ) ;
449-
450- for dependency in os_dependencies {
451- set. insert ( ContainerDependency {
452- ecosystem : dependency. ecosystem . clone ( ) ,
453- name : dependency. name . clone ( ) ,
454- version : dependency. version . clone ( ) ,
455- } ) ;
456- }
457-
458- for dependency in app_dependencies {
459- set. insert ( ContainerDependency {
460- ecosystem : dependency. ecosystem . to_string ( ) ,
461- name : dependency. name . clone ( ) ,
462- version : dependency. version . clone ( ) ,
463- } ) ;
464- }
465-
466- set. into_iter ( ) . collect ( )
470+ dependencies : Vec < ScaDependency > ,
471+ target : & str ,
472+ rootfs_path : & Path ,
473+ ) -> Result < scanr_sca:: ScanResult , EngineError > {
474+ let query_options = scanr_sca:: VulnerabilityQueryOptions {
475+ cache_base_path : Some ( rootfs_path. to_path_buf ( ) ) ,
476+ cache_enabled : false ,
477+ cache_ttl_hours : 24 ,
478+ offline : false ,
479+ force_refresh : false ,
480+ } ;
481+
482+ self . sca_engine
483+ . resolve_dependencies_with_query_options (
484+ dependencies,
485+ query_options,
486+ target. to_string ( ) ,
487+ rootfs_path. display ( ) . to_string ( ) ,
488+ )
489+ . map_err ( |error| EngineError :: new ( format ! ( "sca dependency resolution failed: {error}" ) ) )
467490 }
468491
469492 fn extract_alpine_packages ( & self , rootfs_path : & Path ) -> Result < Vec < OsDependency > , EngineError > {
@@ -909,6 +932,16 @@ fn looks_distroless(rootfs_path: &Path) -> bool {
909932 !has_common_package_managers && !has_shell
910933}
911934
935+ fn ecosystem_from_os_label ( label : & str ) -> Option < ScaEcosystem > {
936+ match label. to_ascii_lowercase ( ) . as_str ( ) {
937+ "alpine" => Some ( ScaEcosystem :: Alpine ) ,
938+ "debian" => Some ( ScaEcosystem :: Debian ) ,
939+ "ubuntu" => Some ( ScaEcosystem :: Ubuntu ) ,
940+ "rhel" => Some ( ScaEcosystem :: Rhel ) ,
941+ _ => None ,
942+ }
943+ }
944+
912945fn dedupe_sca_dependencies ( dependencies : Vec < ScaDependency > ) -> Vec < ScaDependency > {
913946 let mut seen = BTreeSet :: new ( ) ;
914947 let mut output = Vec :: new ( ) ;
0 commit comments