Skip to content

Commit 7037eed

Browse files
author
prasanth_j
committed
feat: implement unified dependency collection for container engine
1 parent 90deab7 commit 7037eed

4 files changed

Lines changed: 201 additions & 66 deletions

File tree

crates/scanr-cli/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,7 @@ async fn main() {
584584
println!("Scanr Container Scan");
585585
println!("Engine: {}", result.metadata.engine_name);
586586
println!("Target: {}", result.metadata.target);
587-
println!("Status: placeholder implementation (C1 skeleton)");
587+
println!("Status: dependency composition scan");
588588
println!("Dependencies discovered: {}", result.metadata.total_dependencies);
589589
println!("Findings: {}", result.findings.len());
590590
}

crates/scanr-container/src/lib.rs

Lines changed: 74 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::process::Command;
66
use std::time::{Duration, Instant};
77

88
use 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};
1010
use serde::Deserialize;
1111
use tempfile::TempDir;
1212
use 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)]
6760
struct 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+
912945
fn dedupe_sca_dependencies(dependencies: Vec<ScaDependency>) -> Vec<ScaDependency> {
913946
let mut seen = BTreeSet::new();
914947
let mut output = Vec::new();

0 commit comments

Comments
 (0)