mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-07-26 11:49:16 +00:00
fix(docker): build compose plugin from ./cmd, not ./cmd/compose (#803)
* fix(docker): build compose plugin from ./cmd, not ./cmd/compose The compose-builder stage in Dockerfile was building ./cmd/compose, which in docker/compose v5.1.2 is `package compose` (cobra command definitions), not the CLI plugin entry point. `go build -o file pkg` against a non-main package writes a Go archive; COPY and chmod accept it, but the kernel cannot exec an ar-format file, so the Docker CLI plugin manager rejected the binary and surfaced only as `docker: unknown command: docker compose` in the v0.64.0 release smoke gate. The plugin's actual main package is at ./cmd (cmd/main.go calls plugin.Run from the cli-plugins framework), per the upstream Makefile in v5.1.2. Also fix the version ldflag to target the v5 module path: in v5 the module moved from github.com/docker/compose/v2 to .../v5, so the previous -X .../v2/internal.Version= silently no-op'd and `docker compose version` would have reported an empty string even after the build target fix. Add -trimpath and -s -w to match the upstream Makefile's release flags. Add an inline ELF-magic sanity check inside the compose-builder stage so this exact failure mode (non-main package emitting an archive) fails the build at the producer instead of surfacing 200 lines later. Hex match via `od -tx1` works on both busybox and GNU coreutils; the named-character form (`od -c`) renders fields differently across the two. Add a binary smoke step to the docker-validate PR job that mirrors the release-time gate, so a regression in either the CLI or compose source build is caught at PR review time rather than at tag-push time. * fix(vex): match Trivy purl format for bundled docker/docker version The compose plugin build target fix exposes a real consequence: now that compose-builder produces a true ELF binary (rather than a Go archive), Trivy can read the embedded Go module metadata and correctly reports the bundled docker/docker dependency as `v28.5.2+incompatible`. The existing VEX subcomponent ID omitted the `+incompatible` Go-module suffix, so Trivy's purl matcher rejected the suppression and CVE-2026-34040 surfaced in the docker-validate scan against the rebuilt image. Update the @id to the literal version Trivy emits and bump the document version per OpenVEX spec. The justification (compose acts as a CLI client, never as a daemon, so the authz-hook path is unreachable) is unchanged and continues to apply to v28.5.2+incompatible identically. * docs(security): explain why CVE-2026-34040 cannot be upgraded out A direct in-build upgrade attempt (`go get github.com/docker/docker@<sha of docker-v29.3.1>`) fails with: invalid version: go.mod has post-v1 module path "github.com/moby/moby/v2" at revision f78c987a moby/moby migrated its Go module path to `github.com/moby/moby/v2` on the docker-29.x branch. The legacy `github.com/docker/docker` import path is therefore frozen at v28.5.2+incompatible from Go's perspective; the proxy.golang.org listing for that module path confirms no v29.x version is resolvable. compose v5.1.2 (and v5.1.3, the latest tag at the time of writing) both still import the legacy path, so the bundled docker/docker library cannot be moved past v28.5.2+incompatible until upstream compose migrates its imports. Document the constraint in two places so a future maintainer (or future me) does not re-attempt the same `go get` and arrive at the same dead end: - Dockerfile compose-builder stage: comment block above the build step. - security/vex/sencho.openvex.json: expand the CVE-2026-34040 statement's impact_statement to spell out the upstream module-split blocker. The not_affected status (compose runs as a CLI client, never executes the daemon authz hook code path) is the principled triage and remains the correct OpenVEX call, not a deferred upgrade. No build or runtime behaviour changes. Pure documentation enrichment. * fix(security): mirror CVE-2026-34040 VEX statement in .trivyignore Trivy reports `Some vulnerabilities have been ignored/suppressed` but still fails the docker-validate scan on CVE-2026-34040, because the OpenVEX statement's product identifier `pkg:oci/sencho` cannot match the build-local image tag `sencho:pr-test`. Trivy does not generate an OCI purl for images that lack a registry digest (aquasecurity/trivy issue 9399), so VEX product matching is a no-op at PR and release scan time. The dual-layer pattern documented in .trivyignore's header was designed for exactly this case: VEX is the canonical, attested, published triage record, and .trivyignore mirrors entries that scan-time matching cannot resolve, with a comment pointing to the corresponding VEX justification. The CVE-2026-34040 OpenVEX statement is unchanged (status=not_affected, justification=vulnerable_code_not_in_execute_path). The .trivyignore entry is a build-time scan filter only, with a referencing comment so auditors land on the VEX file as the source of truth. CVE-2026-39883 already follows this pattern and continues to.
This commit is contained in:
@@ -170,6 +170,18 @@ jobs:
|
||||
format: 'table'
|
||||
trivy-config: trivy.yaml
|
||||
|
||||
# Mirror of the release-time pre-publish smoke gate in docker-publish.yml.
|
||||
# Catches the class of failure where the source-built Docker CLI or
|
||||
# Compose plugin compiles cleanly but does not exec at runtime (e.g. -o
|
||||
# pointed at a non-main Go package, ABI breakage in a base image bump).
|
||||
# The release workflow additionally polls /api/health; PR CI keeps just
|
||||
# the binary check to bound job duration without losing the gate.
|
||||
- name: Smoke test built image (binaries)
|
||||
run: |
|
||||
set -euo pipefail
|
||||
docker run --rm --entrypoint sh sencho:pr-test -c \
|
||||
'docker --version && docker compose version'
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# E2E Tests (PRs only, skipped for release-please PRs)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -23,3 +23,14 @@
|
||||
# BSD operating systems and Sencho ships linux/amd64 + linux/arm64 only.
|
||||
# See security/vex/sencho.openvex.json for the full impact statement.
|
||||
CVE-2026-39883
|
||||
|
||||
# Justification: covered by VEX statement for CVE-2026-34040 (Moby authz
|
||||
# bypass on oversized request body). compose v5.1.2 bundles the legacy
|
||||
# github.com/docker/docker@v28.5.2+incompatible as a client-side library
|
||||
# for API types and codecs; the daemon-side authz hook code path the CVE
|
||||
# affects is unreachable from a CLI client. The advisory's fix in
|
||||
# Docker Engine 29.3.1 ships under the new github.com/moby/moby/v2
|
||||
# module path which compose v5.1.2 does not yet import, so v28.5.2 is
|
||||
# the only Go-module-resolvable version reachable from compose. See
|
||||
# security/vex/sencho.openvex.json for the full impact statement.
|
||||
CVE-2026-34040
|
||||
|
||||
+30
-3
@@ -144,12 +144,39 @@ WORKDIR /src/docker-compose
|
||||
|
||||
RUN mkdir -p /build
|
||||
|
||||
# Build target is ./cmd (the package main with plugin.Run), per docker/compose
|
||||
# v5.1.2's Makefile. The directory ./cmd/compose is package compose (cobra
|
||||
# command definitions only, not main). The module path moved from /v2 to /v5
|
||||
# in the v5 release, so the Version ldflag must reference /v5/internal.
|
||||
#
|
||||
# The bundled github.com/docker/docker dependency stays at v28.5.2+incompatible
|
||||
# (compose v5.1.2's go.mod). It cannot be upgraded to docker-v29.3.1 (the
|
||||
# release that contains the CVE-2026-34040 authz fix) because moby/moby's
|
||||
# docker-29.x branch declares its module path as github.com/moby/moby/v2,
|
||||
# making it unreachable through the legacy github.com/docker/docker import
|
||||
# that compose v5.1.2 still uses. The Go module proxy reflects this and lists
|
||||
# v28.5.2+incompatible as the highest resolvable version. The CVE only affects
|
||||
# Docker Engine's daemon authz hook code path; compose runs as a CLI client
|
||||
# and never executes that path. See security/vex/sencho.openvex.json for the
|
||||
# full not_affected justification, which Trivy honours via trivy.yaml.
|
||||
RUN --mount=type=cache,id=go-mod,sharing=locked,target=/go/pkg/mod \
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} go build \
|
||||
-ldflags "-extldflags=-static \
|
||||
-X github.com/docker/compose/v2/internal.Version=v5.1.2" \
|
||||
-trimpath \
|
||||
-ldflags "-s -w -extldflags=-static \
|
||||
-X github.com/docker/compose/v5/internal.Version=v5.1.2" \
|
||||
-o /build/docker-compose \
|
||||
./cmd/compose
|
||||
./cmd
|
||||
|
||||
# Sanity check: fail the stage immediately if go build did not produce an ELF
|
||||
# executable. Catches the failure mode where -o file points to a non-main
|
||||
# package and Go writes an ar-format archive that passes COPY + chmod but is
|
||||
# not exec-able by the kernel, surfacing only as an opaque plugin-not-found
|
||||
# error from the Docker CLI plugin manager hundreds of build steps later.
|
||||
# `od -tx1` is used instead of `-c` because busybox and GNU coreutils render
|
||||
# `-c` with different field padding; hex output is stable across both.
|
||||
RUN test -f /build/docker-compose \
|
||||
&& magic=$(dd if=/build/docker-compose bs=1 count=4 status=none | od -An -tx1 | tr -d ' \n') \
|
||||
&& [ "$magic" = "7f454c46" ]
|
||||
|
||||
# Stage 5: Production runtime
|
||||
# Runs on the TARGET platform - no compilation happens here.
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
"author": "Studio Saelix",
|
||||
"role": "Vendor",
|
||||
"timestamp": "2026-04-26T00:00:00Z",
|
||||
"last_updated": "2026-04-26T00:00:00Z",
|
||||
"version": 1,
|
||||
"last_updated": "2026-04-27T00:00:00Z",
|
||||
"version": 2,
|
||||
"statements": [
|
||||
{
|
||||
"vulnerability": {
|
||||
@@ -18,7 +18,7 @@
|
||||
"@id": "pkg:oci/sencho",
|
||||
"subcomponents": [
|
||||
{
|
||||
"@id": "pkg:golang/github.com/docker/docker@v28.5.2",
|
||||
"@id": "pkg:golang/github.com/docker/docker@v28.5.2+incompatible",
|
||||
"location": "/usr/local/lib/docker/cli-plugins/docker-compose"
|
||||
}
|
||||
]
|
||||
@@ -26,7 +26,7 @@
|
||||
],
|
||||
"status": "not_affected",
|
||||
"justification": "vulnerable_code_not_in_execute_path",
|
||||
"impact_statement": "The authz bypass applies to Docker daemon plugin authorization hooks. docker-compose v5.1.2 statically bundles docker/docker v28.5.2 as a library but never acts as a daemon and never runs authorization hooks. Sencho invokes compose only for up/down/ps operations against local user-authored compose files. The daemon authz code path is not reachable."
|
||||
"impact_statement": "The authz bypass affects Docker Engine's daemon-side plugin authorization hook on oversized request bodies. docker-compose v5.1.2 statically bundles docker/docker v28.5.2+incompatible as a client-side library for API types and codecs; it never acts as a daemon and never runs authorization hooks. Sencho invokes compose only for up/down/ps operations against local user-authored compose files. The daemon authz code path is not reachable. Note on remediation: the advisory's 'fixed in 29.3.1' refers to Docker Engine the daemon product. The Go library that contains the corresponding source fix is github.com/moby/moby/v2 (a new module path adopted on the docker-29.x branch), not github.com/docker/docker which compose v5.1.2 still imports. Until upstream compose migrates to the v2 import path the v28.5.2+incompatible bundled library is the only Go-module-resolvable version, and the not_affected status is the principled triage rather than a deferred upgrade."
|
||||
},
|
||||
{
|
||||
"vulnerability": {
|
||||
|
||||
Reference in New Issue
Block a user