Arvind Verma

Container image security: scanning, minimal images and signing in CI

Base images, Dockerfile hardening, Trivy, Grype, Docker Scout, Snyk and cloud registry scanners compared, then SBOMs, cosign signing and Kyverno.

By Arvind Verma12 min read

In short

  • Every package in a base image is one you ship and patch. Distroless, Chainguard and Docker Hardened Images drop the shell and package manager.
  • Build, scan with a failing threshold, then sign the digest with cosign keyless. Kyverno 1.20 can refuse pods whose image your workflow did not sign.
  • Pin every GitHub Action to a commit SHA. tj-actions/changed-files in March 2025 and trivy-action in March 2026 both had their version tags rewritten.
On this page
  1. Why the image is where the vulnerabilities are
  2. Choosing a base image
  3. Hardening the Dockerfile
  4. Scanners compared
  5. SBOMs: record what you shipped
  6. Signing and provenance
  7. A complete GitHub Actions workflow
  8. Enforcing at deploy time
  9. Runtime detection with Falco
  10. Hygiene for the CI itself
  11. Checklist

This guide is for small teams that ship containers without a dedicated security engineer. It covers base images, Dockerfile hardening, scanners, SBOMs, signing in GitHub Actions, and refusing unsigned images at deploy time.

Versions, prices and free tiers were checked on 13 November 2026 against vendor documentation, pricing pages and release pages. Container hardening and incident response are part of my day-to-day work, and the pipeline below is the one I would put in front of a small team.

Why the image is where the vulnerabilities are

Your application code is a small part of what you ship. The image also carries an operating system userland: a C library, OpenSSL, often a shell, a package manager and a pile of utilities. Each is something a scanner will flag when a CVE lands, whether your application calls it or not.

Three habits make it worse:

  • Build tools in the runtime image. Compilers, Git, curl and dev dependencies that were only needed to build.
  • Frozen snapshots. An image built in March still carries March’s OpenSSL in November unless something rebuilds it.
  • Tools for the attacker. After a container is compromised, a shell and a package manager are what the attacker reaches for. Docker’s documentation for its hardened images lists removing them as a main way to cut the attack surface.

So the order of work is: ship less, scan what is left, record and sign what you shipped, then refuse anything unsigned at deploy time.

Language packages inside the image are the other half. Dependency scanning tools compared covers catching those at the lockfile, and SAST tools compared covers flaws in your own code.

Choosing a base image

Base Shell and package manager libc Runs as Access and cost
Debian slim (debian:trixie-slim) Yes glibc Root unless you set USER Free
Alpine (alpine:3.24) Yes musl Root unless you set USER Free
Google distroless (-debian13) No; :debug adds a BusyBox shell glibc Non-root with :nonroot tags Free, Apache-2.0
Chainguard Containers Standard images minimal; -dev variants add a shell and package manager Not confirmed Not confirmed Free latest tags; Starter $0 for 5 images; Team $500/month
Docker Hardened Images Runtime images: no; -dev tags for build stages glibc (Debian) or musl (Alpine) Non-root by default Community free, Apache-2.0; Select from $5,000 per repo

Debian slim and Alpine are the familiar choices. trixie-slim is Debian 13 (Docker Hub lists 13.7) and alpine:3.24 is at 3.24.4. Alpine uses musl rather than glibc, so test anything that ships prebuilt native binaries.

Distroless images contain your application and its runtime dependencies, with no package manager or shell. They are now built on Debian 13, and the set covers static, base, cc, Java 17, 21 and 25, Node.js 22, 24 and 26, and Python 3. static-debian13 is about 2 MiB. Every image is signed with cosign keyless, and the README gives the check:

cosign verify gcr.io/distroless/static-debian13:nonroot \
  --certificate-oidc-issuer https://accounts.google.com \
  --certificate-identity [email protected]

Chainguard Containers come in two free forms. The public images at cgr.dev/chainguard/<name> need no login, but are limited to the latest build and only tagged latest and latest-dev, so pin them by digest. Since 17 March 2026 the Starter plan gives you up to 5 images of your choice with all supported versions, 100 GB of downloads a month and 5 users, at $0 with no end date.

Docker Hardened Images (DHI) are free and open source under Apache 2.0 in the Community tier, which covers the full catalogue. You pull them from dhi.io after docker login dhi.io with a free Docker account. Each image ships signed SBOMs, VEX statements and SLSA Build Level 3 provenance. DHI Select starts at $5,000 per repository and adds SLA-backed critical CVE fixes within 7 days and FIPS and STIG variants; Enterprise is priced on request.

