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?