Skip to content

Commit

Permalink
feat(SBOMER-156): adjust container image manifests
Browse files Browse the repository at this point in the history
Switches to a tree of componentes instead of a flat list.

The main, root component is now the image itself. Subcomponents of that
component are all the components we could find in the image.

The component in the metadata holds only basic information and metadata
now. And properties.

Fixes: https://issues.redhat.com/browse/SBOMER-156
  • Loading branch information
goldmann committed Sep 23, 2024
1 parent ea086e3 commit a313e96
Show file tree
Hide file tree
Showing 10 changed files with 3,141 additions and 3,047 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ void parseSkopeoOutput() throws IOException {
ContainerImageInspectOutput data = ObjectMapperProvider.json()
.readValue(rawData, ContainerImageInspectOutput.class);

assertEquals("sha256:f63b27a29c032843941b15567ebd1f37f540160e8066ac74c05367134c2ff3aa", data.getDigest());
assertEquals("registry.redhat.io/jboss-webserver-5/jws58-openjdk17-openshift-rhel8", data.getName());
assertEquals("sha256:ee4e27734a21cc6b8a8597ef2af32822ad0b4677dbde0a794509f55cbaff5ab3", data.getDigest());
assertEquals("registry.com/rh-osbs/amq-streams-console-ui-rhel9", data.getName());
assertEquals("amd64", data.getArchitecture());
assertEquals(34, data.getLabels().size());
assertEquals(21, data.getLabels().size());
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.jboss.sbomer.cli.test.unit.feature.sbom.adjust;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;

import java.io.File;
import java.io.IOException;
Expand Down Expand Up @@ -33,39 +34,56 @@ void init() throws IOException {
void removeAllRpms() throws IOException {
SyftImageAdjuster adjuster = new SyftImageAdjuster(null, false);

assertEquals(192, bom.getComponents().size());

Bom adjusted = adjuster.adjust(bom, tmpDir.toPath());

assertEquals(
"pkg:oci/amq-streams-console-ui-rhel9@sha256%3Af63b27a29c032843941b15567ebd1f37f540160e8066ac74c05367134c2ff3aa?os=linux&arch=amd64&tag=2.7.0-8.1718294415",
"pkg:oci/console-ui-rhel9@sha256%3Aee4e27734a21cc6b8a8597ef2af32822ad0b4677dbde0a794509f55cbaff5ab3?arch=amd64&os=linux&tag=2.7.0-8.1718294415",
adjusted.getMetadata().getComponent().getPurl());
assertEquals(32, adjusted.getComponents().size());
assertEquals("amq-streams/console-ui-rhel9", adjusted.getMetadata().getComponent().getName());
assertEquals(1, adjusted.getComponents().size());
assertEquals(32, adjusted.getComponents().get(0).getComponents().size());
assertEquals(33, adjusted.getDependencies().size());
}

@Test
void removeAllRpmsLeavePrefix() throws IOException {
SyftImageAdjuster adjuster = new SyftImageAdjuster(List.of("/app"), false);

assertEquals(192, bom.getComponents().size());

Bom adjusted = adjuster.adjust(bom, tmpDir.toPath());

assertEquals(23, adjusted.getComponents().size());
assertEquals(1, adjusted.getComponents().size());
assertEquals(23, adjusted.getComponents().get(0).getComponents().size());
assertEquals(24, adjusted.getDependencies().size());
}

@Test
void preserveRpms() throws IOException {
SyftImageAdjuster adjuster = new SyftImageAdjuster(null, true);

assertEquals(192, bom.getComponents().size());

Bom adjusted = adjuster.adjust(bom, tmpDir.toPath());

assertEquals(191, adjusted.getComponents().size());
assertEquals(1, adjusted.getComponents().size());
assertEquals(191, adjusted.getComponents().get(0).getComponents().size());
assertEquals(192, adjusted.getDependencies().size());
}

@Test
void preserveRpmsWithPrefix() throws IOException {
SyftImageAdjuster adjuster = new SyftImageAdjuster(List.of("/app"), true);

assertEquals(192, bom.getComponents().size());

Bom adjusted = adjuster.adjust(bom, tmpDir.toPath());

assertEquals(182, adjusted.getComponents().size());
assertEquals(1, adjusted.getComponents().size());
assertEquals(182, adjusted.getComponents().get(0).getComponents().size());
assertEquals(183, adjusted.getDependencies().size());
}

@Test
Expand All @@ -82,10 +100,45 @@ void shouldLeaveOnlyArchAndEpochQualifiers() throws IOException {

Bom adjusted = adjuster.adjust(bom, tmpDir.toPath());

assertEquals("pkg:rpm/redhat/[email protected]?arch=x86_64", adjusted.getComponents().get(10).getPurl());
// 11th component before adjustment becomes 11th element nested under a root component.
assertEquals(
"pkg:rpm/redhat/[email protected]?arch=x86_64",
adjusted.getComponents().get(0).getComponents().get(10).getPurl());
assertEquals(
"pkg:rpm/redhat/[email protected]_2.1?arch=x86_64&epoch=1",
adjusted.getComponents().get(21).getPurl());
adjusted.getComponents().get(0).getComponents().get(21).getPurl());
}

@Test
void shouldAdjustNameAndPurl() {
SyftImageAdjuster adjuster = new SyftImageAdjuster();

assertEquals("registry.com/rh-osbs/amq-streams-console-ui-rhel9", bom.getMetadata().getComponent().getName());
assertNull(bom.getMetadata().getComponent().getPurl());

Bom adjusted = adjuster.adjust(bom, tmpDir.toPath());

assertEquals("amq-streams/console-ui-rhel9", adjusted.getMetadata().getComponent().getName());
assertEquals(
"pkg:oci/console-ui-rhel9@sha256%3Aee4e27734a21cc6b8a8597ef2af32822ad0b4677dbde0a794509f55cbaff5ab3?arch=amd64&os=linux&tag=2.7.0-8.1718294415",
adjusted.getMetadata().getComponent().getPurl());
}

@Test
void generatedDependenciesShouldHaveProperDependsOn() throws IOException {
SyftImageAdjuster adjuster = new SyftImageAdjuster();

assertEquals(192, bom.getComponents().size());
// assertEquals("", bom.getMetadata().getComponent().getPurl());

Bom adjusted = adjuster.adjust(bom, tmpDir.toPath());

assertEquals(1, bom.getComponents().size());
// It is one less after filtering, because the "operating system" component does not have a purl.
assertEquals(191, bom.getComponents().get(0).getComponents().size());
// Main component + all of it's components
assertEquals(192, adjusted.getDependencies().size());
// Main component dependencies
assertEquals(191, adjusted.getDependencies().get(0).getDependencies().size());
}
}
168 changes: 62 additions & 106 deletions cli/src/test/resources/skopeo.json
Original file line number Diff line number Diff line change
@@ -1,120 +1,76 @@
{
"Name": "registry.redhat.io/jboss-webserver-5/jws58-openjdk17-openshift-rhel8",
"Digest": "sha256:f63b27a29c032843941b15567ebd1f37f540160e8066ac74c05367134c2ff3aa",
"Name": "registry.com/rh-osbs/amq-streams-console-ui-rhel9",
"Digest": "sha256:ee4e27734a21cc6b8a8597ef2af32822ad0b4677dbde0a794509f55cbaff5ab3",
"RepoTags": [
"5.8.0-3",
"5.8.0-5",
"5.8.0",
"latest",
"sha256-f63b27a29c032843941b15567ebd1f37f540160e8066ac74c05367134c2ff3aa.sig",
"sha256-92d28009d5859f97b21268d6a4f1ef20ffbdf700ade89a088870b0901c544465.sig",
"sha256-ccfc5498daeaba66a71e27633945f47c6f2015deaab890ecd1b02d2497d13ae6.sig",
"sha256-ea97e4f5623e378e6ab3ed1854e3c5eefcfeb906be1dd8986ddb5dead3cd7545.sig",
"sha256-a3350845d2206d7eaef01a3cd9d614b20196d9bbfd4a8bd37bcce3c34a3d6e9d.sig",
"sha256-fa08f388fd129d0f9e052f70d9c671b8b44a9dad05317cb243d857831e12654c.sig",
"sha256-e6842df47660072cc8c621427476dd127c29f13b82702febb608052e587a8fb7.sig"
"2.7.0",
"2.7.0-7",
"2.7.0-8",
"2.7.0-8.1718294415",
"2.7.0-8.1723128738",
"2.7.0-8.1724037861",
"2.7.0-8.1724171419",
"2.8.0",
"2.8.0-1",
"2.8.0-2",
"2.8.0-3",
"2.8.0-4",
"amqstreams-2.7-rhel-8-containers-candidate-97282-20240508174835",
"amqstreams-2.7-rhel-8-containers-candidate-97282-20240508174835-x86_64",
"sha256-ffae7f0bdfaba3392bd4c24348fa8b591ed5be3a078afa37c3b214a794ad951a.sbom"
],
"Created": "2024-05-30T12:08:52.509095756Z",
"Created": "2024-06-13T16:16:45.380201731Z",
"DockerVersion": "",
"Labels": {
"architecture": "x86_64",
"build-date": "2024-05-30T12:02:31",
"com.redhat.component": "jboss-webserver-58-openjdk17-rhel8-openshift-container",
"com.redhat.deployments-dir": "/opt/jws-5.8/tomcat/webapps",
"com.redhat.dev-mode": "DEBUG:true",
"com.redhat.dev-mode.port": "JPDA_ADDRESS:8000",
"com.redhat.license_terms": "https://www.redhat.com/agreements",
"description": "Red Hat JBoss Web Server 5.8 - Tomcat 9 OpenShift container image with OpenJDK17 on UBI8",
"distribution-scope": "public",
"io.buildah.version": "1.29.0",
"io.cekit.version": "4.12.0",
"io.fabric8.s2i.version.jolokia": "1.7.1.redhat-00001",
"io.fabric8.s2i.version.maven": "3.6",
"io.k8s.description": "Platform for building and running web applications on JBoss Web Server 5.8 with OpenJDK17 - Tomcat v9",
"io.k8s.display-name": "JBoss Web Server 5.8 OpenJDK17",
"io.openshift.expose-services": "8080:http",
"io.openshift.s2i.destination": "/tmp",
"io.openshift.s2i.scripts-url": "image:///usr/local/s2i",
"io.openshift.tags": "builder,java,tomcat9",
"maintainer": "[email protected]",
"name": "jboss-webserver-5/jws58-openjdk17-rhel8-openshift",
"org.jboss.container.deployments-dir": "/deployments",
"org.jboss.deployments-dir": "/opt/jws-5.8/tomcat/webapps",
"org.jboss.product": "webserver-tomcat9",
"org.jboss.product.openjdk.version": "17",
"org.jboss.product.version": "5.8.0",
"org.jboss.product.webserver-tomcat9.version": "5.8.0",
"release": "5",
"summary": "Red Hat JBoss Web Server 5.8 - Tomcat 9 OpenShift container image with OpenJDK17 on UBI8",
"url": "https://access.redhat.com/containers/#/registry.access.redhat.com/jboss-webserver-5/jws58-openjdk17-rhel8-openshift/images/5.8.0-5",
"vcs-ref": "e74ae74becd2837a739e3b8c58c7b68fdccefc74",
"vcs-type": "git",
"vendor": "Red Hat, Inc.",
"version": "5.8.0"
"architecture": "x86_64",
"build-date": "2024-06-13T16:02:24",
"com.redhat.component": "amqstreams-console-ui-container",
"com.redhat.license_terms": "https://www.redhat.com/agreements",
"description": "AMQ Streams Console UI",
"distribution-scope": "public",
"io.buildah.version": "1.29.0",
"io.k8s.description": "AMQ Streams Console UI",
"io.k8s.display-name": "AMQ Streams Console UI",
"io.openshift.expose-services": "",
"io.openshift.tags": "messaging,amq,jboss",
"licenses": "/root/licenses",
"maintainer": "AMQ Streams Engineering \u003c[email protected]\u003e",
"name": "amq-streams/console-ui-rhel9",
"release": "8.1718294415",
"summary": "AMQ Streams Console UI",
"url": "https://access.redhat.com/containers/#/registry.access.redhat.com/amq-streams/console-ui-rhel9/images/2.7.0-8.1718294415",
"vcs-ref": "857cfa99af870e97d4e50ba06b66173f83153694",
"vcs-type": "git",
"vendor": "Red Hat, Inc.",
"version": "2.7.0"
},
"Architecture": "amd64",
"Os": "linux",
"Layers": [
"sha256:3954349e40073fed567a627e4ceb393b41aa1f9614ff277acb2ba1f0ec3f1a96",
"sha256:621fe81d70cfb2f1f0c8571fe4a04bbd7bdc4a55eaeb32558bde954ef8aaa037",
"sha256:ce9a956bc03cf8277893a8b72c1744e7210f0b73d638c49d6dfacd8b2cfa836e"
"sha256:46b9933268486d0af9ace6cd4c976240915e7a889637ef65d41d443e70d50e64",
"sha256:611caf47daba10301214ad9ae85b31725c8a8c3a2b7c51b890f4bd6b24dac4c8"
],
"LayersData": [
{
"MIMEType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"Digest": "sha256:3954349e40073fed567a627e4ceb393b41aa1f9614ff277acb2ba1f0ec3f1a96",
"Size": 78969888,
"Annotations": null
},
{
"MIMEType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"Digest": "sha256:621fe81d70cfb2f1f0c8571fe4a04bbd7bdc4a55eaeb32558bde954ef8aaa037",
"Size": 125475054,
"Annotations": null
},
{
"MIMEType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"Digest": "sha256:ce9a956bc03cf8277893a8b72c1744e7210f0b73d638c49d6dfacd8b2cfa836e",
"Size": 30013391,
"Annotations": null
}
{
"MIMEType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"Digest": "sha256:46b9933268486d0af9ace6cd4c976240915e7a889637ef65d41d443e70d50e64",
"Size": 82779806,
"Annotations": null
},
{
"MIMEType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"Digest": "sha256:611caf47daba10301214ad9ae85b31725c8a8c3a2b7c51b890f4bd6b24dac4c8",
"Size": 32103745,
"Annotations": null
}
],
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"container=oci",
"JAVA_HOME=/usr/lib/jvm/java-17",
"JAVA_VENDOR=openjdk",
"JAVA_VERSION=17",
"JBOSS_CONTAINER_OPENJDK_JDK_MODULE=/opt/jboss/container/openjdk/jdk",
"CATALINA_OPTS=-Djava.security.egd=file:/dev/./urandom",
"JBOSS_PRODUCT=webserver",
"JBOSS_WEBSERVER_VERSION=5.8.0",
"JPDA_ADDRESS=8000",
"JWS_HOME=/opt/jws-5.8/tomcat",
"PRODUCT_VERSION=5.8.0",
"TOMCAT_VERSION=9.0.87",
"JBOSS_CONTAINER_MAVEN_36_MODULE=/opt/jboss/container/maven/36/",
"MAVEN_VERSION=3.6",
"JBOSS_CONTAINER_JAVA_PROXY_MODULE=/opt/jboss/container/java/proxy",
"JBOSS_CONTAINER_JAVA_JVM_MODULE=/opt/jboss/container/java/jvm",
"JBOSS_CONTAINER_UTIL_LOGGING_MODULE=/opt/jboss/container/util/logging/",
"JBOSS_CONTAINER_MAVEN_DEFAULT_MODULE=/opt/jboss/container/maven/default/",
"S2I_SOURCE_DEPLOYMENTS_FILTER=*.war",
"JBOSS_CONTAINER_S2I_CORE_MODULE=/opt/jboss/container/s2i/core/",
"JBOSS_CONTAINER_MAVEN_S2I_MODULE=/opt/jboss/container/maven/s2i",
"JBOSS_CONTAINER_JWS_S2I_MODULE=/opt/jboss/container/jws/s2i",
"AB_JOLOKIA_AUTH_OPENSHIFT=true",
"AB_JOLOKIA_HTTPS=true",
"AB_JOLOKIA_PASSWORD_RANDOM=true",
"JBOSS_CONTAINER_JOLOKIA_MODULE=/opt/jboss/container/jolokia",
"JOLOKIA_VERSION=1.7.1",
"HOME=/home/jboss",
"AB_PROMETHEUS_ENABLE=True",
"AB_PROMETHEUS_JMX_EXPORTER_CONFIG=/opt/jboss/container/prometheus/etc/jws-jmx-exporter-config.yaml",
"AB_PROMETHEUS_JMX_EXPORTER_PORT=9404",
"JBOSS_CONTAINER_PROMETHEUS_MODULE=/opt/jboss/container/prometheus",
"JBOSS_IMAGE_NAME=jboss-webserver-5/jws58-openjdk17-rhel8-openshift",
"JBOSS_IMAGE_VERSION=5.8.0",
"STI_BUILDER=jee"
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"container=oci",
"NODE_ENV=production",
"NEXT_TELEMETRY_DISABLED=1",
"LOG_LEVEL=info",
"CONSOLE_MODE=read-only",
"PORT=3000",
"HOSTNAME=0.0.0.0"
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -746,8 +746,8 @@ public static Bom fromJsonNode(JsonNode jsonNode) {
}

try {
return new JsonParser()
.parse(jsonNode.isTextual() ? jsonNode.textValue().getBytes() : jsonNode.toString().getBytes());
final String content = jsonNode.isTextual() ? jsonNode.textValue() : jsonNode.toString();
return new JsonParser().parse(content.getBytes(UTF_8));
} catch (ParseException e) {
log.error(e.getMessage(), e);
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
*/
package org.jboss.sbomer.service.feature.sbom.atlas;

import java.io.InputStream;
import java.util.List;

import org.cyclonedx.model.Bom;
import org.eclipse.microprofile.rest.client.annotation.ClientHeaderParam;
Expand All @@ -27,6 +27,8 @@
import org.jboss.sbomer.core.errors.NotFoundException;
import org.jboss.sbomer.core.errors.UnauthorizedException;

import com.fasterxml.jackson.databind.JsonNode;

import io.quarkus.oidc.client.filter.OidcClientFilter;
import io.quarkus.rest.client.reactive.ClientExceptionMapper;
import io.smallrye.reactive.messaging.annotations.Blocking;
Expand Down Expand Up @@ -55,12 +57,8 @@ public interface AtlasClient {
@GET
public Bom get(@QueryParam("id") String purl);

// TODO: check why automatic serialization
@PUT
public void upload(@QueryParam("id") String purl, Bom bom);

@PUT
public void upload(@QueryParam("id") String purl, InputStream bom);
public void upload(@QueryParam("id") String purl, JsonNode bom);

@ClientExceptionMapper
@Blocking
Expand All @@ -69,13 +67,16 @@ static RuntimeException toException(Response response) {

switch (response.getStatus()) {
case 400:
return new ClientException("Bad request, {}", message);
return new ClientException("Bad request", List.of(message));
case 401:
return new UnauthorizedException("Caller is unauthorized to access resource; {}", message);
return new UnauthorizedException(
"Caller is unauthorized to access resource; {}",
message,
List.of(message));
case 403:
return new ForbiddenException("Caller is forbidden to access resource; {}", message);
return new ForbiddenException("Caller is forbidden to access resource; {}", message, List.of(message));
case 404:
return new NotFoundException("Requested resource was not found; {}", message);
return new NotFoundException("Requested resource was not found; {}", message, List.of(message));
default:
break;
}
Expand Down
Loading

0 comments on commit a313e96

Please sign in to comment.