For most teams, distroless or DHI for the runtime stage and an ordinary image for the build stage is the right split.

Hardening the Dockerfile

# syntax=docker/dockerfile:1

# Build stage: full toolchain, never shipped
FROM node:24-trixie-slim@sha256:<digest> AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
    npm ci --omit=dev
COPY . .

# Runtime stage: Node.js only, no shell, no package manager, non-root
FROM gcr.io/distroless/nodejs24-debian13:nonroot@sha256:<digest>
WORKDIR /app
COPY --from=build /app /app
CMD ["server.js"]

Multi-stage builds. Only what you COPY --from=build reaches production. The distroless Node.js image sets its entrypoint to node, so CMD is just the script.

Non-root user. The :nonroot tag handles it here. On Debian slim, create the user yourself, with an explicit UID and GID because Docker’s docs warn that otherwise they can change between rebuilds:

RUN groupadd -r -g 10001 app && useradd --no-log-init -r -u 10001 -g app app
USER app

Pin base images by digest. A tag can be moved to a different image; a digest cannot. Look up the current digest with docker buildx imagetools inspect node:24-trixie-slim, which prints a Digest: line. Pinning means you no longer get fixes silently, so let Dependabot raise pull requests that update both tags and digests:

version: 2
updates:
  - package-ecosystem: "docker"
    directory: "/"
    schedule:
      interval: "weekly"
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"

No secrets in layers. Docker’s docs are blunt: build arguments and environment variables persist in the final image. Use a secret mount, as in the npm ci line above, and pass it at build time:

docker build --secret id=npmrc,src=$HOME/.npmrc -t my-app .

In GitHub Actions, docker/build-push-action takes the same thing through its secrets and secret-files inputs. There is a second reason to keep secrets out of ARG: on public repositories, build-push-action adds mode=max provenance by default, and that provenance records build argument values.

Keep the context clean. A .dockerignore that excludes .git, .env and local build output stops them being copied in by COPY . ..

Scanners compared

Tool Licence and price Detects Fail the build with
Trivy v0.76.2 Apache-2.0, free OS and language packages, secrets, IaC misconfig, licences; makes SBOMs --exit-code 1 --severity HIGH,CRITICAL
Grype v0.121.3 (with Syft v1.57.1) Apache-2.0, free OS and language packages; EPSS, KEV and risk scoring; OpenVEX filtering --fail-on high (exit code 2)
Docker Scout Docker Subscription Service Agreement; Personal plan includes 1 Scout-enabled repo, Pro 2, Team unlimited CVEs matched against the image SBOM, base image recommendations, policies --exit-code --only-severity critical,high (exit code 2)
Snyk Container Commercial service; Free plan 100 container tests, Team from $25/month OS packages and application dependencies in the image; base image upgrade advice --severity-threshold=high (exit code 1)

The same gate in each tool:

trivy image --exit-code 1 --severity HIGH,CRITICAL --ignore-unfixed ghcr.io/my-org/my-app:1.4.0
grype ghcr.io/my-org/my-app:1.4.0 --fail-on high --only-fixed
docker scout cves --exit-code --only-severity critical,high ghcr.io/my-org/my-app:1.4.0
snyk container test ghcr.io/my-org/my-app:1.4.0 --file=Dockerfile --severity-threshold=high

--ignore-unfixed and --only-fixed drop findings with no fix yet; blocking on those stops releases without giving anyone work to do. Snyk gives base image advice only when you pass --file. Docker Scout analyses local images by default; Docker Pro ($9 to $11 a month) and Team ($15 to $16 per user a month) raise the Scout-enabled repo allowance.

Scanning built into cloud registries

Service Price Detects When it scans
Amazon ECR basic scanning No additional cost OS packages only On push or manually, once per image per 24 hours
Amazon ECR enhanced scanning (Amazon Inspector) $0.09 per initial scan, $0.01 per rescan, $0.03 per CI/CD scan (US East); 15-day trial OS and language packages On push and continuously
Google Artifact Registry (Artifact Analysis) $0.26 per image, charged once per digest OS packages; Go, Java, Node.js, PHP, Python, Ruby, Rust and .NET; malicious packages as critical; secrets in preview On push; kept current while the image is pulled within 30 days
Microsoft Defender for Containers Per vCore of Kubernetes worker nodes, with 20 free registry scans per charged vCore a month OS packages (Linux and Windows), language packages (Linux) Within hours of push; daily rescans of images pushed or pulled in the last 30 days, or running in a monitored cluster

