ci: harden release workflow — gate on vulnerabilities, enforce egress#5
ci: harden release workflow — gate on vulnerabilities, enforce egress#5Fieldnote-Echo wants to merge 1 commit into
Conversation
|
Note Gemini is unable to generate a review for this pull request due to the file types involved not being currently supported. |
PR Summary by QodoHarden release workflow: pre-push Trivy gate + enforced egress + SBOM attestation
AI Description
Diagram
High-Level Assessment
Files changed (1)
|
Code Review by Qodo
1. Egress block breaks build
|
| egress-policy: block | ||
| allowed-endpoints: > | ||
| github.com:443 | ||
| api.github.com:443 | ||
| objects.githubusercontent.com:443 | ||
| codeload.github.com:443 | ||
| release-assets.githubusercontent.com:443 | ||
| ghcr.io:443 | ||
| pkg-containers.githubusercontent.com:443 | ||
| actions-results-receiver-production.githubapp.com:443 | ||
| productionresultssa0.blob.core.windows.net:443 | ||
| pypi.org:443 | ||
| files.pythonhosted.org:443 | ||
| download.pytorch.org:443 | ||
| nvcr.io:443 | ||
| huggingface.co:443 | ||
| cdn-lfs.huggingface.co:443 | ||
| cdn-lfs-us-1.hf.co:443 | ||
| fulcio.sigstore.dev:443 | ||
| rekor.sigstore.dev:443 | ||
| tuf-repo-cdn.sigstore.dev:443 | ||
| mirror.gcr.io:443 | ||
| registry-1.docker.io:443 | ||
| auth.docker.io:443 | ||
| production.cloudflare.docker.com:443 |
There was a problem hiding this comment.
1. Egress block breaks build 🐞 Bug ☼ Reliability
The release workflow enables egress-policy: block but the allow-list omits endpoints required by docker/gpu/Dockerfile.optimized (Ubuntu APT repositories and astral.sh), so the Docker build will be network-blocked and the job will fail before Trivy gating/push when layers aren’t already satisfied by cache.
Agent Prompt
## Issue description
The release workflow uses `step-security/harden-runner` with `egress-policy: block`, but the `allowed-endpoints` list is missing outbound destinations used during the Docker build (APT mirror hosts and `astral.sh` for installing `uv`). This will cause the `docker/build-push-action` build to fail with blocked egress on a fresh runner or when cache layers are invalidated.
## Issue Context
`docker/gpu/Dockerfile.optimized` performs:
- `apt-get update` / `apt-get install` (requires Ubuntu APT mirrors; typically `archive.ubuntu.com` and `security.ubuntu.com`, often over port 80)
- `curl -LsSf https://astral.sh/uv/install.sh | sh` (requires `astral.sh:443`, and potentially follow-on downloads)
## Fix Focus Areas
- .github/workflows/release.yml[44-71]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 687a13b2f9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| egress-policy: block | ||
| allowed-endpoints: > |
There was a problem hiding this comment.
Allow the Dockerfile's build-time downloads
When this workflow runs the Buildx steps under egress-policy: block, the Dockerfile still performs outbound apt-get update and curl https://astral.sh/uv/install.sh during the image build (docker/gpu/Dockerfile.optimized lines 15-18 and 45-47). This allow-list does not include the Ubuntu/NVIDIA apt hosts or astral.sh, so those deterministic build-time requests are denied before the Trivy gate or push step and the release job cannot publish an image.
Useful? React with 👍 / 👎.
Close the two substantive gaps from the hardening review: - Trivy no longer runs after 'push: true' (which published before scanning). Restructured to build-and-load → gate scan → push: a fixable CRITICAL now fails the job BEFORE the image is published. Full CRITICAL+HIGH results still go to the Security tab via SARIF (non-blocking, since CUDA base layers carry unactionable HIGH advisories); ignore-unfixed keeps the gate actionable. - harden-runner egress-policy audit → block with an allow-list derived from the workflow's known egress (GitHub/GHCR, PyPI, PyTorch, NVIDIA NGC, HF model download, Sigstore, Trivy DB). Observability → enforcement. Plus: checkout persist-credentials: false; attest the CycloneDX SBOM to the registry (verifiable, matching the build provenance). All actions remain SHA-pinned; actionlint clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
687a13b to
0bc6b5f
Compare
There was a problem hiding this comment.
💡 Codex Review
Kokoro-FastAPI/.github/workflows/release.yml
Line 180 in 0bc6b5f
If the BuildKit cache is unavailable, evicted, or invalidated between these steps, this second build reruns mutable apt-get, unpinned uv sync, and remote installer/download commands, so the filesystem pushed here can differ from the SCAN_TAG image Trivy approved. cache-from is only an optimization and does not establish that the pushed digest is the scanned artifact; promote/tag the already scanned image, or scan the exact digest produced by this build before publishing its release tags.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| with: | ||
| egress-policy: audit | ||
| egress-policy: block | ||
| allowed-endpoints: > |
There was a problem hiding this comment.
Allow the UniDic dictionary download host
Even after the previously reported apt and Astral hosts are allowed, the image build still fails under this allow-list when docker/gpu/Dockerfile.optimized lines 77–79 execute python -m unidic download: UniDic fetches its dictionary from cotonoha-dic.s3-ap-northeast-1.amazonaws.com, which is not permitted here. Add that endpoint so the default INCLUDE_JAPANESE=true build can complete.
Useful? React with 👍 / 👎.
| - name: Trivy scan (SARIF report) | ||
| if: always() && steps.build_scan.outcome == 'success' |
There was a problem hiding this comment.
Make the SARIF reporting path non-blocking
When either this second Trivy invocation or the following SARIF upload fails because of a transient tool, database, or code-scanning ingestion error, the step still marks the job failed; the later Build and push step has the implicit success() condition and is therefore skipped. This makes the supposedly non-blocking report an additional release gate even when the preceding CRITICAL scan passed, so the report and upload steps need failure handling such as continue-on-error.
Useful? React with 👍 / 👎.
| @@ -135,6 +203,15 @@ jobs: | |||
| format: cyclonedx-json | |||
There was a problem hiding this comment.
Generate SPDX JSON for the SBOM attestation
actions/attest-sbom requires the supplied SBOM to use SPDX JSON, but this step writes a CycloneDX JSON document and passes it directly as sbom-path. The attestation action therefore cannot create the SBOM attestation, leaving the job failed after the image has been pushed and preventing the subsequent GitHub Release; generate an SPDX JSON file for this action, or use an attestation mechanism that supports CycloneDX.
Useful? React with 👍 / 👎.
Stacked on #4 (base
update/upstream-sync). Closes the two substantive gaps found while reviewing our own supply-chain hardening before considering offering it upstream.The gaps this fixes
push: true, so a vulnerable image was already published by the time it was scanned — detection-after-publish, not prevention.egress-policy: audit— observability only; it logged exfiltration attempts but didn't stop them.Changes
loadlocally → Trivy gate (fails on a fixableCRITICAL,ignore-unfixed: true) → only then push (fast GHA-cache hit, not a second full build). A vulnerable image is never published.CRITICAL,HIGHto the Security tab via SARIF (if: always()), so HIGH advisories are visible without blocking releases — CUDA base layers routinely carry unactionable HIGH CVEs, so gating on those would block every release.audit→blockwith an allow-list derived from the workflow's real egress (GitHub/GHCR, PyPI, PyTorch, NVIDIA NGC, Hugging Face model download, Sigstore, Trivy DB).checkoutpersist-credentials: false; the CycloneDX SBOM is now attested to the registry (verifiable, matching the build provenance) in addition to being attached to the Release.Verification
actionlintclean; YAML valid (16 steps); all 13 actions remain SHA-pinned.allowed-endpoints. It fails closed and reports the exact host — this is expected one-time tuning for block mode, and this PR is the review gate before it takes effect.Not included (deliberately)
The
release.ymlitself stays a fork-specific amd64-GPU build — it is not upstream-bound (that would regress upstream's multi-arch matrix). The portable pieces (digest-pinneddocker-bake.hcl, SHA-pinning) are the upstream candidates, pending a maintainer's interest.🤖 Generated with Claude Code