Skip to content

Commit

Permalink
feat: create VariantOf relationship from cdx pedigree/variants
Browse files Browse the repository at this point in the history
Fixes trustification#1147

Only analysis endpoints affected, no change to /api/v2/purl endpoint.

Signed-off-by: Jim Crossley <[email protected]>
  • Loading branch information
jcrossley3 authored and JimFuller-RedHat committed Jan 21, 2025
1 parent 38740d1 commit 63f9ada
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 17 deletions.
77 changes: 77 additions & 0 deletions etc/test-data/cyclonedx/66FF73123BB3489.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
{
"version": 1,
"metadata": {
"tools": {
"components": [
{
"name": "SBOMer",
"type": "application",
"author": "Red Hat",
"version": "dce92b5d"
}
]
},
"component": {
"name": "openshift/ose-console",
"purl": "pkg:oci/openshift-ose-console@sha256%3A94a0d7feec34600a858c8e383ee0e8d5f4a077f6bbc327dcad8762acfcf40679",
"type": "container",
"description": "Image index manifest of pkg:oci/openshift-ose-console@sha256%3A94a0d7feec34600a858c8e383ee0e8d5f4a077f6bbc327dcad8762acfcf40679"
},
"timestamp": "2024-12-19T18:04:12Z"
},
"bomFormat": "CycloneDX",
"components": [
{
"name": "openshift/ose-console",
"purl": "pkg:oci/openshift-ose-console@sha256%3A94a0d7feec34600a858c8e383ee0e8d5f4a077f6bbc327dcad8762acfcf40679",
"type": "container",
"bom-ref": "pkg:oci/openshift-ose-console@sha256%3A94a0d7feec34600a858c8e383ee0e8d5f4a077f6bbc327dcad8762acfcf40679",
"version": "sha256:94a0d7feec34600a858c8e383ee0e8d5f4a077f6bbc327dcad8762acfcf40679",
"pedigree": {
"variants": [
{
"name": "openshift/ose-console",
"purl": "pkg:oci/ose-console@sha256%3Ac2d69e860b7457eb42f550ba2559a0452ec3e5c9ff6521d758c186266247678e?arch=s390x&os=linux&tag=v4.14.0-202412110104.p0.g350e1ea.assembly.stream.el8",
"type": "container",
"bom-ref": "pkg:oci/ose-console@sha256%3Ac2d69e860b7457eb42f550ba2559a0452ec3e5c9ff6521d758c186266247678e?arch=s390x&os=linux&tag=v4.14.0-202412110104.p0.g350e1ea.assembly.stream.el8",
"version": "sha256:c2d69e860b7457eb42f550ba2559a0452ec3e5c9ff6521d758c186266247678e",
"supplier": {
"url": [
"https://www.redhat.com"
],
"name": "Red Hat"
},
"publisher": "Red Hat"
}
]
},
"supplier": {
"url": [
"https://www.redhat.com"
],
"name": "Red Hat"
},
"publisher": "Red Hat",
"properties": [
{
"name": "sbomer:image:labels:com.redhat.component",
"value": "openshift-enterprise-console-container"
},
{
"name": "sbomer:image:labels:name",
"value": "openshift/ose-console"
},
{
"name": "sbomer:image:labels:release",
"value": "202412110104.p0.g350e1ea.assembly.stream.el8"
},
{
"name": "sbomer:image:labels:version",
"value": "v4.14.0"
}
]
}
],
"specVersion": "1.6",
"serialNumber": "urn:uuid:537c8dc3-6f66-3cac-b504-cc5fb0a09ece"
}
36 changes: 36 additions & 0 deletions modules/analysis/src/endpoints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -575,4 +575,40 @@ mod test {

Ok(())
}

#[test_context(TrustifyContext)]
#[test(actix_web::test)]
async fn issue_tc_2052(ctx: &TrustifyContext) -> Result<(), anyhow::Error> {
let app = caller(ctx).await?;
ctx.ingest_documents(["cyclonedx/66FF73123BB3489.json"])
.await?;

// Find all deps of parent
let parent = "pkg:oci/openshift-ose-console@sha256:94a0d7feec34600a858c8e383ee0e8d5f4a077f6bbc327dcad8762acfcf40679";
let uri = format!("/api/v2/analysis/dep/{}", urlencoding::encode(parent));
let request: Request = TestRequest::get().uri(&uri).to_request();
let response: Value = app.call_and_read_body_json(request).await;
log::debug!("{response:#?}");
assert_eq!(1, response["items"][0]["deps"].as_array().unwrap().len());

// Ensure child is variant of src
let child = "pkg:oci/ose-console@sha256:c2d69e860b7457eb42f550ba2559a0452ec3e5c9ff6521d758c186266247678e?arch=s390x&os=linux&tag=v4.14.0-202412110104.p0.g350e1ea.assembly.stream.el8";
let uri = format!(
"/api/v2/analysis/root-component/{}",
urlencoding::encode(child)
);
let request: Request = TestRequest::get().uri(&uri).to_request();
let response: Value = app.call_and_read_body_json(request).await;
log::debug!("{response:#?}");
assert_eq!(
"VariantOf",
response["items"][0]["ancestors"][0]["relationship"]
);
assert_eq!(
Value::from(vec![Value::from(parent)]),
response["items"][0]["ancestors"][0]["purl"]
);

Ok(())
}
}
17 changes: 0 additions & 17 deletions modules/ingestor/src/graph/purl/package_version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,21 +67,4 @@ impl<'g> PackageVersionContext<'g> {

Ok(found.map(|model| QualifiedPackageContext::new(self, model)))
}

/// Retrieve known variants of this package version.
///
/// Non-mutating to the fetch.
pub async fn get_variants<C: ConnectionTrait>(
&self,
_pkg: Purl,
connection: &C,
) -> Result<Vec<QualifiedPackageContext>, Error> {
Ok(entity::qualified_purl::Entity::find()
.filter(entity::qualified_purl::Column::VersionedPurlId.eq(self.package_version.id))
.all(connection)
.await?
.into_iter()
.map(|base| QualifiedPackageContext::new(self, base))
.collect())
}
}
26 changes: 26 additions & 0 deletions modules/ingestor/src/graph/sbom/cyclonedx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,32 @@ impl<'a> ComponentCreator<'a> {
self.relationships
.relate(node_id.clone(), Relationship::AncestorOf, target);
}

for variant in comp
.pedigree
.iter()
.flat_map(|pedigree| pedigree.variants.iter().flatten())
{
let target = variant
.bom_ref
.clone()
.unwrap_or_else(|| Uuid::new_v4().to_string());

// create the component

let creator = ComponentCreator::new(
self.cpes,
self.purls,
self.licenses,
self.packages,
self.relationships,
);

creator.create(variant);

self.relationships
.relate(target, Relationship::VariantOf, node_id.clone());
}
}

pub fn add_cpe(&mut self, cpe: Cpe) {
Expand Down

0 comments on commit 63f9ada

Please sign in to comment.