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
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 \
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 /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-actionv0.36.0 installs Trivy v0.70.0 unless you setversion, so the workflow pins the current v0.76.2. The action also pins its ownsetup-trivyandactions/cachedependencies by SHA. - Registry credentials. Trivy reads
TRIVY_USERNAMEandTRIVY_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
dockerupdates. - Secrets through BuildKit secret mounts, never
ARGorENV; a.dockerignorein 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 verifyin 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-privilegepermissions, and Dependabot’sgithub-actionsupdates so pins do not go stale.
Sources
- Distroless container images (GoogleContainerTools, GitHub) github.com
- Documentation for gcr.io/distroless/nodejs (GoogleContainerTools, GitHub) github.com
- Docker Hardened Images (Docker) docker.com
- Use a Docker Hardened Image (Docker Docs) docs.docker.com
- What are hardened images and why use them? (Docker Docs) docs.docker.com
- Chainguard pricing (Chainguard) chainguard.dev
- Introducing Chainguard Catalog Starter (Chainguard) chainguard.dev
- Understanding Chainguard's container image categories (Chainguard Docs) chainguard.dev
- Overview of Chainguard Containers (Chainguard Docs) chainguard.dev
- alpine official image (Docker Hub) hub.docker.com
- debian official image (Docker Hub) hub.docker.com
- Building best practices (Docker Docs) docs.docker.com
- Build secrets (Docker Docs) docs.docker.com
- docker buildx imagetools inspect (Docker Docs) docs.docker.com
- SBOM attestations (Docker Docs) docs.docker.com
- Provenance attestations (Docker Docs) docs.docker.com
- Add SBOM and provenance attestations with GitHub Actions (Docker Docs) docs.docker.com
- docker/build-push-action (GitHub) github.com
- Trivy releases (Aqua Security, GitHub) github.com
- trivy image CLI reference (Trivy docs) trivy.dev
- Private registries (Trivy docs) trivy.dev
- aquasecurity/trivy-action (GitHub) github.com
- trivy-action v0.36.0 action.yaml (Aqua Security, GitHub) github.com
- Trivy ecosystem supply chain temporarily compromised, GHSA-69fq-xp46-6x23 (GitHub) github.com
- anchore/grype (GitHub) github.com
- Grype CLI reference (Anchore) oss.anchore.com
- anchore/syft (GitHub) github.com
- Docker Scout (Docker Docs) docs.docker.com
- docker scout cves (Docker Docs) docs.docker.com
- docker/scout-action (GitHub) github.com
- Docker pricing (Docker) docker.com
- snyk container test (Snyk CLI docs) docs.snyk.io
- Snyk plans and pricing (Snyk) snyk.io
- Scan images for software vulnerabilities in Amazon ECR (AWS Docs) docs.aws.amazon.com
- Scan images for OS vulnerabilities in Amazon ECR (AWS Docs) docs.aws.amazon.com
- New version of Amazon ECR basic scanning is now generally available (AWS) aws.amazon.com
- Amazon Inspector pricing (AWS) aws.amazon.com
- Container scanning overview (Google Cloud Artifact Analysis docs) docs.cloud.google.com
- Artifact Analysis pricing (Google Cloud) cloud.google.com
- Vulnerability management for containers (Microsoft Learn) learn.microsoft.com
- Microsoft Defender for Cloud pricing (Microsoft Azure) azure.microsoft.com
- Azure Retail Prices API: Microsoft Defender for Containers (Microsoft) prices.azure.com
- Cosign v3.0.x release notes (Sigstore, GitHub) github.com
- Cosign 3.0 available (Sigstore blog) blog.sigstore.dev
- cosign sign reference, v3.1.4 (Sigstore, GitHub) github.com
- cosign attest reference, v3.1.4 (Sigstore, GitHub) github.com
- cosign verify-attestation reference, v3.1.4 (Sigstore, GitHub) github.com
- cosign public-instance privacy statement, v3.1.4 source (Sigstore, GitHub) github.com
- Signing containers (Sigstore docs) docs.sigstore.dev
- sigstore/cosign-installer (GitHub) github.com
- Artifact attestations (GitHub Docs) docs.github.com
- actions/attest (GitHub) github.com
- gh attestation verify (GitHub CLI manual) cli.github.com
- SLSA security levels, v1.0 (SLSA) slsa.dev
- ImageValidatingPolicy (Kyverno docs) kyverno.io
- Verify Cosign Keyless Signatures sample policy (Kyverno) kyverno.io
- Kyverno v1.20.0 release (GitHub) github.com
- Policy Controller overview (Sigstore docs) docs.sigstore.dev
- sigstore/policy-controller releases (GitHub) github.com
- Falco documentation (Falco) falco.org
- Falco 0.45.2 release (falcosecurity, GitHub) github.com
- Supply chain compromise of third-party tj-actions/changed-files and reviewdog/action-setup (CISA) cisa.gov
- tj-actions/changed-files advisory GHSA-mrrh-fwg8-r2c3 (GitHub Advisory Database) github.com
- Secure use reference (GitHub Docs) docs.github.com
- Workflow dependency lockfiles (GitHub Docs) docs.github.com
- GitHub Actions workflow lockfiles are generally available (GitHub changelog) github.blog
- Keeping your actions up to date with Dependabot (GitHub Docs) docs.github.com
- actions/checkout (GitHub) github.com
- actions/checkout releases (GitHub) github.com
- docker/setup-buildx-action releases (GitHub) github.com
- docker/login-action releases (GitHub) github.com
- docker/build-push-action releases (GitHub) github.com
- aquasecurity/trivy-action releases (GitHub) github.com
- sigstore/cosign-installer releases (GitHub) github.com
- actions/attest releases (GitHub) github.com