name: CASAN Supply Chain and Provenance on: push: branches: [main] pull_request: branches: [main] workflow_dispatch: jobs: verify-scan-attest: runs-on: ci-runner timeout-minutes: 60 env: CASAN_CI_RUN_FRONTEND: "1" CASAN_CI_RUN_BACKEND: "1" CASAN_CI_RUN_INFRA_LAB: "0" CASAN_REQUIRE_LIVE_SCANNERS: "1" CASAN_TOOLCHAIN_TRUSTED_DIRS: /usr/bin:/bin:/usr/local/bin steps: - name: Checkout full signed history uses: actions/checkout@v4 with: fetch-depth: 0 - name: Verify trusted toolchain run: | set -euo pipefail bash packages/casan-harness/scripts/bash/toolchain-verify.sh npm osv-scanner trivy docker openssl python3 git - name: Enforce signed commits on main if: github.ref == 'refs/heads/main' run: | set -euo pipefail RANGE="${{ github.event.before }}..${{ github.sha }}" if [ "${{ github.event.before }}" = "0000000000000000000000000000000000000000" ]; then RANGE="${{ github.sha }}"; fi bash packages/casan-harness/scripts/bash/verify-signed-commits.sh "$RANGE" - name: Install locked dependencies run: npm ci - name: Run governed test gate run: bash packages/casan-harness/scripts/bash/ci-harness-gate.sh - name: Build immutable candidate images run: | set -euo pipefail docker build --pull -t casan-control-panel-api:${GITHUB_SHA} -f Dockerfile.control-panel-api . docker build --pull -t casan-control-panel-ui:${GITHUB_SHA} -f Dockerfile.control-panel-ui . - name: Live CVE, secret and misconfiguration scan run: | set -euo pipefail mkdir -p dist/security bash packages/casan-harness/scripts/bash/live-supply-chain-scan.sh dist/security \ casan-control-panel-api:${GITHUB_SHA} casan-control-panel-ui:${GITHUB_SHA} - name: Push scanned images by immutable digest (main only) if: github.ref == 'refs/heads/main' && github.event_name == 'push' env: GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} CASAN_REGISTRY: ${{ vars.CASAN_REGISTRY }} run: | set -euo pipefail [ -n "${GITEA_TOKEN:-}" ] || { echo 'missing GITEA_TOKEN'; exit 1; } REGISTRY="${CASAN_REGISTRY:-${GITHUB_SERVER_URL#https://}}" API_IMAGE="$REGISTRY/${GITHUB_REPOSITORY_OWNER}/casan-control-panel-api:${GITHUB_SHA}" UI_IMAGE="$REGISTRY/${GITHUB_REPOSITORY_OWNER}/casan-control-panel-ui:${GITHUB_SHA}" printf '%s' "$GITEA_TOKEN" | docker login "$REGISTRY" -u "$GITHUB_ACTOR" --password-stdin docker tag casan-control-panel-api:${GITHUB_SHA} "$API_IMAGE" docker tag casan-control-panel-ui:${GITHUB_SHA} "$UI_IMAGE" docker push "$API_IMAGE"; docker push "$UI_IMAGE" docker image inspect --format '{{index .RepoDigests 0}}' "$API_IMAGE" > dist/security/api-image.digest docker image inspect --format '{{index .RepoDigests 0}}' "$UI_IMAGE" > dist/security/ui-image.digest - name: Sign SLSA provenance (main only) if: github.ref == 'refs/heads/main' && github.event_name == 'push' env: CASAN_ATTESTATION_PRIVATE_KEY: ${{ secrets.CASAN_ATTESTATION_PRIVATE_KEY }} run: | set -euo pipefail [ -n "${CASAN_ATTESTATION_PRIVATE_KEY:-}" ] || { echo 'missing CASAN_ATTESTATION_PRIVATE_KEY'; exit 1; } key="$(mktemp)"; trap 'rm -f "$key"' EXIT printf '%s\n' "$CASAN_ATTESTATION_PRIVATE_KEY" > "$key"; chmod 600 "$key" mapfile -t images < <(cat dist/security/api-image.digest dist/security/ui-image.digest) bash packages/casan-harness/scripts/bash/release-provenance.sh create \ dist/security/provenance.intoto.json "$key" dist/security/npm.cyclonedx.json \ docs/output/casan/evidence/harness-test-report.md "${images[@]}" openssl pkey -in "$key" -pubout -out dist/security/provenance-public.pem - name: Upload security evidence and provenance if: always() uses: actions/upload-artifact@v4 with: name: casan-security-${{ github.sha }} path: dist/security/ retention-days: 90