A pipeline that builds a Docker image without signing it or documenting its components provides no integrity guarantee. An attacker who compromises the registry, the runner or an include can replace the artifact silently. This guide shows you how to add three layers of protection, SBOM, signature and attestation, to your GitLab CI pipelines, with open source tools that you can test right away on your KVM instance.
What you will learn
By the end of this guide, you will know how to:
- Generate an SBOM (Software Bill of Materials) in SPDX or CycloneDX format with Syft and Trivy
- Sign an OCI image with cosign (Sigstore) and store the signature in the registry
- Produce a SLSA attestation of provenance attached to the image
- Verify the signature and the attestation in a deployment pipeline
- Use the SBOM to detect known vulnerabilities, keep the report of a failed job and document the exceptions
Prerequisites
This lesson assumes that you know what a container image and its digest
are, that you have already built and pushed an image to a GitLab project's
registry, and that you are comfortable with protected and masked
variables. The Developer role is enough to run a pipeline and read its
artifacts, but not to set everything up: the cosign private key must be stored
as a protected and masked CI variable in Settings > CI/CD > Variables, which
requires the Maintainer role on the project (a Developer gets a 403 Forbidden on those variables). The container registry must be enabled on
the project and accept OCI artifacts, otherwise the signature and the
attestation have nowhere to be stored.
On the tooling side, the jobs install Syft, cosign and crane themselves, while Trivy comes from a pinned image. On your workstation you need cosign (same version as in the jobs) to generate the key pair, plus jq and base64 to read the attestations. The final lab also requires DSOXLab. Upstream lessons:
- GitLab registries: build and publish the image you sign
- Security scanners: read a vulnerability report in the pipeline
- Hardening your pipelines: build without a daemon using kaniko (measure 2)
- Managing secrets: protect and mask the signing private key
In which context?
Supply chain security for CI/CD is not a theoretical exercise. You need it when:
- You deploy Docker images to production and must prove their integrity to an auditor or a customer
- Your organization must comply with SLSA Level 2+, the NIST SSDF or the NIS2 directive
- You use third-party base images and need to know exactly which components they embed
- A supply chain incident (compromised dependency, tampered registry) forces you to trace what was deployed
- You run an internal registry and want to block unsigned images
The three layers of the CI supply chain
Supply chain security relies on three complementary mechanisms:
| Layer | Question | Tool | Format |
|---|---|---|---|
| SBOM | Which components does my image contain? | Syft, Trivy | SPDX, CycloneDX |
| Signature | Has the image been modified? | cosign (Sigstore) | OCI signature |
| Attestation | Who built it, when, how? | cosign attest | In-toto / SLSA |
Each layer answers a different question. An SBOM without a signature proves nothing. A signature without an attestation does not say where the artifact comes from. The three together form a verifiable chain of trust.
Generate an SBOM in the pipeline
Two tools produce the SBOM of an image in a GitLab job: Syft, specialized in inventory, and Trivy, which can also scan for vulnerabilities. Both read the image from the project registry, right after the build.
With Syft
Syft (Anchore) analyzes an image and produces a detailed SBOM. Its official image, anchore/syft, contains no shell: GitLab cannot run script: in it, even with entrypoint: [""] (measured on v1.54.1: sh not found). The job therefore starts from a pinned Alpine and installs Syft from the official release, verifying its checksum:
sbom-syft: stage: test image: alpine:3.24.2@sha256:294b683cb724975bec92580e1e685676bd4b50bda910ddb8c51d4cabeaec77e6 variables: SYFT_VERSION: "1.54.1" SYFT_REGISTRY_AUTH_AUTHORITY: $CI_REGISTRY SYFT_REGISTRY_AUTH_USERNAME: $CI_REGISTRY_USER SYFT_REGISTRY_AUTH_PASSWORD: $CI_REGISTRY_PASSWORD before_script: - apk add --no-cache curl tar - base="https://github.com/anchore/syft/releases/download/v${SYFT_VERSION}" - curl -sSfLO "${base}/syft_${SYFT_VERSION}_linux_amd64.tar.gz" - curl -sSfLO "${base}/syft_${SYFT_VERSION}_checksums.txt" - grep "syft_${SYFT_VERSION}_linux_amd64.tar.gz" "syft_${SYFT_VERSION}_checksums.txt" | sha256sum -c - - tar -C /usr/local/bin -xzf "syft_${SYFT_VERSION}_linux_amd64.tar.gz" syft script: - syft "registry:${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHORT_SHA}" -o spdx-json=sbom-spdx.json -o cyclonedx-json=sbom-cdx.json artifacts: paths: - sbom-spdx.json - sbom-cdx.json expire_in: 90 daysThe registry: prefix reads the image directly from the GitLab registry, without a Docker daemon, with the credentials that GitLab provides to the job. The two -o options produce both formats in a single analysis. The SBOM lists every package, library and dependency with its exact version. The installation and the analysis were replayed in an Alpine 3.24.2 container: checksum OK, two SBOMs produced.
With Trivy
Trivy can produce an SBOM and scan for vulnerabilities in a single pass:
sbom-trivy: stage: test image: name: aquasec/trivy:0.75.0@sha256:af6acf9a6b85dfe389a1941505c0ce9efef52a4719635e1a962f022a3d855daa entrypoint: [""] variables: TRIVY_USERNAME: $CI_REGISTRY_USER TRIVY_PASSWORD: $CI_REGISTRY_PASSWORD script: - trivy image --format spdx-json --output sbom-trivy.json $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA - trivy sbom sbom-trivy.json --severity HIGH,CRITICAL --exit-code 1 artifacts: paths: - sbom-trivy.json expire_in: 90 daysThe TRIVY_USERNAME and TRIVY_PASSWORD variables give Trivy access to the project registry. The second command uses the SBOM as input to detect CVEs: replayed on nginx:1.29.2-alpine, it finds 3 CRITICAL and 42 HIGH and exits with 1, which fails the job. Trivy reads its own SPDX without difficulty, unlike the one produced by Syft (see the complete pipeline).
Check the content of the SBOM
An SBOM is checked before being signed: an empty or incomplete inventory would be attested as is. Three jq queries are enough to check the number of packages, their versions and the presence of a sensitive library:
# Count the componentsjq '.packages | length' sbom-spdx.json
# List the components with their versionjq -r '.packages[] | "\(.name) \(.versionInfo)"' sbom-spdx.json | head -20
# Look for a specific package (on Alpine, OpenSSL ships as libssl3)jq -c '.packages[] | select(.name | contains("ssl")) | {name, versionInfo}' sbom-spdx.jsonResult measured on the nginx:1.29.2-alpine image with Syft 1.54.1 (the SPDX document also counts the image itself among its packages):
71alpine-baselayout 3.7.0-r0alpine-baselayout-data 3.7.0-r0alpine-keys 2.5-r0alpine-release 3.22.2-r0...{"name":"libssl3","versionInfo":"3.5.4-r0"}{"name":"ssl_client","versionInfo":"1.37.0-r19"}Sign an image with cosign
Install cosign
cosign is part of the Sigstore project. It signs OCI artifacts and stores the signature in the registry, next to the image. This guide uses cosign v3.1.3, installed from its official release and verified by checksum, rather than the Alpine package: Alpine 3.20 still ships v2.2.1, Alpine 3.24 ships v3.0.6. A hidden job .cosign carries the installation and the registry login; the other jobs extend it with extends: in the same .gitlab-ci.yml.
.cosign: image: alpine:3.24.2@sha256:294b683cb724975bec92580e1e685676bd4b50bda910ddb8c51d4cabeaec77e6 variables: COSIGN_VERSION: "3.1.3" COSIGN_PASSWORD: $COSIGN_KEY_PASSWORD before_script: - apk add --no-cache curl jq crane - base="https://github.com/sigstore/cosign/releases/download/v${COSIGN_VERSION}" - curl -sSfLO "${base}/cosign-linux-amd64" - curl -sSfLO "${base}/cosign_checksums.txt" - grep ' cosign-linux-amd64$' cosign_checksums.txt | sha256sum -c - - install -m 0755 cosign-linux-amd64 /usr/local/bin/cosign - echo "$CI_REGISTRY_PASSWORD" | cosign login "$CI_REGISTRY" -u "$CI_REGISTRY_USER" --password-stdin - echo "$CI_REGISTRY_PASSWORD" | crane auth login "$CI_REGISTRY" -u "$CI_REGISTRY_USER" --password-stdincrane is then used, in each job, to resolve the tag to a digest: IMAGE_REF designates the exact content of the image, and not a label that another push could move. The rest of the guide assumes this hidden job.
Sign with a key, without a public log
With a key pair, cosign v3 follows by default the signing configuration published by Sigstore and records every signature in Rekor, the public transparency log. For a private registry, that means publishing the name and digest of your images. As in the SBOM guide, the job therefore creates an empty signing configuration, which calls no public service:
sign-image: extends: .cosign stage: deploy script: - IMAGE_REF="${CI_REGISTRY_IMAGE}@$(crane digest "${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHORT_SHA}")" - echo "$COSIGN_PRIVATE_KEY" > /tmp/cosign.key - cosign signing-config create --out signing-config.json - cosign sign --key /tmp/cosign.key --signing-config signing-config.json --yes "$IMAGE_REF" after_script: - rm -f /tmp/cosign.keycosign signing-config create with no option writes {"mediaType":"application/vnd.dev.sigstore.signingconfig.v0.2+json","rekorTlogConfig":{},"tsaConfig":{}}: neither Rekor nor timestamping. The price is paid at verification, which must state explicitly that no log is expected. If your images are public and you want traceability in Rekor, simply remove --signing-config: cosign then uses the public services.
Generate the key pair
The pair is generated only once, on your workstation, with the same version of cosign as the jobs. The private key is encrypted with the password you type, which becomes the COSIGN_KEY_PASSWORD variable:
cosign generate-key-pairResult measured with cosign 3.1.3:
Enter password for private key:Enter password for private key again:Private key written to cosign.keyPublic key written to cosign.pubStore cosign.key in a protected and masked CI variable. Version cosign.pub in the repository for verification.
Keyless mode with Sigstore
Keyless mode eliminates key management: cosign obtains an ephemeral certificate from Fulcio, authenticated by the GitLab OIDC token, and records the signature in Rekor. In v3, the service URLs come from the configuration published by Sigstore: the old --fulcio-url, --rekor-url and --oidc-issuer options are deprecated, and cosign 3.1.3 refuses to sign when they are passed (cannot specify service URLs and use signing config).
sign-keyless: extends: .cosign stage: deploy id_tokens: SIGSTORE_ID_TOKEN: aud: sigstore script: - IMAGE_REF="${CI_REGISTRY_IMAGE}@$(crane digest "${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHORT_SHA}")" - cosign sign --identity-token="$SIGSTORE_ID_TOKEN" --yes "$IMAGE_REF"Verify a signature
Verification uses the public key versioned in the repository, or, in keyless mode, requires the expected pipeline identity and OIDC issuer. A signature made with the empty configuration has no entry in a transparency log: the verification must know it, hence --insecure-ignore-tlog=true.
# With a key, signature made without a public logcosign verify --key cosign.pub --insecure-ignore-tlog=true "$IMAGE_REF"
# Keyless: verify the pipeline identity (project on gitlab.com)cosign verify \ --certificate-identity-regexp="^https://gitlab.com/my-group/my-project//" \ --certificate-oidc-issuer=https://gitlab.com \ "$IMAGE_REF"Output measured with cosign 3.1.3 against a local registry, after signing with a key:
WARNING: Skipping tlog verification is an insecure practice that lacks transparency and auditability verification for the signature.
Verification for localhost:15002/demo/app@sha256:b03ccb7431a2e3172f5cbae96d82bd792935f33ecb88fbf2940559e475745c4e --The following checks were performed on each of these signatures: - The cosign claims were validated - Existence of the claims in the transparency log was verified offline - The signatures were verified against the specified public keyThree measured behaviors to know. Without --insecure-ignore-tlog=true, verifying a signature made without a log fails with not enough verified log entries from transparency log: 0 < 1. The "verified offline" line is displayed anyway: it proves nothing here, it is the warning that tells the truth. Finally, on an image that was never signed, cosign v3 answers no signatures found.
Produce a SLSA attestation
A provenance attestation binds the artifact to its build context: who, when, how, with what.
Attach the SBOM as an attestation
The job wraps the SPDX SBOM produced by sbom-syft in an in-toto attestation signed by the same key as the image, then pushes it to the registry next to it:
attest-sbom: extends: .cosign stage: deploy needs: - sbom-syft - sign-image script: - IMAGE_REF="${CI_REGISTRY_IMAGE}@$(crane digest "${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHORT_SHA}")" - echo "$COSIGN_PRIVATE_KEY" > /tmp/cosign.key - cosign signing-config create --out signing-config.json - cosign attest --key /tmp/cosign.key --signing-config signing-config.json --yes --predicate sbom-spdx.json --type spdxjson "$IMAGE_REF" after_script: - rm -f /tmp/cosign.keySLSA provenance attestation
For a SLSA-compliant attestation, use the slsaprovenance predicate:
attest-provenance: extends: .cosign stage: deploy script: - IMAGE_REF="${CI_REGISTRY_IMAGE}@$(crane digest "${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHORT_SHA}")" - echo "$COSIGN_PRIVATE_KEY" > /tmp/cosign.key - | cat > provenance.json <<EOF { "buildType": "https://gitlab.com/gitlab-ci", "builder": { "id": "$CI_RUNNER_ID" }, "invocation": { "configSource": { "uri": "$CI_PROJECT_URL", "digest": {"sha1": "$CI_COMMIT_SHA"}, "entryPoint": ".gitlab-ci.yml" } }, "metadata": { "buildStartedOn": "$CI_JOB_STARTED_AT", "completeness": { "parameters": true, "environment": true, "materials": false } }, "materials": [ { "uri": "$CI_REGISTRY_IMAGE", "digest": {"sha256": "${IMAGE_REF##*sha256:}"} } ] } EOF - cosign signing-config create --out signing-config.json - cosign attest --key /tmp/cosign.key --signing-config signing-config.json --yes --predicate provenance.json --type slsaprovenance "$IMAGE_REF" after_script: - rm -f /tmp/cosign.keyVerify an attestation
cosign verify-attestation checks the signature of the attestation then returns its content encoded in base64: the final jq decodes the payload to read the attested predicate.
# Verify the SBOM attestationcosign verify-attestation --key cosign.pub --insecure-ignore-tlog=true \ --type spdxjson "$IMAGE_REF" | jq -r '.payload' | base64 -d | jq .
# Verify the provenance attestationcosign verify-attestation --key cosign.pub --insecure-ignore-tlog=true \ --type slsaprovenance "$IMAGE_REF" | jq -r '.payload' | base64 -d | jq .Expected result (provenance):
{ "buildType": "https://gitlab.com/gitlab-ci", "builder": { "id": "42" }, "invocation": { "configSource": { "uri": "https://gitlab.example.com/my-group/my-project", "digest": {"sha1": "<commit SHA>"}, "entryPoint": ".gitlab-ci.yml" } }}Complete pipeline: build → SBOM → sign → attest → verify
Here is a pipeline that chains all the steps:
stages: - build - test - sign - verify
variables: IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
build: stage: build image: name: ghcr.io/osscontainertools/kaniko:v1.28.6-debug@sha256:7ab44d0131c3051b6dea128067e258c7afb5c4815082a93b9cf6c624f2140bf0 entrypoint: [""] before_script: - mkdir -p /kaniko/.docker - echo "{\"auths\":{\"${CI_REGISTRY}\":{\"auth\":\"$(printf "%s:%s" "${CI_REGISTRY_USER}" "${CI_REGISTRY_PASSWORD}" | base64 | tr -d '\n')\"}}}" > /kaniko/.docker/config.json script: - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile Dockerfile --destination $IMAGE --digest-file $CI_PROJECT_DIR/image-digest artifacts: paths: - image-digest
sbom: stage: test image: alpine:3.24.2@sha256:294b683cb724975bec92580e1e685676bd4b50bda910ddb8c51d4cabeaec77e6 variables: SYFT_VERSION: "1.54.1" SYFT_REGISTRY_AUTH_AUTHORITY: $CI_REGISTRY SYFT_REGISTRY_AUTH_USERNAME: $CI_REGISTRY_USER SYFT_REGISTRY_AUTH_PASSWORD: $CI_REGISTRY_PASSWORD before_script: - apk add --no-cache curl tar - base="https://github.com/anchore/syft/releases/download/v${SYFT_VERSION}" - curl -sSfLO "${base}/syft_${SYFT_VERSION}_linux_amd64.tar.gz" - curl -sSfLO "${base}/syft_${SYFT_VERSION}_checksums.txt" - grep "syft_${SYFT_VERSION}_linux_amd64.tar.gz" "syft_${SYFT_VERSION}_checksums.txt" | sha256sum -c - - tar -C /usr/local/bin -xzf "syft_${SYFT_VERSION}_linux_amd64.tar.gz" syft script: - syft "registry:${IMAGE}" -o spdx-json=sbom.json -o cyclonedx-json=sbom.cdx.json artifacts: paths: - sbom.json - sbom.cdx.json
vuln-scan: stage: test image: name: aquasec/trivy:0.75.0@sha256:af6acf9a6b85dfe389a1941505c0ce9efef52a4719635e1a962f022a3d855daa entrypoint: [""] script: - trivy sbom sbom.cdx.json --severity HIGH,CRITICAL --exit-code 1 needs: - sbom
sign-and-attest: extends: .cosign stage: sign needs: - build - sbom script: - IMAGE_REF="${CI_REGISTRY_IMAGE}@$(crane digest "${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHORT_SHA}")" - echo "$COSIGN_PRIVATE_KEY" > /tmp/cosign.key - cosign signing-config create --out signing-config.json - cosign sign --key /tmp/cosign.key --signing-config signing-config.json --yes "$IMAGE_REF" - cosign attest --key /tmp/cosign.key --signing-config signing-config.json --yes --predicate sbom.json --type spdxjson "$IMAGE_REF" after_script: - rm -f /tmp/cosign.key
verify-before-deploy: extends: .cosign stage: verify script: - IMAGE_REF="${CI_REGISTRY_IMAGE}@$(crane digest "${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHORT_SHA}")" - cosign verify --key cosign.pub --insecure-ignore-tlog=true "$IMAGE_REF" - cosign verify-attestation --key cosign.pub --insecure-ignore-tlog=true --type spdxjson "$IMAGE_REF" allow_failure: falseThis pipeline assumes the hidden job .cosign defined above in the same file. Each job resolves IMAGE_REF to a digest from the tag pushed by build; kaniko also writes this digest into the image-digest artifact.
The vuln-scan job reads the CycloneDX SBOM, not the SPDX. Measured with Syft 1.54.1 and Trivy 0.75.0 on an Alpine image: trivy sbom on Syft's SPDX displays Unsupported os family="none", scans nothing and exits with 0, so a --exit-code 1 gate turns green without having checked anything. On the CycloneDX of the same analysis, it finds the vulnerabilities (1 CRITICAL, 11 HIGH) and exits with 1. The SPDX remains the one that is attested.
Keep the report, and waive only what was triaged
The vuln-scan job is meant to fail, and that is what makes its evidence disappear: with the default artifacts:when: on_success setting, GitLab does not keep the artifacts of a failed job. The report that explains the red pipeline is therefore lost at the very moment it is needed. A --format json --output combined with when: always keeps it readable in the forge in both cases:
script: - trivy sbom sbom.cdx.json --severity HIGH,CRITICAL --exit-code 1 --format json --output trivy-report.json artifacts: when: always paths: - trivy-report.jsonThen comes the case of a vulnerability the security team has reviewed and decided not to fix right away. Trivy reads those decisions from a .trivyignore file at the repository root, one identifier per line. Two disciplines apply, because a silent exception passes the review and survives for years without anyone knowing why: the reason is written as a comment right above the identifier, and only the triaged finding belongs there.
# Package present in the image but never loaded by the application.# Security review of 2026-10-20, to be lifted at the next base change.CVE-2024-21538Waiving the other critical vulnerabilities to make the pipeline pass amounts to removing the scanner while keeping its job: the dashboard turns green and the delivery is no safer. A pipeline that stays red after a justified exception is doing its job, and the real fix is to change the image base.
Block unsigned images
To prevent the deployment of unverified images, add a mandatory verification job to the deployment pipeline. Like the other cosign jobs, it logs in to the registry before reading the signature:
stages: - pre-deploy - deploy
verify-signature: extends: .cosign stage: pre-deploy script: - | if ! cosign verify --key cosign.pub --insecure-ignore-tlog=true "$DEPLOY_IMAGE" 2>/dev/null; then echo "ERROR: unsigned image or invalid signature" echo "Image: $DEPLOY_IMAGE" exit 1 fi echo "Signature verified" allow_failure: falseFor Kubernetes, Kyverno and Connaisseur can block unsigned images at the cluster level.
Troubleshooting
Most failures come from a missing registry authentication, a key that does not match, or an SBOM format that the next tool does not read. The table starts from the symptom displayed by the job.
| Symptom | Likely cause | Fix |
|---|---|---|
no signatures found or no matching signatures | Image not signed or wrong key | Check that cosign.pub matches the key used to sign |
not enough verified log entries from transparency log: 0 < 1 | Signature made without Rekor (empty configuration) | Add --insecure-ignore-tlog=true to the verification |
cannot specify service URLs and use signing config | cosign v2 options (--fulcio-url, --rekor-url, --oidc-issuer) on a cosign v3 | Remove them: v3 reads the Sigstore configuration, or a configuration created by cosign signing-config create |
UNAUTHORIZED: authentication required | docker login not done | Add cosign login to before_script with CI_REGISTRY_* |
Error: signing [image]: getting remote image | Image not found | Check that the $CI_COMMIT_SHORT_SHA tag exists in the registry |
| Empty SBOM (0 packages) | scratch image with no package database | Scan the Dockerfile or the sources. A distroless image is not empty: Syft reads the dpkg database there (5 packages on distroless/static-debian12) |
trivy sbom gate always green, Unsupported os in the log | Syft's SPDX SBOM not recognized by Trivy | Scan the CycloneDX SBOM |
MANIFEST_UNKNOWN on the attestation | The registry does not support OCI artifacts | Upgrade the registry or use --attachment-tag-prefix |
| cosign keyless fails | OIDC misconfigured | Check id_tokens in the .gitlab-ci.yml and /.well-known/openid-configuration |
| Trivy SBOM scan timeout | Large image | Add --timeout 15m and use the Trivy DB cache |
vuln-scan job red, but no report in the artifacts | artifacts:when defaults to on_success: nothing is kept when the job fails | Add when: always to the artifacts: block |
Hands-on lab
Two labs chain the two halves of this guide on the same image, the one the pipeline of the course's running-example project publishes to the registry of its forge. The first adds the scan and its threshold: validation reads from the forge API a Trivy JSON report kept as an artifact, covering the image at the commit tag, a pipeline that really failed on a critical vulnerability with no deployment at all, and a .trivyignore that waives the single triaged finding, reason included. The second adds the inventory and the proof: a CycloneDX SBOM as an artifact, a signature and an attestation that cosign verifies itself, and a pipeline that did refuse an unsigned image before the deployment.
Key takeaways
Supply chain security in CI relies on three complementary layers, which three practices make useful:
- SBOM: document what each image contains. Syft or Trivy generate a complete inventory in a standard format (SPDX, CycloneDX). Without an SBOM, you cannot answer the question "does this CVE affect us?"
- Signature: prove integrity. cosign signs the image and stores the signature in the registry. Any modification invalidates the signature. With a key, an empty signing configuration avoids publishing your images in Rekor; keyless mode removes the key but requires an issuer recognized by Fulcio, such as gitlab.com.
- Attestation: prove provenance. The SLSA attestation binds the artifact to the pipeline that built it. Without an attestation, a signature proves integrity but not origin.
- Verification: the critical link. Signing without verifying is useless. The
verify-before-deployjob must be mandatory and blocking in the deployment pipeline. - Automation: every step must be in the pipeline, not in a manual script. What is not automated will not be done.
- Vulnerability triage: keep the report even when the job fails (
artifacts:when: always), and waive in.trivyignoreonly the finding that was reviewed, with its reason as a comment. An exception without a reason is a vulnerability accepted by oversight.
Sources
- SLSA, Supply-chain Levels for Software Artifacts, OpenSSF
- Sigstore, cosign, Sigstore
- Syft, SBOM generation, Anchore
- Trivy, Vulnerability scanner and SBOM, Aqua Security
- GitLab CI, Signing with cosign, GitLab Docs
- SPDX Specification, Linux Foundation
- CycloneDX Specification, OWASP
- NIST SSDF, Secure Software Development Framework, NIST
Test your knowledge
Knowledge check
Check what you have learned with this interactive quiz
How it works
- The timer starts when you click Start
- Multiple choice, true/false and short answer questions
- You can move between questions
- Detailed results are shown at the end
Starts the quiz and the timer
Review
(0/0)Score by domain
A domain groups several guides and gets several questions, so its score is more reliable than a per-topic score, which often rests on a single question.
Summary
Strengths
No topic passed at 80% on at least two questions yet.
Weak areas
No topic below the pass mark.
Recommended next
Hints before you try again?
Continue the course: GitLab CI: audit, compliance and pipeline traceability
A new full quiz with random questions
Work again only on the questions you missed