Skip to content

Commit 48ad67e

Browse files
authored
fix(domain): handle image layers without digests (#20)
1 parent c5f5b46 commit 48ad67e

File tree

3 files changed

+73
-20
lines changed

3 files changed

+73
-20
lines changed

src/domain/scanresult/scan_result.rs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use std::sync::Arc;
2121
pub struct ScanResult {
2222
scan_type: ScanType,
2323
metadata: Metadata,
24-
layers: HashMap<String, Arc<Layer>>,
24+
layers: Vec<Arc<Layer>>,
2525
packages: HashMap<Arc<Package>, ()>,
2626
vulnerabilities: HashMap<String, Arc<Vulnerability>>,
2727
policies: HashMap<String, Arc<Policy>>,
@@ -54,7 +54,7 @@ impl ScanResult {
5454
labels,
5555
created_at,
5656
),
57-
layers: HashMap::new(),
57+
layers: Vec::new(),
5858
packages: HashMap::new(),
5959
vulnerabilities: HashMap::new(),
6060
policies: HashMap::new(),
@@ -79,19 +79,26 @@ impl ScanResult {
7979
command: String,
8080
) -> Arc<Layer> {
8181
let layer = Arc::new(Layer::new(digest.clone(), index, size, command));
82-
self.layers.insert(digest, layer.clone());
82+
self.layers.push(layer.clone());
8383
layer
8484
}
8585

8686
pub fn find_layer_by_digest(&self, digest: &str) -> Option<Arc<Layer>> {
87-
self.layers.get(digest).cloned()
87+
if digest.trim().is_empty() {
88+
return None;
89+
}
90+
91+
self.layers
92+
.iter()
93+
.find(|l| l.digest() == Some(digest))
94+
.cloned()
8895
}
8996

9097
pub fn layers(&self) -> Vec<Arc<Layer>> {
9198
self.layers
92-
.values()
93-
.cloned()
99+
.iter()
94100
.sorted_by(|a, b| a.index().cmp(&b.index()))
101+
.cloned()
95102
.collect()
96103
}
97104

@@ -651,8 +658,8 @@ mod tests {
651658
// Add layer twice
652659
let layer = scan_result.add_layer("layer-1".to_string(), 0, None, "CMD".to_string());
653660
let layer2 = scan_result.add_layer("layer-1".to_string(), 0, None, "CMD".to_string());
654-
assert_ne!(Arc::as_ptr(&layer), Arc::as_ptr(&layer2)); // It creates a new Arc and replaces.
655-
assert_eq!(scan_result.layers().len(), 1);
661+
assert_ne!(Arc::as_ptr(&layer), Arc::as_ptr(&layer2)); // It creates a new Arc and adds it.
662+
assert_eq!(scan_result.layers().len(), 2);
656663

657664
// Add package twice
658665
let pkg = scan_result.add_package(

src/infra/sysdig_image_scanner_json_scan_result_v1.rs

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,14 @@ impl From<JsonScanResultV1> for ScanResult {
3030
}
3131

3232
fn add_layers(report: &JsonResult, scan_result: &mut ScanResult) {
33-
report
34-
.layers
35-
.values()
36-
.filter(|json_layer| !json_layer.digest.is_empty())
37-
.for_each(|json_layer| {
38-
scan_result.add_layer(
39-
json_layer.digest.clone(),
40-
json_layer.index,
41-
json_layer.size,
42-
json_layer.command.clone().unwrap_or_default(),
43-
);
44-
});
33+
report.layers.values().for_each(|json_layer| {
34+
scan_result.add_layer(
35+
json_layer.digest.clone(),
36+
json_layer.index,
37+
json_layer.size,
38+
json_layer.command.clone().unwrap_or_default(),
39+
);
40+
});
4541
}
4642

4743
fn add_risk_accepts(result: &JsonResult, scan_result: &mut ScanResult) {
@@ -632,4 +628,51 @@ mod tests {
632628
);
633629
// assert_eq!(scan_result.vulnerabilities().len(), 97);
634630
}
631+
632+
#[test]
633+
fn test_handles_layers_without_digest() {
634+
let postgres_13_json = include_bytes!("../../tests/fixtures/scan-results/postgres_13.json");
635+
let json_scan_result: JsonScanResultV1 = serde_json::from_slice(postgres_13_json).unwrap();
636+
let scan_result: ScanResult = json_scan_result.into();
637+
638+
assert_eq!(
639+
scan_result.layers().len(),
640+
25,
641+
"Should have 25 layers in total"
642+
);
643+
644+
let layers_with_digest = scan_result
645+
.layers()
646+
.into_iter()
647+
.filter(|l| l.digest().is_some())
648+
.count();
649+
assert_eq!(
650+
layers_with_digest, 14,
651+
"Should have 14 layers with a digest"
652+
);
653+
654+
let layers_without_digest = scan_result
655+
.layers()
656+
.into_iter()
657+
.filter(|l| l.digest().is_none())
658+
.count();
659+
assert_eq!(
660+
layers_without_digest, 11,
661+
"Should have 11 layers without a digest"
662+
);
663+
664+
assert!(
665+
scan_result.find_layer_by_digest("").is_none(),
666+
"Searching for an empty digest should return None"
667+
);
668+
assert!(
669+
scan_result.find_layer_by_digest(" ").is_none(),
670+
"Searching for a whitespace digest should return None"
671+
);
672+
673+
let digest = "sha256:04d52f0a5b32b0f627bbd4427a0374f0a8d2d409dbbfda0099d89b87c774df36";
674+
let found_layer = scan_result.find_layer_by_digest(digest);
675+
assert!(found_layer.is_some(), "Should find layer by valid digest");
676+
assert_eq!(found_layer.unwrap().digest(), Some(digest));
677+
}
635678
}

tests/fixtures/Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ RUN curl -L https://get.jenkins.io/war-stable/2.401.3/jenkins.war -o /jenkins.wa
88

99
FROM nginx:latest
1010

11+
1112
RUN apt update && apt full-upgrade -y
1213

1314
COPY --from=builder /jenkins.war /jenkins.war
15+
16+
ENV foo=bar

0 commit comments

Comments
 (0)