Chapter 26Lesson 03~195 minutes

SBOM Generation, Dependency Lists, Vulnerability Reports, Policy Evaluation, and Supply-Chain Evidence: Configuration, Design Choices, and Tradeoffs

Choose deliberately between build-time and post-build SBOMs, source and image inventories, advisory and blocking policy, and centralized versus project-local control while preserving auditability and portability.

DesignBuild-time SBOMImage SBOMAdvisory vs gateGovernance

Learning objectives

  • Choose build-time versus post-build SBOM generation based on the artifact boundary you need to prove.
  • Distinguish source dependency SBOMs from image/runtime SBOMs and know when both are needed.
  • Design advisory and blocking policies without confusing policy strictness with evidence quality.
  • Compare centralized policy with project autonomy, including GitLab Ultimate policy options and a Free local policy path.
  • Design retention and promotion practices that keep SBOM, artifact, vulnerability data, and policy decisions linked.

1. Design starts with the question you need to answer

“Generate an SBOM” is not a complete requirement. A source-oriented SBOM can represent resolved application dependencies. An image SBOM can reveal OS packages introduced by the base image. A post-build scan can describe what ended up in the artifact, while a pre-build inventory can support faster developer feedback. Choose the evidence point deliberately.

State layer Evidence to capture Why it matters
Source/revision CI_PIPELINE_SOURCE, ref, CI_COMMIT_SHA, pipeline/job IDs An SBOM without the source/build identity it describes is detached inventory, not reproducible evidence.
Compiled CI configuration Merged YAML, rules result, included component/template identity Proves which generator/scanner/policy jobs GitLab actually created.
Build/artifact Artifact filename, SHA-256 digest, package/image identity The SBOM must be tied to the exact bytes consumers receive.
SBOM CycloneDX spec version, serial number, components, PURLs, dependency graph, generator version Describes inventory and relationships; it does not by itself establish vulnerability or trust.
Vulnerability correlation Advisory ID/source, affected component/PURL, severity, fixed version, database snapshot/time Vulnerability status is time-dependent data layered on top of inventory.
Policy Policy/rule version, threshold, exception ID/owner/expiry, allow/block result A gate is a governance decision, not an intrinsic property of the SBOM.
GitLab security state Report ingestion, Dependency List/Vulnerability Report visibility, retention window GitLab UI state is derived from uploaded reports and may expire or be tier-dependent.
External/consumer state Downloaded digest, deployed image/package digest, verification result Proves whether the consumer actually used the bytes described by the evidence bundle.

2. Build-time versus post-build SBOM

Choice Strength Risk / blind spot Good evidence
Build-time from lockfile/resolver Fast, deterministic, close to developer dependency intent. May miss files/packages injected later in packaging or base images. Lockfile digest + resolver/tool version + SBOM + source SHA.
Post-build from directory/archive Describes packaged output more directly. Some ecosystems lose dependency metadata after packaging. Artifact digest + scanner/generator version + SBOM.
Image SBOM Captures OS and application packages in the built image. Does not prove runtime configuration or deployed digest. Image digest + registry identity + image SBOM + deployment digest.
Both source and image Best comparison across intent and shipped bytes. More work, storage, reconciliation, and false-difference handling. Two named SBOMs tied to same source/build pipeline and separate scopes.

3. Source versus image SBOM: different boundaries

A Python lockfile can say the application resolved requests; a container image can also contain OpenSSL, libc, shell tools, CA certificates, and package-manager metadata from the base image. Neither inventory supersedes the other. If the production unit is a container, the image digest is the strongest distribution identity, and the image SBOM should be tied to that digest.

For optional local tooling, Syft can generate CycloneDX from directories or images. If used in production, pin and record its release. The current release observed during this chapter's verification is Syft 1.51.1 (2026-08-27); do not silently follow a mutable latest container tag.

4. Advisory versus blocking policy

Mode Behavior Use when Failure mode to avoid
Advisory Publish findings and policy evaluation but keep delivery moving. New rollout, noisy data, or uncertain ownership while measuring impact. Advisory forever with no owner or graduation criteria.
Blocking Fail or require approval when explicit conditions are met. Evidence quality is stable, remediation/exception workflow exists, and ownership is clear. Blocking on missing/stale evidence without a fail-safe design or exception path.
Tiered Block Critical/High, warn lower severity, with bounded exceptions. Common production balance when severity is only one input among exploitability/context. Treating severity as objective risk without asset/environment context.

A gate should record its rule version and input evidence. If you change a threshold, the historical result must remain explainable under the policy that existed at that time.

5. Centralized policy versus project autonomy

Project-local policy is transparent and available on Free: a script in the repository evaluates the evidence and becomes part of code review. The tradeoff is that the same developers may be able to edit both the application and its gate.

GitLab security policies can enforce scans/jobs/approvals across linked projects, but current policy capabilities are Ultimate. Central enforcement improves separation of duties and consistency, while adding policy-project ownership, propagation, precedence, and failure-mode complexity.

