Hi @TimBrown1611! Good question. I think your question illustrates the difference between squashed
and all-layers
scopes that you can pass to Syft.
Let’s start with a Dockerfile:
FROM fedora:latest
RUN yum install -y jq-1.7.1-4.fc40
RUN yum remove -y jq
RUN yum install -y jq-1.7.1-7.fc40
So now we make an image with two versions of jq
in two different layers, and we see what Syft makes of it.
docker build . -t localhost/discourse274
syft -o json=syft.json localhost/discourse274
syft --scope all-layers -o json=all-layers.json localhost/discourse274
Then we can use jq
to look at how jq
is written down in the two SBOMs:
For squashed scope (cat syft.json| jq '[ .artifacts[] | select(.name == "jq") | {purl: .purl, locations: .locations[]} ]'
):
[
{
"purl": "pkg:rpm/fedora/jq@1.7.1-7.fc40?arch=aarch64&distro=fedora-40&upstream=jq-1.7.1-7.fc40.src.rpm",
"locations": {
"path": "/usr/lib/sysimage/rpm/rpmdb.sqlite",
"layerID": "sha256:5d1f3baf10a3b56faa51f98890082a9d8523abc4984b77f23264db6b4c1a9261",
"accessPath": "/usr/lib/sysimage/rpm/rpmdb.sqlite",
"annotations": {
"evidence": "primary"
}
}
}
]
Now let’s look at the all-layers scope with the same jq command, but on all-layers.json
:
[
{
"purl": "pkg:rpm/fedora/jq@1.7.1-4.fc40?arch=aarch64&distro=fedora-40&upstream=jq-1.7.1-4.fc40.src.rpm",
"locations": {
"path": "/usr/lib/sysimage/rpm/rpmdb.sqlite",
"layerID": "sha256:c25c9316928a0f6488c57f9b62347ebc0f4e787e5b6bdcf1512c2f2c69d5db0c",
"accessPath": "/usr/lib/sysimage/rpm/rpmdb.sqlite",
"annotations": {
"evidence": "primary"
}
}
},
{
"purl": "pkg:rpm/fedora/jq@1.7.1-7.fc40?arch=aarch64&distro=fedora-40&upstream=jq-1.7.1-7.fc40.src.rpm",
"locations": {
"path": "/usr/lib/sysimage/rpm/rpmdb.sqlite",
"layerID": "sha256:5d1f3baf10a3b56faa51f98890082a9d8523abc4984b77f23264db6b4c1a9261",
"accessPath": "/usr/lib/sysimage/rpm/rpmdb.sqlite",
"annotations": {
"evidence": "primary"
}
}
}
]
As you can see, when scope is “squashed” Syft only reports the jq version that was installed in the last layer of the Dockerfile, because that’s what’s actually present if you run the container, whereas the all-layers
scope reports both versions of jq
because it considers packages that are present in any layer.
Does this answer your question?