Skip to content

[sbom] Emit container distro in per-container SBOM so grype scans deb/OS packages#28406

Open
lunyue-ms wants to merge 3 commits into
sonic-net:masterfrom
lunyue-ms:test/enable-mgmt-sbom-cve-scan
Open

[sbom] Emit container distro in per-container SBOM so grype scans deb/OS packages#28406
lunyue-ms wants to merge 3 commits into
sonic-net:masterfrom
lunyue-ms:test/enable-mgmt-sbom-cve-scan

Conversation

@lunyue-ms

@lunyue-ms lunyue-ms commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Why I did it

The docker-sonic-mgmt SBOM-based vulnerability scan (added opt-in in #27455)
only ever reported npm CVEs — grype silently skipped every Debian/Ubuntu
(deb) and OS package.

Root cause: the per-container SBOM (target/<container>.gz.sbom.cdx.json)
carried no OS/distro context. metadata.component was type: container, there
was no operating-system component, and every deb component used a bare
pkg:deb/debian/<name>@<ver> PURL with no distro= qualifier. grype needs the
distro to select an OS advisory feed, so it dropped all deb/OS packages and
matched only language (npm/py/…) packages.

For docker-sonic-mgmt (an ubuntu:24.04 image) this meant the scan reported
~22 npm findings and 0 deb, hiding exactly the Debian/Ubuntu CVEs that
image-based scanners (S360, trivy) surface — defeating the purpose of the scan.

Work item tracking
  • Microsoft ADO: 38823175

How I did it

scripts/build_sbom.py — per-container SBOM path (_container_main):

  • Detect the container distro by reading /etc/os-release from the image
    archive (detect_container_distro). Handles both docker-archive
    (<hash>/layer.tar) and OCI (blobs/sha256/*) layouts, and the common case
    where /etc/os-release is a symlink to /usr/lib/os-release. Overridable via
    SBOM_CONTAINER_DISTRO=<id>-<version>. Detection failure degrades gracefully
    (warns, keeps the legacy shape) and never fails the build.
  • Emit a CycloneDX operating-system component carrying syft:distro:id /
    syft:distro:versionID, which grype reads to identify the distro.
  • Namespace deb PURLs to the detected distro with a distro= qualifier, e.g.
    pkg:deb/ubuntu/adduser@3.137ubuntu1?arch=amd64&distro=ubuntu-24.04.
  • The aggregate .bin SBOM path is unchanged (distro defaults to None
    → legacy pkg:deb/debian/…), so this only affects the standalone
    per-container test SBOMs (docker-ptf, docker-sonic-mgmt).

.azure-pipelines/docker-sonic-mgmt.yml:

  • Enable the sbom_vuln_scan job (previously gated with condition: false).
  • Add .azure-pipelines/docker-sonic-mgmt.yml to the PR paths filter so
    changes to this pipeline definition actually trigger it (mirroring
    docker-ptf.yml).

How to verify it

  1. Build the SBOM:
    make ENABLE_SBOM=y ... target/docker-sonic-mgmt.gz
    
    The build log prints [build_sbom.py] Container distro: ubuntu 24.04, and
    target/docker-sonic-mgmt.gz.sbom.cdx.json now contains an
    operating-system component plus pkg:deb/ubuntu/…&distro=ubuntu-24.04 deb
    PURLs.
  2. Scan it:
    grype sbom:target/docker-sonic-mgmt.gz.sbom.cdx.json
    
    deb CVEs are now reported.

Measured on the real docker-sonic-mgmt SBOM, grype findings went from 23 (all
npm, 0 deb)
to 114 (23 npm + 91 deb). The sbom_vuln_scan CI job
(mssonic build 1166089) confirmed 62 deb CVEs (medium+) in addition to the
npm findings, where before it reported npm only.

Which release branch to backport (provide reason below if selected)

  • 202305
  • 202311
  • 202405
  • 202411
  • 202505
  • 202511
  • 202512
  • 202605
  • 202608

Tested branch (Please provide the tested image version)

Description for the changelog

Link to config_db schema for YANG module changes

A picture of a cute animal (not mandatory but encouraged)

@mssonicbld

Copy link
Copy Markdown
Collaborator

/azp run Azure.sonic-buildimage

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@lunyue-ms
lunyue-ms marked this pull request as ready for review July 14, 2026 23:20
@lunyue-ms
lunyue-ms requested a review from lguohan as a code owner July 14, 2026 23:20
Copilot AI review requested due to automatic review settings July 14, 2026 23:20
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the docker-sonic-mgmt Azure Pipeline definition to ensure the SBOM-based vulnerability scan job actually runs and that edits to the pipeline YAML itself will trigger the pipeline in PRs.

Changes:

  • Extend the PR path filter to include .azure-pipelines/docker-sonic-mgmt.yml, so pipeline YAML changes trigger PR validation.
  • Enable the sbom_vuln_scan job by removing the condition: false gate (leaving it continueOnError: true).

Comment thread .azure-pipelines/docker-sonic-mgmt.yml Outdated
Copilot AI review requested due to automatic review settings July 15, 2026 08:01
@mssonicbld

Copy link
Copy Markdown
Collaborator

/azp run Azure.sonic-buildimage

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

Comment thread scripts/build_sbom.py Outdated
Comment thread .azure-pipelines/docker-sonic-mgmt.yml Outdated
Copilot AI review requested due to automatic review settings July 15, 2026 23:12
@lunyue-ms
lunyue-ms force-pushed the test/enable-mgmt-sbom-cve-scan branch from 5e8892a to 1cf1893 Compare July 15, 2026 23:12
@mssonicbld

Copy link
Copy Markdown
Collaborator

/azp run Azure.sonic-buildimage

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

Comment thread scripts/build_sbom.py Outdated
Comment thread scripts/build_sbom.py Outdated
Comment thread scripts/build_sbom.py Outdated
Comment thread scripts/build_sbom.py
Comment thread .azure-pipelines/docker-sonic-mgmt.yml Outdated
Comment out `condition: false` on the sbom_vuln_scan job so the
docker-sonic-mgmt SBOM-based vulnerability scan (grype + OpenVEX)
runs in the pipeline, and add .azure-pipelines/docker-sonic-mgmt.yml
to the PR path filter (mirroring docker-ptf.yml) so a change to this
pipeline definition actually triggers it. The SBOM sidecar is already
produced by the Build stage (ENABLE_SBOM=y); only this post-build
scan job was gated.

Signed-off-by: Lun Yue <17232861+lunyue-ms@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 16, 2026 03:09
@lunyue-ms
lunyue-ms force-pushed the test/enable-mgmt-sbom-cve-scan branch from 1cf1893 to 4ac9266 Compare July 16, 2026 03:09
@mssonicbld

Copy link
Copy Markdown
Collaborator

/azp run Azure.sonic-buildimage

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

Comment thread scripts/build_sbom.py Outdated
Comment thread scripts/build_sbom.py Outdated
@lunyue-ms lunyue-ms changed the title [ci] docker-sonic-mgmt: enable SBOM CVE scan + trigger on yml change [sbom] Emit container distro in per-container SBOM so grype scans deb/OS packages Jul 16, 2026
The per-container SBOM (target/<container>.gz.sbom.cdx.json) carried no
OS/distro context: metadata.component was type=container and every deb
component used a bare pkg:deb/debian/... PURL with no distro qualifier.
grype needs the distro to pick an OS advisory feed, so it silently
skipped ALL deb/OS packages and only reported language (npm) CVEs. e.g.
docker-sonic-mgmt reported 22 npm findings and 0 deb, hiding the
Debian/Ubuntu CVEs that image-based scanners catch.

Detect the container distro from /etc/os-release in the image archive
(overridable via SBOM_CONTAINER_DISTRO), emit a CycloneDX
operating-system component with syft:distro:* properties, and namespace
deb PURLs to the distro with a distro= qualifier. The aggregate .bin
path is unchanged (distro defaults to None -> legacy behavior).

Validated with grype 0.112.0 on the real docker-sonic-mgmt SBOM:
before = 23 findings (all npm, 0 deb); after = 114 (23 npm + 91 deb).

Signed-off-by: Lun Yue <17232861+lunyue-ms@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 16, 2026 03:22
@lunyue-ms
lunyue-ms force-pushed the test/enable-mgmt-sbom-cve-scan branch from 4ac9266 to e69f2f8 Compare July 16, 2026 03:22
@mssonicbld

Copy link
Copy Markdown
Collaborator

/azp run Azure.sonic-buildimage

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Comment thread scripts/build_sbom.py
@lunyue-ms
lunyue-ms requested a review from bhouse-nexthop July 16, 2026 04:20
@lunyue-ms

Copy link
Copy Markdown
Contributor Author

Hi @bhouse-nexthop — this builds on your SBOM work (#27455), so your review would be appreciated.

build_sbom.py: the per-container SBOM carried no OS/distro context, so grype silently skipped every deb/OS package (docker-sonic-mgmt only reported npm CVEs). This detects the container distro from /etc/os-release, emits an operating-system component with syft:distro:*, and adds a distro= qualifier to deb PURLs. The aggregate .bin path is unchanged.

docker-sonic-mgmt.yml: enables the sbom_vuln_scan job you'd gated with condition: false — flagging since it was deliberate; happy to drop that and land only the build_sbom.py fix if you'd prefer.

Verified on the real SBOM: grype went 23 (npm-only) → 114 (23 npm + 91 deb).

Thanks!

The sbom_vuln_scan job installed python3-pip but never used it:
scripts/sbom_vuln_scan.py is pure stdlib and grype is fetched via wget
(scripts/install_sbom_tool.sh), not pip. Only jq is used. Remove
python3-pip so it is not installed on the sonic-ubuntu-1c agent
(security hardening).

Signed-off-by: Lun Yue <17232861+lunyue-ms@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 16, 2026 08:27
@mssonicbld

Copy link
Copy Markdown
Collaborator

/azp run Azure.sonic-buildimage

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

Comment thread scripts/build_sbom.py
Comment on lines +1093 to +1100
if nm.endswith("etc/os-release") or nm.endswith("usr/lib/os-release"):
ex = tf.extractfile(member)
if ex is not None:
got = _parse_os_release(
ex.read().decode("utf-8", "replace")
)
if got:
return got
Comment thread scripts/build_sbom.py
Comment on lines +1109 to +1119
if nm.endswith("etc/os-release") or nm.endswith(
"usr/lib/os-release"
):
ex = outer.extractfile(m)
if ex is not None:
got = _parse_os_release(
ex.read().decode("utf-8", "replace")
)
if got:
return got
continue
Comment thread scripts/build_sbom.py
Comment on lines +1132 to +1149
ex = outer.extractfile(m)
if ex is None:
continue
# Spill the layer to a temp file, then parse with random
# access. Opening a nested tar directly off the gzip outer
# stream (mode="r|*") silently finds no members, and
# buffering whole layers in memory risks OOM on large
# images, so stream to disk (bounded memory) and seek.
tmp = tempfile.NamedTemporaryFile(
prefix="sbom-distro-", suffix=".tar",
dir=os.path.dirname(os.path.abspath(gz_path)) or None,
delete=False,
)
try:
shutil.copyfileobj(ex, tmp, 1024 * 1024)
tmp.close()
with tarfile.open(tmp.name, "r:*") as inner:
got = _search(inner)
Comment thread scripts/build_sbom.py
Comment on lines 507 to 510
def observation_components_for_scope(
target_path: str, scope: str, arch: str, supplier: str,
distro: Optional[tuple] = None,
) -> list:
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants