-
-
Notifications
You must be signed in to change notification settings - Fork 93
I would like to confirm the recommended CycloneDX layout for a container image produced from a simple Dockerfile:
FROM debian:11 RUN apt-get -y install vim # additional package COPY myapp . # self-built Go binary
Inventory:
- Container image
- Operating System (Debian 11)
- Debian base packages (
libc6,base-files, ...)
plus the additional package (vim)
- Debian base packages (
- Application binary
myapp- Go modules (
modA,modB, ...) with their own depends-on graph
- Go modules (
- Operating System (Debian 11)
After reading the Relationships guide, I still see several plausible patterns.
Below are abbreviated CycloneDX JSON snippets for each (only the fields needed to illustrate the structure).
Pattern 1 — two parallel assemblies (OS and binary)
OS contains OS packages, and myapp is considered part of the image, not of the OS. The image contains two assemblies: the OS and the application.
{
"metadata": { "component": { "type":"container", "name":"image", "bom-ref":"img" } },
"components": [
{ "type":"operating-system", "name":"debian", "bom-ref":"os",
"components": [
{ "type":"library", "name":"libc6" },
{ "type":"library", "name":"base-files" },
{ "type":"library", "name":"vim" }
]},
{ "type":"application", "name":"myapp", "bom-ref":"bin",
"components": [
{ "type":"library", "name":"modA" },
{ "type":"library", "name":"modB" }
]}
],
"dependencies": [
{ "ref":"os", "dependsOn":["base-files"] },
{ "ref":"modB","dependsOn":["modA"] }
]
}Pattern 2 — single nested assembly (everything under the OS)
Everything is nested under the OS assembly; myapp is treated as something contained by the OS.
{
"metadata": { "component": { "type":"container", "name":"image", "bom-ref":"img" } },
"components": [
{ "type":"operating-system", "name":"debian", "bom-ref":"os",
"components": [
{ "type":"library", "name":"libc6" },
{ "type":"library", "name":"base-files" },
{ "type":"library", "name":"vim" },
{ "type":"application", "name":"myapp", "bom-ref":"bin",
"components": [
{ "type":"library", "name":"modA" },
{ "type":"library", "name":"modB" }
]}
]}
],
"dependencies": [
{ "ref":"os", "dependsOn":["base-files"] },
{ "ref":"modB","dependsOn":["modA"] }
]
}Pattern 3 — flat components + dependencies only
In this view, the entire image depends on both the OS and the binary.
The rationale is that, just as nginx:latest would not function without the nginx binary, the image cannot run without myapp, so the binary can be modelled as a dependency of the container itself.
{
"metadata": { "component": { "type":"container", "name":"image", "bom-ref":"img" } },
"components": [
{ "type":"operating-system", "name":"debian", "bom-ref":"os" },
{ "type":"library", "name":"libc6" },
{ "type":"library", "name":"base-files" },
{ "type":"library", "name":"vim" },
{ "type":"application", "name":"myapp", "bom-ref":"bin" },
{ "type":"library", "name":"modA" },
{ "type":"library", "name":"modB" }
],
"dependencies": [
{ "ref":"img", "dependsOn":["os","bin"] },
{ "ref":"os", "dependsOn":["libc6","base-files","vim"] },
{ "ref":"bin", "dependsOn":["modA","modB"] },
{ "ref":"modB","dependsOn":["modA"] }
]
}Question
Which of these patterns does the CycloneDX community regard as the most idiomatic / interoperable way to represent such an everyday container image?
Side note
@jkowalleck commented below earlier:
The question is probably not to be answered by CycloneDX people, but by @opencontainers or the Trivy community.
If the choice really is "out of scope" for the CycloneDX spec, then every tool will encode the same image differently, and interoperability will suffer. Given such a common structure, would it make sense for CycloneDX to publish a recommended profile so that SBOMs converge on a single canonical layout?
Thank you for any guidance.
All reactions
Replies: 2 comments 2 replies
from my perspective, the answer is heavily depending on the actual architecture, and probably very debatable.
The question is probably not to be answered by CycloneDX poeple, but by @opencontainers community.
but anyway, here are my 2cents:
- base image is "debian" -> a component "debian"
- some debian package "foo" is installed (
apt install ...) -> "foo" is a subcomponent of "debian" - some "myapp" is copied into the file system of the image -> "myapp" is not a subcomponent of "debian", but a sibling of "debian"
my result would be:
{
"metadata": { "component": { "type":"container", "name":"image", "bom-ref":"img" } },
"components": [
{ "type":"operating-system", "name":"debian", "bom-ref":"os",
"components": [
{ "type":"library", "name":"libc6" },
{ "type":"library", "name":"base-files" },
{ "type":"application", "name":"vim" }
]
},
{ "type":"application", "name":"myapp", "bom-ref":"bin",
"components": [
{ "type":"library", "name":"modA" },
{ "type":"library", "name":"modB" }
]
}
],
"dependencies": [
{ "ref":"img", "dependsOn":["os","bin", "vim"] },
{ "ref":"os", "dependsOn":["libc6","base-files"] },
{ "ref":"libc6" },
{ "ref":"base-files" },
{ "ref":"vim" },
{ "ref":"bin", "dependsOn":["modA","modB"] },
{ "ref":"modB","dependsOn":["modA"] }
{ "ref":"modA" }
]
}All reactions
Thanks for your answer. What do you think about my comment below? Is interoperability out of scope in CycloneDX?
If the choice really is "out of scope" for the CycloneDX spec, then every tool will encode the same image differently, and interoperability will suffer. Given such a common structure, would it make sense for CycloneDX to publish a recommended profile so that SBOMs converge on a single canonical layout?
All reactions
some debian package "foo" is installed (apt install ...) -> "foo" is a subcomponent of "debian"
I think this is very opinionated. I would not generalize this.
then every tool will encode the same image differently, and interoperability will suffer. Given such a common structure, would it make sense for CycloneDX to publish a recommended profile so that SBOMs converge on a single canonical layout?
My opinion on this: manufacturer should be responsible for producing SBOM for their software as well as resolving ambiguities based on understanding of their product. That BOM becomes canonical for the specific product. I don't believe it is correct that other people will then try to recreate BOM for the same product.
Instead, there should be a system in place to distribute manufacturer's BOM to other parties - look at TEA project here - https://github.com/CycloneDX/transparency-exchange-api/
All reactions
some debian package "foo" is installed (apt install ...) -> "foo" is a subcomponent of "debian"
I think this is very opinionated. I would not generalize this.
I agree — modeling every installed package as a subcomponent of the base image (e.g., debian:11) is a very opinionated approach, and potentially problematic.
If we take this route, we risk repeating the same kind of oversimplification seen in some NVD entries. A common pitfall is when a vulnerability in one of the ~50,000 Debian packages leads to the entire Debian distribution being marked as affected. A good example of this kind of issue is CVE-2021-44228 (and no, you probably didn’t expect a different example from me 😄).
Because of this, I believe Pattern 1 is the safer and more accurate approach. It clearly distinguishes the components provided by the base image (e.g., debian:11) from those introduced in the derived image, avoiding misleading containment relationships.