Scenario Recommended starting point Prerequisite Observable evidence
Small team learning SBOMs Project-local Python policy script Free/disposable project Policy file SHA + pipeline/job IDs + policy-result JSON.
Regulated group requiring uniform scan execution GitLab security/pipeline execution policy Ultimate + policy project governance Policy project/ref + scope + injected jobs + result.
Need approval based on findings Merge request approval policy Ultimate + consistent security reports on source/target Completed scan reports + policy evaluation + approval record.
Air-gapped/offline environment Pinned local generator + mirrored advisory data + local policy Controlled tool/database distribution Generator checksum + DB snapshot ID + SBOM + local result.

6. Dependency List and vulnerability state are derived products

The Dependency List is a GitLab view built from supported dependency/container scanning and CycloneDX data on the default branch. The Vulnerability Report is a vulnerability-management view with lifecycle state. Neither replaces the raw SBOM/report that produced it. UI state can also have retention and archival rules different from job artifacts.

On GitLab.com, current documentation says pipeline security findings expire after 30 days. Self-Managed/Dedicated default to 90 days with an administrator-configurable range. Vulnerability records on the default branch have a separate lifecycle. Design your own evidence retention instead of assuming a security tab is permanent storage.

7. Exception design

A good exception is not “ignore CVE.” It identifies the finding/component, owner, reason, scope, expiry, compensating control, and review date. Keep the original finding in the evidence. The policy result can reference the exception, but the SBOM and vulnerability report should not be altered merely to make the gate green.

{
  "exception_id": "EXC-LAB-001",
  "finding_id": "CVE-LAB-2026-0001",
  "component": "pkg:pypi/requests@2.32.5",
  "owner": "lab-security-reviewer",
  "reason": "synthetic checkpoint exception",
  "expires": "2026-09-19",
  "scope": "glci-ch26-sbom-lab only",
  "compensating_control": "none required: synthetic finding"
}

8. Promotion: move same bytes, do not regenerate evidence casually

When an artifact moves from test to staging to production, prefer promotion of the same digest. Rebuilding in every environment creates new bytes and invalidates the direct link to the original SBOM unless you regenerate and re-evaluate evidence for each build. Chapter 23 established immutable artifact promotion; Chapter 26 adds the SBOM/policy bundle to that identity.

9. Worked decision

A containerized service has a Python lockfile, a base image, and a production image. The team needs rapid MR feedback and production traceability.

  1. Generate a lockfile/source SBOM during MR validation for fast dependency feedback.
  2. Build the image once and record its digest.
  3. Generate an image SBOM after build, tied to that digest.
  4. Correlate vulnerabilities using a recorded advisory snapshot/time.
  5. Apply policy to the image evidence for release promotion; keep MR policy advisory if evidence is still noisy.
  6. Deploy by digest and verify that the running target matches the approved digest.

This is more work than one SBOM, but each artifact answers a precise question.

Knowledge check

When is a post-build SBOM preferable to a lockfile-only SBOM?

Why might a production container need both a source SBOM and an image SBOM?

What is the main governance tradeoff of project-local policy?

Why should an exception not delete the finding from evidence?

Why is same-bytes promotion valuable for SBOM evidence?

Next lesson

Diagnostics, failure modes, security, and performance

Preserve first-failure evidence, then diagnose wrong-artifact SBOMs, stale severity data, missing reports, detached digests, and misleading policy outcomes.

Version and compatibility note

GitLab and GitLab Runner evolve continuously. Treat version-sensitive YAML, runner/executor behavior, APIs, security features, policy controls, tiers, and deprecations as assumptions to verify against the current official GitLab documentation before production use. Preserve the exact project/ref/SHA, compiled configuration, pipeline/job IDs, runner/tool versions, and external target evidence used for any reproducible lab or incident record.

Official references and version notes

Documentation verification date: 2026-09-12. GitLab currently documents the Dependency List, artifacts:reports:cyclonedx, Vulnerability Report, and organization-wide security policies as Ultimate features. The mandatory course path therefore keeps the SBOM, vulnerability correlation, policy decision, hashes, and evidence bundle as ordinary artifacts and local scripts so it remains Free/disposable-compatible. GitLab's Dependency List can ingest CycloneDX 1.4, 1.5, or 1.6 documents from the latest default-branch pipeline. Security-policy evaluation trusts scanner artifact reports; policy evaluation does not itself prove the authenticity or integrity of the scanner that produced them. The lab pins cyclonedx-bom==7.3.1, released 2026-07-23, and requests CycloneDX 1.6 explicitly. GitLab security policies are Ultimate and can enforce scan or pipeline behavior centrally. The mandatory course path intentionally models the same evidence/policy separation with repository-local scripts so core learning does not depend on a paid tier.

Current assumptions used in this chapter: Version-sensitive YAML, Runner/executor behavior, APIs, security features, policy controls, tiers, and deprecations must be verified against the exact GitLab, GitLab Runner, tool, and external-system versions used in production. Preserve the exact project/ref/SHA, compiled configuration, pipeline/job IDs, runner/tool versions, and external target evidence used for reproducible work.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.