Skip to content

Commit

Permalink
feat: add a way to find related SBOMs by CPE
Browse files Browse the repository at this point in the history
Also, get rid of the central analysis graph instance.
  • Loading branch information
ctron committed Jan 17, 2025
1 parent 0693b5c commit 52fce44
Show file tree
Hide file tree
Showing 49 changed files with 1,861 additions and 1,677 deletions.
26 changes: 23 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ packageurl = "0.3.0"
parking_lot = "0.12"
peak_alloc = "0.2.0"
pem = "3"
petgraph = { version = "0.6.5", features = ["serde-1"] }
percent-encoding = "2.3.1"
petgraph = { version = "0.7.1", features = ["serde-1"] }
prometheus = "0.13.3"
quick-xml = "0.37.0"
rand = "0.8.5"
Expand Down
1 change: 1 addition & 0 deletions common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ log = { workspace = true }
native-tls = { workspace = true }
packageurl = { workspace = true }
pem = { workspace = true }
percent-encoding = { workspace = true }
postgresql_embedded = { workspace = true, features = ["blocking", "tokio"] }
regex = { workspace = true }
reqwest = { workspace = true, features = ["native-tls"] }
Expand Down
9 changes: 5 additions & 4 deletions common/src/purl.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use packageurl::PackageUrl;
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
use serde::{
de::{Error, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
Expand Down Expand Up @@ -157,7 +158,7 @@ impl Display for Purl {
"?{}",
self.qualifiers
.iter()
.map(|(k, v)| format!("{}={}", k, v))
.map(|(k, v)| format!("{}={}", k, utf8_percent_encode(v, NON_ALPHANUMERIC)))
.collect::<Vec<_>>()
.join("&")
)
Expand Down Expand Up @@ -250,7 +251,7 @@ mod tests {
async fn purl_oci() -> Result<(), anyhow::Error> {
let purl: Purl = serde_json::from_str(
r#"
"pkg:oci/ose-cluster-network-operator@sha256:0170ba5eebd557fd9f477d915bb7e0d4c1ad6cd4c1852d4b1ceed7a2817dd5d2?repository_url=registry.redhat.io/openshift4/ose-cluster-network-operator&tag=v4.11.0-202403090037.p0.g33da9fb.assembly.stream.el8"
"pkg:oci/ose-cluster-network-operator@sha256:0170ba5eebd557fd9f477d915bb7e0d4c1ad6cd4c1852d4b1ceed7a2817dd5d2?repository_url=registry.redhat.io%2Fopenshift4%2Fose%2Dcluster%2Dnetwork%2Doperator&tag=v4%2E11%2E0%2D202403090037%2Ep0%2Eg33da9fb%2Eassembly%2Estream%2Eel8"
"#,
)
.unwrap();
Expand All @@ -273,12 +274,12 @@ mod tests {
Some(&"v4.11.0-202403090037.p0.g33da9fb.assembly.stream.el8".to_string())
);

let purl: Purl = "pkg:oci/ose-cluster-network-operator@sha256:0170ba5eebd557fd9f477d915bb7e0d4c1ad6cd4c1852d4b1ceed7a2817dd5d2?repository_url=registry.redhat.io/openshift4/ose-cluster-network-operator&tag=v4.11.0-202403090037.p0.g33da9fb.assembly.stream.el8".try_into()?;
let purl: Purl = "pkg:oci/ose-cluster-network-operator@sha256:0170ba5eebd557fd9f477d915bb7e0d4c1ad6cd4c1852d4b1ceed7a2817dd5d2?repository_url=registry%2Eredhat%2Eio%2Eopenshift4%2Eose%2Dcluster%2Dnetwork%2Doperator&tag=v4%2E11%2E0%2D202403090037%2Ep0%2Eg33da9fb%2Eassembly%2Estream%2Eel8".try_into()?;
let json = serde_json::to_string(&purl).unwrap();

assert_eq!(
json,
r#""pkg:oci/ose-cluster-network-operator@sha256:0170ba5eebd557fd9f477d915bb7e0d4c1ad6cd4c1852d4b1ceed7a2817dd5d2?repository_url=registry.redhat.io/openshift4/ose-cluster-network-operator&tag=v4.11.0-202403090037.p0.g33da9fb.assembly.stream.el8""#
r#""pkg:oci/ose-cluster-network-operator@sha256:0170ba5eebd557fd9f477d915bb7e0d4c1ad6cd4c1852d4b1ceed7a2817dd5d2?repository_url=registry%2Eredhat%2Eio%2Eopenshift4%2Eose%2Dcluster%2Dnetwork%2Doperator&tag=v4%2E11%2E0%2D202403090037%2Ep0%2Eg33da9fb%2Eassembly%2Estream%2Eel8""#
);
Ok(())
}
Expand Down
3 changes: 1 addition & 2 deletions etc/test-data/spdx/simple.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
"documentNamespace": "uri:just-an-example",
"name": "simple",
"packages": [

{
"SPDXID": "SPDXRef-A",
"copyrightText": "NOASSERTION",
Expand All @@ -26,7 +25,7 @@
{
"referenceCategory": "SECURITY",
"referenceLocator": "cpe:/a:redhat:simple:1::el9",
"referenceType": "cpe23Type"
"referenceType": "cpe22Type"
}
],
"filesAnalyzed": false,
Expand Down
2 changes: 2 additions & 0 deletions modules/analysis/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ trustify-entity = { workspace = true }
actix-http = { workspace = true }
actix-web = { workspace = true }
anyhow = { workspace = true }
cpe = { workspace = true }
log = { workspace = true }
petgraph = { workspace = true }
parking_lot= { workspace = true }
sea-orm = { workspace = true }
sea-query = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
thiserror = { workspace = true }
tracing = { workspace = true }
utoipa = { workspace = true, features = ["actix_extras", "uuid"] }
Expand Down
7 changes: 7 additions & 0 deletions modules/analysis/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Analysis Graph

## Get a component

```bash
http localhost:8080/api/v2/analysis/root-component/B
```
73 changes: 43 additions & 30 deletions modules/analysis/src/endpoints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ pub async fn search_component_root_components(
) -> actix_web::Result<impl Responder> {
Ok(HttpResponse::Ok().json(
service
.retrieve_root_components(search, paginated, db.as_ref())
.retrieve_root_components(&search, paginated, db.as_ref())
.await?,
))
}
Expand All @@ -92,13 +92,13 @@ pub async fn get_component_root_components(
let purl: Purl = Purl::from_str(&key).map_err(Error::Purl)?;
Ok(HttpResponse::Ok().json(
service
.retrieve_root_components_by_purl(purl, paginated, db.as_ref())
.retrieve_root_components(&purl, paginated, db.as_ref())
.await?,
))
} else {
Ok(HttpResponse::Ok().json(
service
.retrieve_root_components_by_name(key.to_string(), paginated, db.as_ref())
.retrieve_root_components(&key.to_string(), paginated, db.as_ref())
.await?,
))
}
Expand All @@ -125,7 +125,7 @@ pub async fn search_component_deps(
) -> actix_web::Result<impl Responder> {
Ok(HttpResponse::Ok().json(
service
.retrieve_deps(search, paginated, db.as_ref())
.retrieve_deps(&search, paginated, db.as_ref())
.await?,
))
}
Expand All @@ -150,15 +150,11 @@ pub async fn get_component_deps(
) -> actix_web::Result<impl Responder> {
if key.starts_with("pkg:") {
let purl: Purl = Purl::from_str(&key).map_err(Error::Purl)?;
Ok(HttpResponse::Ok().json(
service
.retrieve_deps_by_purl(purl, paginated, db.as_ref())
.await?,
))
Ok(HttpResponse::Ok().json(service.retrieve_deps(&purl, paginated, db.as_ref()).await?))
} else {
Ok(HttpResponse::Ok().json(
service
.retrieve_deps_by_name(key.to_string(), paginated, db.as_ref())
.retrieve_deps(&key.to_string(), paginated, db.as_ref())
.await?,
))
}
Expand Down Expand Up @@ -187,8 +183,14 @@ mod test {
let request: Request = TestRequest::get().uri(uri).to_request();
let response: Value = app.call_and_read_body_json(request).await;

if response["items"][0]["purl"] == "pkg:rpm/redhat/[email protected]"
|| response["items"][1]["purl"] == "pkg:rpm/redhat/[email protected]"
if response["items"][0]["purl"]
.as_array()
.unwrap()
.contains(&Value::from("pkg:rpm/redhat/[email protected]"))
|| response["items"][1]["purl"]
.as_array()
.unwrap()
.contains(&Value::from("pkg:rpm/redhat/[email protected]"))
{
assert_eq!(&response["total"], 2);
} else {
Expand All @@ -200,10 +202,13 @@ mod test {
let uri = "/api/v2/analysis/root-component?q=BB";
let request: Request = TestRequest::get().uri(uri).to_request();
let response: Value = app.call_and_read_body_json(request).await;
assert_eq!(response["items"][0]["purl"], "pkg:rpm/redhat/[email protected]");
assert_eq!(
response["items"][0]["purl"],
Value::from(["pkg:rpm/redhat/[email protected]"])
);
assert_eq!(
response["items"][0]["ancestors"][0]["purl"],
"pkg:rpm/redhat/[email protected]?arch=src"
Value::from(["pkg:rpm/redhat/[email protected]?arch=src"])
);

assert_eq!(&response["total"], 1);
Expand All @@ -224,10 +229,13 @@ mod test {

let response: Value = app.call_and_read_body_json(request).await;

assert_eq!(response["items"][0]["purl"], "pkg:rpm/redhat/[email protected]");
assert_eq!(
response["items"][0]["purl"],
Value::from(["pkg:rpm/redhat/[email protected]"])
);
assert_eq!(
response["items"][0]["ancestors"][0]["purl"],
"pkg:rpm/redhat/[email protected]?arch=src"
Value::from(["pkg:rpm/redhat/[email protected]?arch=src"])
);
assert_eq!(&response["total"], 1);
Ok(())
Expand All @@ -247,10 +255,13 @@ mod test {

let response: Value = app.call_and_read_body_json(request).await;

assert_eq!(response["items"][0]["purl"], "pkg:rpm/redhat/[email protected]");
assert_eq!(
response["items"][0]["purl"],
Value::from(["pkg:rpm/redhat/[email protected]"])
);
assert_eq!(
response["items"][0]["ancestors"][0]["purl"],
"pkg:rpm/redhat/[email protected]?arch=src"
Value::from(["pkg:rpm/redhat/[email protected]?arch=src"])
);
assert_eq!(&response["total"], 1);
Ok(())
Expand All @@ -276,15 +287,15 @@ mod test {

assert_eq!(
response["items"][0]["purl"],
"pkg:maven/net.spy/[email protected]?type=jar"
Value::from(["pkg:maven/net.spy/[email protected]?type=jar"])
);
assert_eq!(
response["items"][0]["document_id"],
"https://access.redhat.com/security/data/sbom/spdx/quarkus-bom-3.2.11.Final-redhat-00001"
);
assert_eq!(
response["items"][0]["ancestors"][0]["purl"],
"pkg:maven/com.redhat.quarkus.platform/[email protected]?type=pom&repository_url=https%3a%2f%2fmaven.repository.redhat.com%2fga%2f"
Value::from(["pkg:maven/com.redhat.quarkus.platform/[email protected]?repository_url=https%3A%2F%2Fmaven%2Erepository%2Eredhat%2Ecom%2Fga%2F&type=pom"])
);

assert_eq!(&response["total"], 2);
Expand Down Expand Up @@ -334,20 +345,22 @@ mod test {
let request: Request = TestRequest::get().uri(uri).to_request();
let response: Value = app.call_and_read_body_json(request).await;

println!("Result: {response:#?}");

assert_eq!(
response["items"][0]["purl"],
"pkg:rpm/redhat/[email protected]?arch=src"
Value::from(["pkg:rpm/redhat/[email protected]?arch=src"]),
);
assert_eq!(
response["items"][0]["deps"][0]["purl"],
"pkg:rpm/redhat/[email protected]?arch=src"
Value::from(["pkg:rpm/redhat/[email protected]?arch=src"]),
);
assert_eq!(
response["items"][0]["deps"][1]["purl"],
"pkg:rpm/redhat/[email protected]"
Value::from(["pkg:rpm/redhat/[email protected]"]),
);

assert_eq!(&response["total"], 3);
assert_eq!(&response["total"], 2);
Ok(())
}

Expand All @@ -364,15 +377,15 @@ mod test {

assert_eq!(
response["items"][0]["purl"],
"pkg:rpm/redhat/[email protected]?arch=src"
Value::from(["pkg:rpm/redhat/[email protected]?arch=src"]),
);
assert_eq!(
response["items"][0]["deps"][0]["purl"],
"pkg:rpm/redhat/[email protected]?arch=src"
Value::from(["pkg:rpm/redhat/[email protected]?arch=src"]),
);
assert_eq!(
response["items"][0]["deps"][1]["purl"],
"pkg:rpm/redhat/[email protected]"
Value::from(["pkg:rpm/redhat/[email protected]"]),
);

assert_eq!(&response["total"], 1);
Expand All @@ -392,11 +405,11 @@ mod test {

assert_eq!(
response["items"][0]["purl"],
"pkg:rpm/redhat/[email protected]?arch=src"
Value::from(["pkg:rpm/redhat/[email protected]?arch=src"]),
);
assert_eq!(
response["items"][0]["deps"][0]["purl"],
"pkg:rpm/redhat/[email protected]"
Value::from(["pkg:rpm/redhat/[email protected]"]),
);
assert_eq!(&response["total"], 1);
Ok(())
Expand All @@ -420,7 +433,7 @@ mod test {

assert_eq!(
response["items"][0]["purl"],
"pkg:maven/net.spy/[email protected]?type=jar"
Value::from(["pkg:maven/net.spy/[email protected]?type=jar"]),
);
assert_eq!(&response["total"], 2);
Ok(())
Expand Down
Loading

0 comments on commit 52fce44

Please sign in to comment.