Azure’s pricing page fills in its figures with script, so the Defender numbers here come from Azure’s retail price API: $0.00941 per vCore-hour in East US (about $6.87 for a 730-hour month) and $0.29 per image scan beyond the free allowance. Defender scans ACR, ECR, Artifact Registry, GCR, Docker Hub and JFrog Artifactory.

Registry scanning is a good second net, because it keeps rescanning images after release. It does not replace a gate in CI, because by the time it reports, the image is already pushed.

SBOMs: record what you shipped

A software bill of materials lists every package in the image. On its own it finds nothing. Its value is that when the next big advisory lands, you can answer “which running images contain this?” from stored SBOMs instead of rebuilding old releases.

BuildKit can produce one during the build and attach it to the image as an attestation, using a Syft-based scanner by default:

docker buildx build -t ghcr.io/my-org/my-app:1.4.0 \
  --sbom=true --attest type=provenance,mode=max --push .

docker buildx imagetools inspect ghcr.io/my-org/my-app:1.4.0 \
  --format "{{ json .SBOM.SPDX }}" > sbom.spdx.json

By default BuildKit scans only the final stage. Declare ARG BUILDKIT_SBOM_SCAN_STAGE=true before the first FROM to record build stages too. Attestations need a registry push; the local image store on a runner cannot hold them.

Syft does the same job standalone and writes several formats at once:

syft ghcr.io/my-org/my-app:1.4.0 -o spdx-json=./spdx.json -o cyclonedx-json=./cdx.json
grype sbom:./spdx.json --fail-on high

The second line is the habit that pays off. Rescan stored SBOMs on a schedule, daily or weekly, so a CVE published after release reaches you without a code change.

Signing and provenance

Keyless signing with cosign

Cosign keyless signing in GitHub Actions uses the workflow’s OIDC token to get a short-lived certificate tied to the workflow’s identity, such as https://github.com/my-org/my-app/.github/workflows/release.yml@refs/heads/main. There is no private key to store, leak or rotate. The job needs id-token: write.

Three things to know about the current version, cosign v3.1.4:

  • Sign the digest, never the tag. Cosign’s own reference says so, to avoid signing something other than what you built.
  • The format changed in cosign 3.0 (October 2025). The Sigstore bundle format is now the default, and signatures use the OCI 1.1 referrers specification. Check that whatever verifies your images supports this before you enforce.
  • The public instance is public. Cosign’s privacy notice says the signing identity is stored in public transparency logs and cannot be removed later. In GitHub Actions that identity includes the repository and workflow path, which matters for a private repository.

Verify a signature like this:

cosign verify ghcr.io/my-org/my-app@sha256:<digest> \
  --certificate-identity https://github.com/my-org/my-app/.github/workflows/release.yml@refs/heads/main \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

SLSA provenance and GitHub artifact attestations

Provenance records how and where an artefact was built. SLSA’s levels are a useful yardstick: Build L1 means provenance exists, L2 means a hosted build platform signs it, and L3 means hardened builds that resist tampering during the build.

BuildKit already adds provenance through build-push-action: mode=max on public repositories, mode=min on private ones. Docker recommends setting provenance: mode=max everywhere, which is safe once secrets are out of build arguments.

GitHub artifact attestations add signed provenance you can check with the GitHub CLI: SLSA v1.0 Build Level 2 on their own, Level 3 when the build runs in a reusable workflow. They work in public repositories on every plan; private and internal repositories need GitHub Enterprise Cloud, where GitHub uses its own Sigstore instance with no public transparency log. If that applies, add this step to the workflow below, with attestations: write and artifact-metadata: write permissions:

      - name: GitHub artifact attestation
        uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
        with:
          subject-name: ${{ env.IMAGE }}
          subject-digest: ${{ steps.build.outputs.digest }}
          push-to-registry: true

Then verify with gh attestation verify oci://ghcr.io/my-org/my-app@sha256:<digest> --owner my-org. actions/attest-build-provenance still works, but since v4 it is a wrapper and new workflows should use actions/attest.

A complete GitHub Actions workflow

Build and push with SBOM and provenance, fail on fixable high or critical vulnerabilities, then sign the digest and attach the SBOM as a signed attestation. Each Action is pinned to the commit behind the release in its comment.

