diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..a9f7cb4 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,129 @@ +# release flow: +# 1. tag v* on main triggers this workflow +# 2. build job produces zopa.wasm (--release=small) + sha256 and exposes +# base64 subjects for the slsa-github-generator +# 3. provenance job generates SLSA v1.0 attestation via the reusable workflow +# (runs in an isolated builder, uploads the intoto.jsonl to the release) +# 4. sign job runs cosign sign-blob keylessly and attaches the sigstore bundle +# 5. release job attaches the wasm + sha256 to the GitHub Release +# +# secrets used: none. credentials are minted via OIDC per run. + +name: release + +on: + push: + tags: ["v*"] + workflow_dispatch: + +permissions: {} + +jobs: + build: + name: build wasm + checksum + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + outputs: + wasm-name: ${{ steps.pack.outputs.wasm-name }} + wasm-sha256: ${{ steps.pack.outputs.wasm-sha256 }} + subjects-base64: ${{ steps.pack.outputs.subjects-base64 }} + steps: + - name: harden runner + uses: step-security/harden-runner@a5ad31d6a139d249332a2605b85202e8c0b78450 # v2 + with: + egress-policy: audit + + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1 + with: + version: 0.16.0 + + - name: build (release-small) + run: zig build --release=small + + - name: pack + id: pack + run: | + set -euo pipefail + src="zig-out/bin/zopa.wasm" + name="zopa-${GITHUB_REF_NAME}.wasm" + cp "$src" "$name" + sha=$(shasum -a 256 "$name" | awk '{print $1}') + printf '%s %s\n' "$sha" "$name" > "$name.sha256" + subjects=$(printf '%s %s\n' "$sha" "$name" | base64 -w0) + echo "wasm-name=$name" >>"$GITHUB_OUTPUT" + echo "wasm-sha256=$sha" >>"$GITHUB_OUTPUT" + echo "subjects-base64=$subjects" >>"$GITHUB_OUTPUT" + ls -l "$name" "$name.sha256" + wc -c "$name" + + - name: upload wasm + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: wasm + path: | + ${{ steps.pack.outputs.wasm-name }} + ${{ steps.pack.outputs.wasm-name }}.sha256 + if-no-files-found: error + retention-days: 7 + + provenance: + name: slsa v1.0 provenance + needs: [build] + permissions: + actions: read + contents: write + id-token: write + # exception: slsa-github-generator reusable workflows MUST be referenced by tag, + # not sha. internally this ref is parsed as `refs/tags/vX.Y.Z` to fetch the + # pre-built builder binary from the matching release. sha pinning makes the + # binary download fail with "Invalid ref ... Expected ref of the form + # refs/tags/vX.Y.Z". see https://github.com/slsa-framework/slsa-github-generator + uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0 + with: + base64-subjects: ${{ needs.build.outputs.subjects-base64 }} + provenance-name: ${{ needs.build.outputs.wasm-name }}.intoto.jsonl + upload-assets: true + + sign: + name: cosign sign-blob (keyless) + needs: [build, provenance] + runs-on: ubuntu-latest + permissions: + contents: write + id-token: write + steps: + - name: harden runner + uses: step-security/harden-runner@a5ad31d6a139d249332a2605b85202e8c0b78450 # v2 + with: + egress-policy: audit + + - uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1 + + - name: download wasm + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + name: wasm + + - name: sign + # cosign v4 emits a single sigstore bundle (.sigstore.json) by default; + # --output-signature / --output-certificate are deprecated and ignored. + run: | + cosign sign-blob --yes \ + --bundle "${{ needs.build.outputs.wasm-name }}.sigstore.json" \ + "${{ needs.build.outputs.wasm-name }}" + + - name: attach to release + uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3 + with: + files: | + ${{ needs.build.outputs.wasm-name }} + ${{ needs.build.outputs.wasm-name }}.sha256 + ${{ needs.build.outputs.wasm-name }}.sigstore.json + fail_on_unmatched_files: true + generate_release_notes: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d0abc2..7521fd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,3 +27,11 @@ once the first stable tag ships. proxy-wasm host buffer ownership conventions. - Integration tests in Node, wasmtime, and a real Envoy (`zig build test`, `test-wasmtime`, `test-envoy`). +- Automated releases on `v*` tags with SLSA v1.0 build provenance and + cosign keyless signatures. Each release attaches `zopa-.wasm`, + `.sha256`, `.intoto.jsonl`, and `.sigstore.json`. + +### Fixed + +- README badges (CI, OpenSSF Scorecard) now resolve. They were left + pointing at `kanywst/zopa` after the repo moved to `0-draft/zopa`. diff --git a/README.md b/README.md index a75ec99..2ffc150 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ Tiny, zero-allocation authorization engine for proxy-wasm and the edge. ~50 KB. No GC. No deps. [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE) -[![CI](https://github.com/kanywst/zopa/actions/workflows/ci.yml/badge.svg)](https://github.com/kanywst/zopa/actions/workflows/ci.yml) -[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/kanywst/zopa/badge)](https://securityscorecards.dev/viewer/?uri=github.com/kanywst/zopa) +[![CI](https://github.com/0-draft/zopa/actions/workflows/ci.yml/badge.svg)](https://github.com/0-draft/zopa/actions/workflows/ci.yml) +[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/0-draft/zopa/badge)](https://securityscorecards.dev/viewer/?uri=github.com/0-draft/zopa) [![Zig](https://img.shields.io/badge/zig-0.16.0-orange.svg)](https://ziglang.org) zopa runs as a `wasm32-freestanding` module. Hosts hand it a request