diff --git a/.github/scripts/install-trivy.sh b/.github/scripts/install-trivy.sh index 03cb253..e993d11 100644 --- a/.github/scripts/install-trivy.sh +++ b/.github/scripts/install-trivy.sh @@ -1,19 +1,55 @@ #!/usr/bin/env bash set -euo pipefail -# Release assets and these platform names were checked against v0.76.1. +# Public OCI index pins the platform-specific executable without GitHub API access. version=0.76.1 +image=ghcr.io/aquasecurity/trivy:0.76.1@sha256:790fd66d1697395d8693d1acaa1ea702406a20f109b6fc7bac8db623eca6c06c +evidence_dir="${1:-evidence/scanner-install}" +mkdir -p "$evidence_dir" .ci-tools +stage=host-platform +platform= +cid= +finish() { + result=$? + trap - EXIT + if [[ -n "$cid" ]]; then + docker rm -f "$cid" >/dev/null 2>&1 || true + fi + rm -f .ci-tools/trivy.new + python3 - "$evidence_dir/install.json" "$result" "$stage" "$image" "$platform" "$version" <<'PY' +import hashlib +import json +from pathlib import Path +import sys +path, code, stage, image, platform, version = sys.argv[1:] +record = { + "status": "installed" if code == "0" else "failed", + "exit_code": int(code), "stage": stage, "image": image, "platform": platform, + "binary_source": "/usr/local/bin/trivy", "expected_version": version, +} +if code == "0": + record["binary_sha256"] = hashlib.sha256(Path(".ci-tools/trivy").read_bytes()).hexdigest() +Path(path).write_text(json.dumps(record, indent=2) + "\n") +PY + exit "$result" +} +trap finish EXIT case "$(uname -m)" in - x86_64) platform=64bit ;; - aarch64|arm64) platform=ARM64 ;; + x86_64) platform=linux/amd64 ;; + aarch64|arm64) platform=linux/arm64 ;; *) echo "Unsupported scanner host architecture" >&2; exit 1 ;; esac -mkdir -p .ci-tools -asset="trivy_${version}_Linux-${platform}.tar.gz" -gh release download "v${version}" --repo aquasecurity/trivy \ - --pattern "$asset" --pattern "trivy_${version}_checksums.txt" --dir .ci-tools -( - cd .ci-tools - grep " ${asset}$" "trivy_${version}_checksums.txt" | sha256sum --check --strict - - tar -xzf "$asset" trivy -) -echo "$PWD/.ci-tools" >> "$GITHUB_PATH" +stage=pull +docker pull --platform "$platform" "$image" +stage=create +cid=$(docker create --platform "$platform" "$image") +stage=extract +docker cp "$cid:/usr/local/bin/trivy" .ci-tools/trivy.new +chmod 0755 .ci-tools/trivy.new +stage=verify-version +.ci-tools/trivy.new --version | grep -Fx "Version: $version" +mv .ci-tools/trivy.new .ci-tools/trivy +stage=publish-path +if [[ -n "${GITHUB_PATH:-}" ]]; then + echo "$PWD/.ci-tools" >> "$GITHUB_PATH" +fi +stage=complete diff --git a/.github/workflows/image-release.yml b/.github/workflows/image-release.yml index bd8b336..25a9765 100644 --- a/.github/workflows/image-release.yml +++ b/.github/workflows/image-release.yml @@ -110,9 +110,7 @@ jobs: if [[ -n "$release_id" ]]; then gh api --method PATCH "repos/$GITHUB_REPOSITORY/releases/$release_id" -F draft=true > evidence/draft-release.json fi - - name: Install verified scanner release - env: - GH_TOKEN: ${{ github.token }} + - name: Install pinned public OCI scanner run: bash .github/scripts/install-trivy.sh - name: Source/UI dependency vulnerability, secret and misconfiguration gate with SPDX SBOM run: python3 .github/scripts/image-policy.py source evidence/source diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index d51d6ee..7cc368d 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -27,6 +27,11 @@ jobs: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: persist-credentials: false + - name: Initialize retained runtime/scanner state + run: | + mkdir -p evidence/scanner-install + printf '%s\n' '{"status":"not_started","reason":"waiting_for_native_build_and_smoke"}' \ + > evidence/scanner-install/install.json - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: ref: ${{ github.sha }} @@ -35,10 +40,6 @@ jobs: - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 - name: Validate fail-closed policy helpers run: python3 .github/scripts/test-image-policy.py - - name: Install scanner - env: - GH_TOKEN: ${{ github.token }} - run: bash .github/scripts/install-trivy.sh - name: Build once, populating OCI metadata from CI env: BUILDX_METADATA_PROVENANCE: max @@ -68,9 +69,12 @@ jobs: trap 'docker rm -f "$cid" >/dev/null' EXIT docker cp "$cid:/home/agent" "output-$ARCH" - name: Test production image + id: smoke run: sh source/deployments/docker/test-image.sh "$ARCH" - name: Strip binary run: tar -cf "agent-$ARCH.tar" -C "output-$ARCH" . && rm -rf "output-$ARCH" + - name: Install pinned public OCI scanner after runtime tests + run: bash .github/scripts/install-trivy.sh - name: Source/UI dependency and final image gates with SPDX SBOMs run: | failed=0 @@ -87,6 +91,22 @@ jobs: name: agent-${{ matrix.architecture }}.tar path: agent-${{ matrix.architecture }}.tar if-no-files-found: error + - name: Record runtime result independently of scanner availability + if: always() + env: + SMOKE_OUTCOME: ${{ steps.smoke.outcome }} + run: | + mkdir -p evidence + python3 - <<'PY' + import json + import os + from pathlib import Path + Path("evidence/runtime-smoke.json").write_text(json.dumps({ + "architecture": os.environ["ARCH"], + "outcome": os.environ["SMOKE_OUTCOME"], + "workflow_revision": os.environ["GITHUB_SHA"], + }, indent=2) + "\n") + PY - name: Retain reports on failure if: always() uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4