Command Palette

Search for a command to run...

Hectal
Lab 1.3·Supply ChainHIGH

'Are we affected?' in five minutes, not five days

A critical vulnerability in a widely used library is announced on a Friday evening. Leadership asks which of our images contain it. Nobody knows.

Weakness
Executive Order 14028 / NTIA SBOM minimum elements
Target
every image ShopLite builds
SBOMs (CycloneDX / SPDX)syftgrype on SBOMsstoring SBOMs per buildcontinuous re-scanning

Run these techniques only against the lab you own. Using them on systems you don't have permission to test is illegal.

The threat

  1. 01The exposure window

    When Log4Shell was disclosed in December 2021, exploitation attempts started within hours. Organisations spent days grepping repositories and unpacking images, because the vulnerable library was usually a TRANSITIVE dependency, invisible in their own build files. The risk isn't the vulnerability alone: it's the time it takes you to answer 'where do we run this?'

What's at risk

  • Days of exposure while teams search manually, with the real risk of missing an affected service entirely.

Detect

  1. 01Generate an SBOM for an image

    A Software Bill of Materials lists every component in an artifact: OS packages, language libraries, versions, licences, and package URLs (purls). syft builds one by inspecting the image, including dependencies of dependencies. CycloneDX and SPDX are the two standard formats.

    terminal
    $ docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v "$PWD:/out" anchore/syft:v1.26.0 shoplite:good -o cyclonedx-json=/out/sbom.cdx.json
    jq '.components | length' sbom.cdx.json
    ── output ──
    ✔ Loaded image shoplite:good
    ✔ Parsed image sha256:4b1f…
    ✔ Cataloged contents 312 packages
    214
  2. 02Answer the question from the SBOM

    Searching a JSON file takes milliseconds. Across 40 images with stored SBOMs, it's a loop, or a single query in an SBOM platform like Dependency-Track.

    terminal
    $ jq -r '.components[] | select(.name=="lodash") | "\(.name) \(.version) \(.purl)"' sbom.cdx.json
    ── output ──
    lodash 4.17.21 pkg:npm/lodash@4.17.21
  3. 03Scan the SBOM instead of the image

    grype matches an SBOM against vulnerability databases. Because the SBOM is stored, you can re-scan old builds against TODAY's advisories without rebuilding or even pulling the image.

    terminal
    $ docker run --rm -v "$PWD:/in" anchore/grype:v0.94.0 sbom:/in/sbom.cdx.json --fail-on high
    ── output ──
    ✔ Scanned for vulnerabilities [3 vulnerability matches]
    NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY
    busybox 1.37.0-r12 1.37.0-r13 apk CVE-2025-XXXXX Medium
    ...

Defend

  1. 01Produce and keep an SBOM for every build

    Generate the SBOM in the same pipeline that builds the image, attach it to the image as a signed attestation (Lab 1.4), and upload it to an inventory. When the next critical CVE lands, the question becomes a query.

    Hardened

    .github/workflows/build.ymladd to fileyaml
          - uses: anchore/sbom-action@<sha> # v0
            with:
              image: ${{ env.IMAGE }}@${{ steps.build.outputs.digest }}
              format: cyclonedx-json
              output-file: sbom.cdx.json
          - run: cosign attest --yes --type cyclonedx --predicate sbom.cdx.json "$IMAGE@$DIGEST"
          - run: curl -sf -X POST "$DTRACK_URL/api/v1/bom" -H "X-Api-Key: $DTRACK_KEY" -F "project=$PROJECT_UUID" -F "bom=@sbom.cdx.json"
  2. 02Re-scan continuously, not just at build time

    New vulnerabilities are disclosed in software that hasn't changed. A nightly job re-scans the SBOMs of everything currently deployed and opens tickets for new high or critical findings. Dependency-Track does this continuously for every uploaded SBOM.

Verify

  1. 01The Friday-night drill

    Pick any package name and time how long it takes to list every deployed image containing it, with versions. Minutes means the process works. Run the drill before you need it.

    terminal
    $ for f in sboms/*.cdx.json; do jq -r --arg f "$f" '.components[] | select(.name=="openssl" or .name=="libssl3") | "\($f) \(.name) \(.version)"' "$f"; done
    ── output ──
    sboms/shoplite-api.cdx.json libssl3 3.5.1-r0
    sboms/payments.cdx.json libssl3 3.5.1-r0
    sboms/worker.cdx.json libssl3 3.3.2-r1 ← older base image, rebuild

The concepts

What an SBOM is (and isn't)

An SBOM is an inventory: component names, versions, suppliers, identifiers (purl, CPE), relationships, and licences. It doesn't say whether anything is vulnerable. That comes from matching it against advisory databases, and optionally VEX documents, which state 'this CVE doesn't affect us because the vulnerable code isn't used'. Its value is that it's produced once, at build time, and queried forever.

Your turn

01

Generate an SPDX SBOM instead of CycloneDX for the same image.

02

Why scan the SBOM of what's DEPLOYED rather than what's on the main branch?

Interview questions

01

What is an SBOM and why do organisations need one?

0/4 · 0%