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.
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.
- Generate a lockfile/source SBOM during MR validation for fast dependency feedback.
- Build the image once and record its digest.
- Generate an image SBOM after build, tied to that digest.
- Correlate vulnerabilities using a recorded advisory snapshot/time.
- Apply policy to the image evidence for release promotion; keep MR policy advisory if evidence is still noisy.
- 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?
When you need evidence about what actually entered the packaged artifact or image, including components introduced after dependency resolution.
Why might a production container need both a source SBOM and an image SBOM?
The source SBOM describes application dependencies; the image SBOM can additionally capture OS/base-image packages. Their scopes differ.
What is the main governance tradeoff of project-local policy?
It is transparent and portable, but project maintainers may be able to change both application code and the gate, reducing separation of duties.
Why should an exception not delete the finding from evidence?
The finding is historical evidence. The exception is a separate governance decision with owner, reason, scope, and expiry.
Why is same-bytes promotion valuable for SBOM evidence?
The approved artifact digest remains stable across environments, so the SBOM and policy result continue to describe the exact bytes being promoted.
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.
- Dependency list — official reference.
- Dependency scanning by using SBOM — official reference.
- Continuous dependency scanning — official reference.
- CI/CD artifacts report types — official reference.
- CI/CD YAML syntax — official reference.
- Security scanning results — official reference.
- Vulnerability report — official reference.
- Security policies — official reference.
- Merge request approval policies — official reference.
- Scan execution policies — official reference.
- CycloneDX Python SBOM generator — official reference.
- CycloneDX specification 1.6 — official reference.
- Anchore Syft — official reference.
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.