# .github/workflows/release.yml
name: release

on:
  push:
    branches: [main]

permissions: {}

env:
  IMAGE: ghcr.io/my-org/my-app

jobs:
  image:
    runs-on: ubuntu-24.04
    permissions:
      contents: read
      packages: write   # push to GHCR
      id-token: write   # OIDC token for keyless signing
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          persist-credentials: false

      - uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1

      - uses: docker/login-action@8aa5831fb8d48d8f76594c7ddde61ae58019f550 # v4.8.0
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push with SBOM and provenance
        id: build
        uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
        with:
          context: .
          push: true
          tags: ${{ env.IMAGE }}:${{ github.sha }}
          sbom: true
          provenance: mode=max

      - name: Scan, fail on fixable high or critical
        uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
        with:
          image-ref: ${{ env.IMAGE }}@${{ steps.build.outputs.digest }}
          version: v0.76.2
          severity: CRITICAL,HIGH
          ignore-unfixed: true
          exit-code: "1"
        env:
          TRIVY_USERNAME: ${{ github.actor }}
          TRIVY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}

      - uses: sigstore/cosign-installer@756fe2d284c7292c4e9b62c8a28f1e5629c59532 # v4.1.3

      - name: Sign the digest and attest the SBOM
        env:
          REF: ${{ env.IMAGE }}@${{ steps.build.outputs.digest }}
        run: |
          docker buildx imagetools inspect "$REF" --format "{{ json .SBOM.SPDX }}" > sbom.spdx.json
          cosign sign --yes "$REF"
          cosign attest --yes --type spdxjson --predicate sbom.spdx.json "$REF"

Notes on the choices:

  • Scan after push, sign after scan. Attestations need a registry push, so Trivy scans the pushed digest. A failing scan stops the job before signing, and the deploy-time policy below refuses unsigned images, so a vulnerable image never runs.
  • Trivy version. trivy-action v0.36.0 installs Trivy v0.70.0 unless you set version, so the workflow pins the current v0.76.2. The action also pins its own setup-trivy and actions/cache dependencies by SHA.
  • Registry credentials. Trivy reads TRIVY_USERNAME and TRIVY_PASSWORD. Its docs warn these are tried against every registry it meets, which is fine when GHCR is the only one.
  • Fewer third-party Actions. The SBOM comes from BuildKit, not another Action. Every Action you add runs with your token.

Enforcing at deploy time

A signature nobody checks is decoration.

Kyverno. Kyverno 1.20.0, released on 12 November 2026, marks ImageValidatingPolicy stable. Kyverno builds against the cosign v3 libraries, and the 1.20.0 notes include fixes for cosign v2 and v3 verification. This is adapted from Kyverno’s own keyless sample:

apiVersion: policies.kyverno.io/v1
kind: ImageValidatingPolicy
metadata:
  name: require-signed-my-app
spec:
  validationActions: [Audit]   # move to [Deny] once reports are clean
  failurePolicy: Ignore        # move to Fail together with Deny
  webhookConfiguration:
    timeoutSeconds: 30
  matchConstraints:
    resourceRules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE", "UPDATE"]
        resources: ["pods"]
  matchImageReferences:
    - glob: "ghcr.io/my-org/my-app*"
  validationConfigurations:
    mutateDigest: true
    verifyDigest: false
    required: true
  attestors:
    - name: github
      cosign:
        keyless:
          identities:
            - issuer: https://token.actions.githubusercontent.com
              subject: https://github.com/my-org/my-app/.github/workflows/release.yml@refs/heads/main
        ctlog:
          url: https://rekor.sigstore.dev
  variables:
    - name: allImages
      expression: images.?containers.orValue([]) + images.?initContainers.orValue([]) + images.?ephemeralContainers.orValue([])
  validations:
    - expression: variables.allImages.map(image, verifyImageSignatures(image, [attestors.github])).all(count, count > 0)
      message: Images must be signed by the release workflow.

mutateDigest rewrites image tags in the pod spec to digests, so the image cannot change between admission and pull. Private registries need a credentials block, because ImageValidatingPolicy ignores global credential helpers. verifyAttestationSignatures checks the SBOM attestation the same way.

Sigstore policy-controller (v0.15.1) is the alternative, enforcing a ClusterImagePolicy in namespaces labelled policy.sigstore.dev/include=true. Its overview page says bundle-format support covers attestations, not plain signatures, yet cosign 3 signs in that format by default. Its v0.15.0 release moved to the cosign v3 libraries, so the page may be stale. Test a real signed image before relying on it.

