mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-07-26 11:49:16 +00:00
feat(security): rebuild Docker CLI/Compose from source, pin base image digests (#789)
* feat(security): rebuild Docker CLI/Compose from source, pin base image digests Build Docker CLI v29.4.0 and Compose v5.1.2 from source against Go 1.26.2 to resolve 7 CVEs that were accepted in .trivyignore: - CVE-2026-32280/32281/32282/32283/33810: Go stdlib x509/TLS and DNS issues in the upstream static CLI binary (compiled with Go 1.26.1). All fixed by rebuilding with Go 1.26.2. - CVE-2026-33186: grpc 1.78.0 in Docker CLI. Eliminated from the SBOM by building with the patched Go toolchain and updated module graph. - CVE-2026-33671: picomatch ReDoS in npm bundled with node:22-alpine. Resolved by removing npm/npx from the runtime image entirely (npm is only needed at build time). Remaining 5 entries in .trivyignore are vendored deps inside Compose v5.1.2 (buildkit, moby/docker, otel) that cannot be patched without an upstream Compose release. These will be expressed as OpenVEX not_affected statements in the follow-up PR (feat/security-sbom-vex). Also in this commit: - Pin all Dockerfile FROM lines to @sha256: digests (node:22-alpine, tonistiigi/xx, golang:1.26-alpine) to prevent silent base image changes between scan and publish. - SHA-pin all GitHub Actions in docker-publish.yml and ci.yml that were previously referenced by mutable @vN tags. - Add a binary version smoke test to confirm docker/compose produce expected output before the multi-arch push proceeds. * fix(docker): fix cli-builder vendor mode and compose-builder cache path docker/cli v29.4.0 uses CalVer and ships vendor.mod instead of go.mod, so plain `go build` fails with "cannot find main module." Fix: copy vendor.mod -> go.mod and vendor.sum -> go.sum before building, then pass -mod=vendor so all deps come from the vendored tree with no network access. Cache mount is not needed with vendor mode and is removed. compose-builder used /root/go/pkg/mod as the cache mount target, but golang:alpine sets GOPATH=/go, so the module cache lives at /go/pkg/mod. The wrong path caused a silent cache miss on every build. Corrected.
This commit is contained in:
+13
-13
@@ -39,10 +39,10 @@ jobs:
|
||||
&& github.head_ref != 'chore/refresh-screenshots'
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
@@ -72,7 +72,7 @@ jobs:
|
||||
# re-run `tsc` on the same source. retention-days is the minimum; this
|
||||
# artifact is only needed for the downstream e2e job in the same run.
|
||||
- name: Upload backend dist
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: backend-dist
|
||||
path: backend/dist
|
||||
@@ -90,10 +90,10 @@ jobs:
|
||||
&& github.head_ref != 'chore/refresh-screenshots'
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
@@ -126,10 +126,10 @@ jobs:
|
||||
&& github.head_ref != 'chore/refresh-screenshots'
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v4
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
||||
|
||||
# Computed once per job run. Feeds Dockerfile's APK_CACHE_BUST arg so
|
||||
# the `apk upgrade` layer rebuilds at least once per calendar day, even
|
||||
@@ -140,7 +140,7 @@ jobs:
|
||||
run: echo "date=$(date -u +%Y-%m-%d)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Build Docker image (validation only)
|
||||
uses: docker/build-push-action@v7
|
||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
|
||||
with:
|
||||
context: .
|
||||
push: false
|
||||
@@ -177,14 +177,14 @@ jobs:
|
||||
&& github.head_ref != 'chore/refresh-screenshots'
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
|
||||
# Reuse the dist/ that the `backend` job produced and verified, instead
|
||||
# of running `tsc` a second time against the same source tree. The
|
||||
# composite action's `skip-backend-build` input short-circuits its build
|
||||
# step when this artifact is already in place.
|
||||
- name: Download backend dist
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
name: backend-dist
|
||||
path: backend/dist
|
||||
@@ -199,7 +199,7 @@ jobs:
|
||||
|
||||
- name: Upload E2E report and service logs
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: playwright-report
|
||||
path: |
|
||||
@@ -240,7 +240,7 @@ jobs:
|
||||
permission-pull-requests: write
|
||||
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
|
||||
@@ -293,7 +293,7 @@ jobs:
|
||||
permission-contents: write
|
||||
|
||||
- name: Checkout Sencho repo
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
path: sencho
|
||||
# Need the previous commit to diff against for the docs-change gate.
|
||||
|
||||
@@ -28,15 +28,15 @@ jobs:
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v4
|
||||
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4
|
||||
with:
|
||||
platforms: arm64
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v4
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
||||
|
||||
# Computed once per job run. Feeds Dockerfile's APK_CACHE_BUST arg so
|
||||
# the `apk upgrade` layer rebuilds at least once per calendar day, even
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
run: echo "date=$(date -u +%Y-%m-%d)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v4
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v6
|
||||
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6
|
||||
with:
|
||||
images: saelix/sencho
|
||||
# On a v-tag push we publish:
|
||||
@@ -89,7 +89,7 @@ jobs:
|
||||
# distro package CVEs are arch-agnostic at the manifest level, and
|
||||
# arch-specific container-relevant CVEs are extraordinarily rare.
|
||||
- name: Build release image for pre-publish scan (amd64, loaded)
|
||||
uses: docker/build-push-action@v7
|
||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
|
||||
with:
|
||||
context: .
|
||||
push: false
|
||||
@@ -122,6 +122,11 @@ jobs:
|
||||
- name: Smoke test release image (pre-publish)
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# Verify source-built Docker CLI and Compose binaries are present and functional.
|
||||
# Both checks run in one container to avoid double start-up overhead.
|
||||
docker run --rm --entrypoint sh localhost/sencho:release-scan -c \
|
||||
'docker --version && docker compose version'
|
||||
|
||||
# Not using --rm so that a crashed container sticks around long
|
||||
# enough for `docker logs` in the trap to surface the stack trace.
|
||||
# The trap force-removes it explicitly whether it is still running
|
||||
@@ -141,7 +146,7 @@ jobs:
|
||||
|
||||
- name: Build and push Docker image
|
||||
id: build
|
||||
uses: docker/build-push-action@v7
|
||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
|
||||
Reference in New Issue
Block a user