ci: scan, describe and sign the published container image - #193
Merged
Conversation
Adopters pulling the image had no way to verify what was inside a tag or that it came from this repository. The image is now built and scanned before it is pushed, rather than after, so a bad image is never published. Fixable high and critical findings fail the release and every finding is reported to the security tab. An SPDX SBOM and a max-detail provenance statement are attached to the image, and the pushed digest is signed with cosign keyless, so there is no signing key to store or rotate. The job summary prints the digest and the exact verification commands. Two exclusions, both deliberate. Unfixed advisories do not block, because a release held up by something no change here can resolve trains people to bypass the gate. Neither does npm's own bundled tree inside the node base image: the container runs node directly and never invokes npm. That second one is not theoretical, node:24-slim currently carries four HIGH findings there, so without it the gate would have failed on the first release. The application's own dependencies are still scanned and still block. A multi-platform build cannot be loaded into the local daemon, so the scan builds linux/amd64 alone and the push builds both. Buildx caches the layers. Closes #163
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #163.
What was there
docker-publish.ymlbuilt and pushed to ghcr.io with no scan, no SBOM, and nosignature. An adopter pulling
ghcr.io/fells-code/seamless-auth-apihad no wayto verify what was inside a tag or that it came from this repository.
What the workflow does now
the release, so a bad image is never published rather than being withdrawn
after the fact. Findings also go to the security tab, including when the gate
failed, so what blocked a release is visible outside the job log.
so the registry can answer what is inside a tag and where it was built.
rotate; the workflow's OIDC token is exchanged for a short-lived certificate.
so the answer to "how do I check this" is in the run rather than in someone's
memory.
The exclusions, both deliberate and one of them load-bearing
Unfixed advisories do not block. An advisory with no available fix is real, but a
release held up by something no change here can resolve only trains people to
bypass the gate.
Neither does npm's own bundled tree inside the Node base image. This one is not
theoretical. I scanned
node:24-slimbefore trusting the gate, and itcurrently carries four fixed HIGH findings, all under
usr/local/lib/node_modules/npm/node_modules/:They are
fixedstatus, soignore-unfixeddoes not filter them, and the gateas I first wrote it would have failed on the very first release. The container
runs
node dist/server.jsand never invokes npm, and nothing in this repositorycan patch them ahead of an upstream image rebuild. Scoping the skip to that one
path keeps high blocking for
/app/node_modules, which is what this repositoryactually controls.
Re-run with the skip in place: exit 0.
Why the image is built twice
A multi-platform build cannot be loaded into the local daemon, so it cannot be
scanned before it is pushed. The scan builds
linux/amd64alone and loads it;the push builds both platforms. Buildx caches the layers, so the second build is
cheap.
What is not verified
The gate has not run against a full application build. The Dockerfile installs a
compiler toolchain, builds the API, then clones and builds the admin dashboard,
which is not quick to reproduce locally and would not be conclusive against a
GitHub runner anyway. The base image is the part most likely to trip the gate and
that part is checked. The first tagged release is the first real run, and if it
does trip, the finding will be in the security tab rather than only in the log.