Without Kubernetes, put the cosign verify command from earlier in the deploy job, before the image is pulled onto ECS, Cloud Run or a VM. Deploy by digest, never by tag.

Runtime detection with Falco

Falco covers what happens after deployment. It parses Linux system calls at runtime, by default through a modern eBPF probe, and matches them against YAML rules; the defaults flag privilege escalation, shell execution and changes to sensitive directories. It is a graduated CNCF project under Apache-2.0, at 0.45.2 since 29 October 2026.

Distroless pairs well with it: when the image has no shell, any shell that appears in the container is worth an alert.

Hygiene for the CI itself

Your pipeline holds the registry token and the signing identity, so it is part of the supply chain.

Pin third-party Actions to a full commit SHA. In March 2025, attackers retroactively moved several version tags of tj-actions/changed-files to a malicious commit that dumped secrets from runner memory into workflow logs (CVE-2025-30066). CISA’s alert gives an affected window of 12 to 15 March 2025 and covers a related compromise of reviewdog/action-setup. In March 2026 the scanner itself was the target: Aqua’s advisory (CVE-2026-33634) reports that an attacker force-pushed 76 of 77 trivy-action tags and all 7 setup-trivy tags to malicious commits, and published a malicious Trivy v0.69.4. SHA pins were untouched by the tag rewrites.

Consider the new lockfile. Since 28 October 2026, GitHub workflow dependency lockfiles are generally available on Free, Team and Enterprise Cloud plans. The gh actions-lock extension writes .github/workflows/actions.lock, recording the commit behind every action, transitive ones included, and GitHub’s docs say it supersedes SHA pinning. Reusable workflows are not covered, so pin those by SHA. The feature is new, so the workflow above keeps plain SHA pins.

Least privilege. Start with permissions: {} and grant per job. Set persist-credentials: false on checkout unless a later step pushes to Git. actions/checkout v7 refuses fork pull request code under pull_request_target and workflow_run unless you opt in; keep that default. Keep registry and deploy credentials out of jobs that build untrusted pull requests.

For the platform side, including OIDC to cloud providers instead of stored keys, see CI/CD tools compared.

Checklist

  • Runtime stage on distroless, DHI or Chainguard; build tools only in an earlier stage.
  • Non-root user, with an explicit UID when you create it yourself.
  • Base images pinned by digest, with Dependabot docker updates.
  • Secrets through BuildKit secret mounts, never ARG or ENV; a .dockerignore in place.
  • CI scan that fails on fixable high and critical findings: Trivy, Grype, Docker Scout or Snyk.
  • Registry scanning switched on for continuous rescans after release.
  • SBOM attached to every image, and stored SBOMs rescanned on a schedule.
  • Every image signed by digest with cosign keyless, provenance at mode=max, and artifact attestations where your plan allows.
  • Kyverno or policy-controller refusing unsigned images, rolled out in audit mode first; cosign verify in the deploy job where there is no cluster.
  • Falco or equivalent runtime detection on production nodes.
  • Every third-party Action pinned to a commit SHA or locked with actions.lock, least-privilege permissions, and Dependabot’s github-actions updates so pins do not go stale.

