Files
gstack/.github/workflows/cso-runtime-images.yml
T
Garry TanandOpenAI Codex 4a3c6a8a3c v1.87.0.0 feat: add verified CSO audits and replayable repair bundles (#2852)
* feat(cso): add verified audits and replayable repair bundles

* fix(cso): harden qualification and setup boundaries

* fix(cso): assemble security canaries at runtime

* fix(cso): bound release proof and maintenance work

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* fix(cso): require complete evaluation reports

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* fix(cso): replay expired snapshots from supplied source

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* test(cso): synchronize DNS cancellation assertion

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* chore(ship): exempt repository owner from liveness proof

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* test(cso): make recheck retention overlap deterministic

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* chore: bump version and changelog (v1.85.0.0)

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* fix(cso): pass native release gates

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* chore: move release to v1.86.0.0

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* fix(cso): resolve rechecks by finding

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* chore: move release to v1.87.0.0

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* fix(cso): pass macOS and Windows release gates

Normalize BSD wc output, compare Windows paths by filesystem identity, preserve portable snapshot race coverage, and narrow POSIX-only Windows fixtures.

Co-Authored-By: OpenAI Codex <noreply@openai.com>

* fix(cso): harden native verification gates

* fix(cso): refine Windows native diagnostics

* test(cso): isolate Windows Git startup failure

* test(cso): stabilize Windows native diagnostics

* fix(cso): support hardened Git on Windows

* fix(cso): close final verification gaps

* test(cso): bound cold Docker fixture setup

* fix(cso): restore cross-platform free-suite gates

---------

Co-authored-by: OpenAI Codex <noreply@openai.com>
2026-09-14 15:14:58 -07:00

375 lines
18 KiB
YAML

name: Stage CSO Runtime Images
# Pull requests build every reviewed input on its native architecture without
# publishing. The cso-runtime-release environment must require a maintainer
# reviewer and permit deployments from protected main only. Staging digests
# never enter the execution catalog automatically.
on:
pull_request:
paths:
- '.github/workflows/cso-runtime-images.yml'
- 'lib/cso/**'
- 'scripts/cso-image-matrix.ts'
- 'scripts/cso-verify-runtime-base.ts'
- 'scripts/cso-runtime-promotion.ts'
- 'scripts/cso-public-ghcr.ts'
- 'test/cso-*.test.ts'
workflow_dispatch:
permissions:
contents: read
concurrency:
group: cso-runtime-images-${{ github.event.pull_request.number || 'release' }}
cancel-in-progress: false
jobs:
reviewed-inputs:
if: github.event_name == 'pull_request' || (github.ref == 'refs/heads/main' && github.event_name == 'workflow_dispatch')
runs-on: ubuntu-24.04
outputs:
matrix: ${{ steps.inputs.outputs.matrix }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: 1.4.0
- name: Validate committed image digests and complete platform matrix
id: inputs
run: |
set -eu
matrix="$(bun run scripts/cso-image-matrix.ts)"
printf 'matrix=%s\n' "$matrix" >> "$GITHUB_OUTPUT"
validate-native:
needs: reviewed-inputs
if: github.event_name == 'pull_request'
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.reviewed-inputs.outputs.matrix) }}
runs-on: ${{ matrix.runner }}
timeout-minutes: 35
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6
with:
bun-version: 1.4.0
- uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e
- name: Require native Docker and verify reviewed source manifests
env:
CSO_ARCH: ${{ matrix.arch }}
CSO_PLATFORM: ${{ matrix.platform }}
CSO_STACK: ${{ matrix.stack }}
run: |
set -eu
case "$CSO_ARCH:$(uname -m)" in
amd64:x86_64|arm64:aarch64) ;;
*) echo 'Native architecture mismatch; emulation cannot validate a runtime.' >&2; exit 1 ;;
esac
bun run scripts/cso-verify-runtime-base.ts
- name: Compile native trusted image helpers
run: |
bun build --compile \
--no-compile-autoload-dotenv \
--no-compile-autoload-bunfig \
--no-compile-autoload-tsconfig \
--no-compile-autoload-package-json \
lib/cso/verifier.ts --outfile lib/cso/images/gstack-cso-verifier
bun build --compile \
--no-compile-autoload-dotenv \
--no-compile-autoload-bunfig \
--no-compile-autoload-tsconfig \
--no-compile-autoload-package-json \
lib/cso/preparation-container.ts --outfile lib/cso/images/gstack-cso-preparation
- name: Build the reviewed runtime without publishing
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a
with:
context: lib/cso/images
file: lib/cso/images/${{ matrix.stack }}.Dockerfile
platforms: ${{ matrix.platform }}
build-args: |
BASE_IMAGE=${{ matrix.baseImage }}
UV_IMAGE=${{ matrix.uvImage }}
tags: localhost/gstack-cso-pr-${{ matrix.stack }}:${{ github.sha }}-${{ matrix.arch }}
load: true
push: false
provenance: false
sbom: false
- name: Check the built image identity and fail-closed entrypoint
env:
CSO_IMAGE: localhost/gstack-cso-pr-${{ matrix.stack }}:${{ github.sha }}-${{ matrix.arch }}
run: |
set -eu
test "$(docker image inspect "$CSO_IMAGE" --format '{{.Config.User}}')" = "10001:10001"
test "$(docker image inspect "$CSO_IMAGE" --format '{{json .Config.Entrypoint}}')" = '["/opt/cso/entrypoint"]'
docker run --rm --pull never --network none --read-only --cap-drop ALL \
--security-opt no-new-privileges --entrypoint /opt/cso/entrypoint \
"$CSO_IMAGE" /bin/true
docker image rm "$CSO_IMAGE"
stage:
needs: reviewed-inputs
if: github.ref == 'refs/heads/main' && github.event_name == 'workflow_dispatch'
environment: cso-runtime-release
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.reviewed-inputs.outputs.matrix) }}
runs-on: ${{ matrix.runner }}
timeout-minutes: 40
permissions:
contents: read
packages: write
id-token: write
attestations: write
artifact-metadata: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: 1.4.0
- name: Require the declared native architecture and Docker daemon
env:
CSO_ARCH: ${{ matrix.arch }}
run: |
set -eu
case "$CSO_ARCH:$(uname -m)" in
amd64:x86_64|arm64:aarch64) ;;
*) echo 'Native architecture mismatch; emulation cannot qualify a runtime.' >&2; exit 1 ;;
esac
docker --host unix:///var/run/docker.sock info
- uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4
- uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Verify reviewed source manifests and exact base versions
env:
CSO_PLATFORM: ${{ matrix.platform }}
CSO_STACK: ${{ matrix.stack }}
run: bun run scripts/cso-verify-runtime-base.ts
- name: Compile native trusted HTTP verifier for the runtime image
run: |
bun build --compile \
--no-compile-autoload-dotenv \
--no-compile-autoload-bunfig \
--no-compile-autoload-tsconfig \
--no-compile-autoload-package-json \
lib/cso/verifier.ts --outfile lib/cso/images/gstack-cso-verifier
bun build --compile \
--no-compile-autoload-dotenv \
--no-compile-autoload-bunfig \
--no-compile-autoload-tsconfig \
--no-compile-autoload-package-json \
lib/cso/preparation-container.ts --outfile lib/cso/images/gstack-cso-preparation
- name: Prepare immutable staging tag
id: image
env:
CSO_STACK: ${{ matrix.stack }}
CSO_ARCH: ${{ matrix.arch }}
run: |
set -eu
image="ghcr.io/${GITHUB_REPOSITORY,,}/cso-staging/$CSO_STACK-$CSO_ARCH"
printf 'name=%s\n' "$image" >> "$GITHUB_OUTPUT"
printf 'tag=%s:%s-%s\n' "$image" "$GITHUB_SHA" "$GITHUB_RUN_ID" >> "$GITHUB_OUTPUT"
- name: Build native staging image with SBOM and provenance
id: build
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7
with:
context: lib/cso/images
file: lib/cso/images/${{ matrix.stack }}.Dockerfile
platforms: ${{ matrix.platform }}
build-args: |
BASE_IMAGE=${{ matrix.baseImage }}
UV_IMAGE=${{ matrix.uvImage }}
tags: ${{ steps.image.outputs.tag }}
push: true
provenance: mode=max
attests: type=sbom,generator=${{ matrix.sbomGeneratorImage }}
- name: Extract the immutable image SBOM
env:
CSO_IMAGE: ${{ steps.image.outputs.name }}@${{ steps.build.outputs.digest }}
run: |
set -eu
docker buildx imagetools inspect "$CSO_IMAGE" --format '{{ json .SBOM.SPDX }}' > sbom.spdx.json
jq -e '.spdxVersion == "SPDX-2.3" and (.packages | type == "array")' sbom.spdx.json
- name: Sign build provenance
uses: actions/attest@f7c74d28b9d84cb8768d0b8ca14a4bac6ef463e6 # v4.2.0
with:
subject-name: ${{ steps.image.outputs.name }}
subject-digest: ${{ steps.build.outputs.digest }}
push-to-registry: true
- name: Sign the image SBOM
uses: actions/attest@f7c74d28b9d84cb8768d0b8ca14a4bac6ef463e6 # v4.2.0
with:
subject-name: ${{ steps.image.outputs.name }}
subject-digest: ${{ steps.build.outputs.digest }}
sbom-path: sbom.spdx.json
push-to-registry: true
- name: Verify provenance and SBOM signer identities
env:
GH_TOKEN: ${{ github.token }}
CSO_IMAGE: ${{ steps.image.outputs.name }}@${{ steps.build.outputs.digest }}
run: |
set -eu
signer="https://github.com/$GITHUB_REPOSITORY/.github/workflows/cso-runtime-images.yml@refs/heads/main"
gh attestation verify "oci://$CSO_IMAGE" --repo "$GITHUB_REPOSITORY" --cert-identity "$signer" --source-ref refs/heads/main --source-digest "$GITHUB_SHA" --deny-self-hosted-runners --predicate-type https://slsa.dev/provenance/v1 --format json > provenance-verification.json
gh attestation verify "oci://$CSO_IMAGE" --repo "$GITHUB_REPOSITORY" --cert-identity "$signer" --source-ref refs/heads/main --source-digest "$GITHUB_SHA" --deny-self-hosted-runners --predicate-type https://spdx.dev/Document/v2.3 --format json > sbom-verification.json
- name: Require a public package and anonymously load the exact staged digest
env:
GH_TOKEN: ${{ github.token }}
CSO_IMAGE: ${{ steps.image.outputs.name }}@${{ steps.build.outputs.digest }}
CSO_PLATFORM: ${{ matrix.platform }}
run: |
set -euo pipefail
bun run scripts/cso-public-ghcr.ts verify \
--image "$CSO_IMAGE" --platform "$CSO_PLATFORM" \
--repository "$GITHUB_REPOSITORY" --output public-image.json
- name: Install helper dependencies and compile trusted tools
run: |
bun install --frozen-lockfile --ignore-scripts
bun run build:cso
- name: Exercise containment against the staged digest
env:
GSTACK_CSO_DOCKER_TESTS: "1"
GSTACK_CSO_TEST_IMAGE: ${{ steps.image.outputs.name }}@${{ steps.build.outputs.digest }}
GSTACK_CSO_TEST_PLATFORM: ${{ matrix.platform }}
GSTACK_CSO_TEST_STACK: ${{ matrix.stack }}
GSTACK_CSO_EXPECTED_VERSIONS: ${{ toJSON(matrix.versions) }}
DOCKER_HOST: unix:///var/run/docker.sock
run: bun test --max-concurrency 1 test/cso-docker-integration.test.ts
- name: Record staging evidence without promoting the catalog
env:
CSO_BUILD_REVISION: ${{ matrix.inputRevision }}
CSO_IMAGE: ${{ steps.image.outputs.name }}@${{ steps.build.outputs.digest }}
CSO_PLATFORM: ${{ matrix.platform }}
CSO_RUNTIME_ID: ${{ matrix.runtimeId }}
CSO_STACK: ${{ matrix.stack }}
CSO_VERSIONS: ${{ toJSON(matrix.versions) }}
run: |
set -eu
sha256sum public-image.json sbom.spdx.json provenance-verification.json sbom-verification.json > evidence.sha256
sbom_digest="sha256:$(sha256sum sbom.spdx.json | cut -d ' ' -f 1)"
provenance_digest="sha256:$(sha256sum provenance-verification.json | cut -d ' ' -f 1)"
workflow="https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"
jq -n --arg buildRevision "$CSO_BUILD_REVISION" --arg runtimeId "$CSO_RUNTIME_ID" \
--arg image "$CSO_IMAGE" --arg platform "$CSO_PLATFORM" --arg stack "$CSO_STACK" \
--arg commit "$GITHUB_SHA" --arg workflow "$workflow" --arg sbom "$sbom_digest" \
--arg provenance "$provenance_digest" --argjson versions "$CSO_VERSIONS" \
'{schemaVersion:1,helperAbi:3,state:"staged",buildRevision:$buildRevision,runtimeId:$runtimeId,image:$image,platform:$platform,stack:$stack,versions:$versions,sourceCommit:$commit,workflow:$workflow,sbomDigest:$sbom,provenanceDigest:$provenance,verifiedProvenance:true,qualification:"pending native and private release gates; no automatic promotion"}' > staged-image.json
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: cso-staged-${{ matrix.stack }}-${{ matrix.arch }}
path: |
staged-image.json
public-image.json
sbom.spdx.json
provenance-verification.json
sbom-verification.json
evidence.sha256
if-no-files-found: error
retention-days: 30
qualify-native:
needs: [reviewed-inputs, stage]
if: github.ref == 'refs/heads/main' && github.event_name == 'workflow_dispatch'
environment: cso-runtime-release
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.reviewed-inputs.outputs.matrix) }}
runs-on: ${{ matrix.runner }}
timeout-minutes: 40
permissions:
contents: read
packages: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: 1.4.0
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: cso-staged-${{ matrix.stack }}-${{ matrix.arch }}
path: staged-application
- if: matrix.stack == 'rails'
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: cso-staged-postgresql-${{ matrix.arch }}
path: staged-postgresql
- name: Require native Docker and anonymously load the signed staged inputs
env:
GH_TOKEN: ${{ github.token }}
CSO_ARCH: ${{ matrix.arch }}
CSO_PLATFORM: ${{ matrix.platform }}
CSO_STACK: ${{ matrix.stack }}
run: |
set -eu
case "$CSO_ARCH:$(uname -m)" in amd64:x86_64|arm64:aarch64) ;; *) exit 1 ;; esac
docker --host unix:///var/run/docker.sock info
(cd staged-application && sha256sum --check evidence.sha256)
if test "$CSO_STACK" = rails; then
(cd staged-postgresql && sha256sum --check evidence.sha256)
fi
app="$(jq -er --arg platform "$CSO_PLATFORM" --arg stack "$CSO_STACK" 'select(.state == "staged" and .platform == $platform and .stack == $stack) | .image' staged-application/staged-image.json)"
bun run scripts/cso-public-ghcr.ts verify \
--image "$app" --platform "$CSO_PLATFORM" \
--repository "$GITHUB_REPOSITORY" --output application-public-image.json
printf 'GSTACK_CSO_TEST_IMAGE=%s\n' "$app" >> "$GITHUB_ENV"
printf 'GSTACK_CSO_EXPECTED_VERSIONS=%s\n' "$(jq -c '.versions' staged-application/staged-image.json)" >> "$GITHUB_ENV"
if test "$CSO_STACK" = rails; then
pg="$(jq -er --arg platform "$CSO_PLATFORM" 'select(.state == "staged" and .platform == $platform and .stack == "postgresql") | .image' staged-postgresql/staged-image.json)"
bun run scripts/cso-public-ghcr.ts verify \
--image "$pg" --platform "$CSO_PLATFORM" \
--repository "$GITHUB_REPOSITORY" --output postgresql-public-image.json
printf 'GSTACK_CSO_TEST_POSTGRES_IMAGE=%s\n' "$pg" >> "$GITHUB_ENV"
printf 'GSTACK_CSO_TEST_POSTGRES_VERSION=%s\n' "$(jq -er '.versions.postgresql' staged-postgresql/staged-image.json)" >> "$GITHUB_ENV"
fi
- name: Compile trusted helpers and run the matching staged cold-start journey
env:
GSTACK_CSO_DOCKER_TESTS: "1"
GSTACK_CSO_TEST_PLATFORM: ${{ matrix.platform }}
GSTACK_CSO_TEST_STACK: ${{ matrix.stack }}
DOCKER_HOST: unix:///var/run/docker.sock
run: |
set -eu
bun install --frozen-lockfile --ignore-scripts
bun run build:cso
bun run test:cso:docker
- name: Record native gate evidence without claiming runtime qualification
env:
CSO_STACK: ${{ matrix.stack }}
CSO_PLATFORM: ${{ matrix.platform }}
run: |
set -eu
jq --arg stack "$CSO_STACK" --arg platform "$CSO_PLATFORM" '
. + {
state:"native-gates-passed",
nativeChecks:(if $stack == "postgresql" then {
containmentPassed:true,coldStartPassed:true,multiDatabasePassed:true,readinessPassed:true
} else {
containmentPassed:true,coldStartPassed:true,positiveNegativeAssertionsPassed:true,
acquisitionPublicOnlyPassed:true,offlineLifecyclePassed:true
} + (if $stack == "rails" then {
railsSqlitePassed:true,railsPostgresqlPassed:true,nativeExtensionsPassed:true
} else {} end) end),
privateChecks:{heldOutRepairPassed:"pending",accuracyGatesPassed:"pending",secretCanaryPassed:"pending",watchdogCleanupPassed:"pending"},
qualified:false,
promotion:"prohibited until authenticated private qualification evidence completes every release gate"
}' staged-application/staged-image.json > qualification-evidence.json
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: cso-qualification-gates-${{ matrix.stack }}-${{ matrix.arch }}
path: qualification-evidence.json
if-no-files-found: error
retention-days: 30