Sources

  1. Distroless container images (GoogleContainerTools, GitHub) github.com
  2. Documentation for gcr.io/distroless/nodejs (GoogleContainerTools, GitHub) github.com
  3. Docker Hardened Images (Docker) docker.com
  4. Use a Docker Hardened Image (Docker Docs) docs.docker.com
  5. What are hardened images and why use them? (Docker Docs) docs.docker.com
  6. Chainguard pricing (Chainguard) chainguard.dev
  7. Introducing Chainguard Catalog Starter (Chainguard) chainguard.dev
  8. Understanding Chainguard's container image categories (Chainguard Docs) chainguard.dev
  9. Overview of Chainguard Containers (Chainguard Docs) chainguard.dev
  10. alpine official image (Docker Hub) hub.docker.com
  11. debian official image (Docker Hub) hub.docker.com
  12. Building best practices (Docker Docs) docs.docker.com
  13. Build secrets (Docker Docs) docs.docker.com
  14. docker buildx imagetools inspect (Docker Docs) docs.docker.com
  15. SBOM attestations (Docker Docs) docs.docker.com
  16. Provenance attestations (Docker Docs) docs.docker.com
  17. Add SBOM and provenance attestations with GitHub Actions (Docker Docs) docs.docker.com
  18. docker/build-push-action (GitHub) github.com
  19. Trivy releases (Aqua Security, GitHub) github.com
  20. trivy image CLI reference (Trivy docs) trivy.dev
  21. Private registries (Trivy docs) trivy.dev
  22. aquasecurity/trivy-action (GitHub) github.com
  23. trivy-action v0.36.0 action.yaml (Aqua Security, GitHub) github.com
  24. Trivy ecosystem supply chain temporarily compromised, GHSA-69fq-xp46-6x23 (GitHub) github.com
  25. anchore/grype (GitHub) github.com
  26. Grype CLI reference (Anchore) oss.anchore.com
  27. anchore/syft (GitHub) github.com
  28. Docker Scout (Docker Docs) docs.docker.com
  29. docker scout cves (Docker Docs) docs.docker.com
  30. docker/scout-action (GitHub) github.com
  31. Docker pricing (Docker) docker.com
  32. snyk container test (Snyk CLI docs) docs.snyk.io
  33. Snyk plans and pricing (Snyk) snyk.io
  34. Scan images for software vulnerabilities in Amazon ECR (AWS Docs) docs.aws.amazon.com
  35. Scan images for OS vulnerabilities in Amazon ECR (AWS Docs) docs.aws.amazon.com
  36. New version of Amazon ECR basic scanning is now generally available (AWS) aws.amazon.com
  37. Amazon Inspector pricing (AWS) aws.amazon.com
  38. Container scanning overview (Google Cloud Artifact Analysis docs) docs.cloud.google.com
  39. Artifact Analysis pricing (Google Cloud) cloud.google.com
  40. Vulnerability management for containers (Microsoft Learn) learn.microsoft.com
  41. Microsoft Defender for Cloud pricing (Microsoft Azure) azure.microsoft.com
  42. Azure Retail Prices API: Microsoft Defender for Containers (Microsoft) prices.azure.com
  43. Cosign v3.0.x release notes (Sigstore, GitHub) github.com
  44. Cosign 3.0 available (Sigstore blog) blog.sigstore.dev
  45. cosign sign reference, v3.1.4 (Sigstore, GitHub) github.com
  46. cosign attest reference, v3.1.4 (Sigstore, GitHub) github.com
  47. cosign verify-attestation reference, v3.1.4 (Sigstore, GitHub) github.com
  48. cosign public-instance privacy statement, v3.1.4 source (Sigstore, GitHub) github.com
  49. Signing containers (Sigstore docs) docs.sigstore.dev
  50. sigstore/cosign-installer (GitHub) github.com
  51. Artifact attestations (GitHub Docs) docs.github.com
  52. actions/attest (GitHub) github.com
  53. gh attestation verify (GitHub CLI manual) cli.github.com
  54. SLSA security levels, v1.0 (SLSA) slsa.dev
  55. ImageValidatingPolicy (Kyverno docs) kyverno.io
  56. Verify Cosign Keyless Signatures sample policy (Kyverno) kyverno.io
  57. Kyverno v1.20.0 release (GitHub) github.com
  58. Policy Controller overview (Sigstore docs) docs.sigstore.dev
  59. sigstore/policy-controller releases (GitHub) github.com
  60. Falco documentation (Falco) falco.org
  61. Falco 0.45.2 release (falcosecurity, GitHub) github.com
  62. Supply chain compromise of third-party tj-actions/changed-files and reviewdog/action-setup (CISA) cisa.gov
  63. tj-actions/changed-files advisory GHSA-mrrh-fwg8-r2c3 (GitHub Advisory Database) github.com
  64. Secure use reference (GitHub Docs) docs.github.com
  65. Workflow dependency lockfiles (GitHub Docs) docs.github.com
  66. GitHub Actions workflow lockfiles are generally available (GitHub changelog) github.blog
  67. Keeping your actions up to date with Dependabot (GitHub Docs) docs.github.com
  68. actions/checkout (GitHub) github.com
  69. actions/checkout releases (GitHub) github.com
  70. docker/setup-buildx-action releases (GitHub) github.com
  71. docker/login-action releases (GitHub) github.com
  72. docker/build-push-action releases (GitHub) github.com
  73. aquasecurity/trivy-action releases (GitHub) github.com
  74. sigstore/cosign-installer releases (GitHub) github.com
  75. actions/attest releases (GitHub) github.com