diff --git a/.github/workflows/release-check.yml b/.github/workflows/release-check.yml new file mode 100644 index 0000000..0f7d1ec --- /dev/null +++ b/.github/workflows/release-check.yml @@ -0,0 +1,51 @@ +name: Supply Chain Check + +on: + pull_request: + paths: + - ".github/workflows/release.yml" + - ".github/workflows/release-check.yml" + - "composer.json" + +permissions: + contents: read + +jobs: + verify-release-artifacts: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v7 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.4" + extensions: simplexml, zip + coverage: none + + - name: Install runtime dependencies + run: composer install --no-dev --prefer-dist --no-progress --no-interaction + + - name: Create test package + run: composer archive --format=zip --file=elp-parser-test + + - name: Generate test CycloneDX SBOM + uses: anchore/sbom-action@v0.24.2 + with: + path: . + format: cyclonedx-json + output-file: elp-parser-test.cdx.json + upload-artifact: false + upload-release-assets: false + + - name: Generate and verify checksums + run: | + sha256sum elp-parser-test.zip elp-parser-test.cdx.json > SHA256SUMS + sha256sum --check SHA256SUMS + + - name: Validate CycloneDX document + run: | + jq -e '.bomFormat == "CycloneDX"' elp-parser-test.cdx.json + jq -e '.specVersion != null' elp-parser-test.cdx.json + jq -e '.metadata.component != null' elp-parser-test.cdx.json diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2869696..354de33 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,31 +10,68 @@ jobs: build: runs-on: ubuntu-latest permissions: + actions: read + attestations: write contents: write + id-token: write + steps: - uses: actions/checkout@v7 - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: '8.0' + php-version: "8.4" extensions: simplexml, zip coverage: none - - name: Install dependencies - run: composer install --no-dev --prefer-dist --no-progress + - name: Install runtime dependencies + run: composer install --no-dev --prefer-dist --no-progress --no-interaction - name: Get tag id: tag - run: echo "tag=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT + run: echo "tag=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT" - name: Create package - run: composer archive --format=zip --file="elp-parser-${{ steps.tag.outputs.tag }}" + run: | + composer archive \ + --format=zip \ + --file="elp-parser-${{ steps.tag.outputs.tag }}" + + - name: Generate CycloneDX SBOM + uses: anchore/sbom-action@v0.24.2 + with: + path: . + format: cyclonedx-json + output-file: elp-parser-${{ steps.tag.outputs.tag }}.cdx.json + upload-artifact: false + upload-release-assets: false + + - name: Generate checksums + run: | + sha256sum \ + "elp-parser-${{ steps.tag.outputs.tag }}.zip" \ + "elp-parser-${{ steps.tag.outputs.tag }}.cdx.json" \ + > SHA256SUMS + + - name: Attest build provenance + uses: actions/attest-build-provenance@v4.2.2 + with: + subject-path: elp-parser-${{ steps.tag.outputs.tag }}.zip + + - name: Attest SBOM + uses: actions/attest-sbom@v4.1.0 + with: + subject-path: elp-parser-${{ steps.tag.outputs.tag }}.zip + sbom-path: elp-parser-${{ steps.tag.outputs.tag }}.cdx.json - name: Create Release uses: softprops/action-gh-release@v3 with: - files: elp-parser-${{ steps.tag.outputs.tag }}.zip + files: | + elp-parser-${{ steps.tag.outputs.tag }}.zip + elp-parser-${{ steps.tag.outputs.tag }}.cdx.json + SHA256SUMS draft: false prerelease: false generate_release_notes: true diff --git a/README.md b/README.md index 43aa0d5..b9493fd 100644 --- a/README.md +++ b/README.md @@ -429,6 +429,10 @@ php tests/upstream-compat.php /path/to/exelearning/test/fixtures - [Security policy](SECURITY.md) - [Changelog](CHANGELOG.md) +## Release integrity + +Tagged releases publish a CycloneDX SBOM, SHA-256 checksums and GitHub artifact attestations for build provenance and the SBOM. See [Release supply chain](docs/supply-chain.md). + ## License The project is distributed under the MIT License. See [LICENSE.md](LICENSE.md). diff --git a/docs/supply-chain.md b/docs/supply-chain.md new file mode 100644 index 0000000..90053e4 --- /dev/null +++ b/docs/supply-chain.md @@ -0,0 +1,28 @@ +# Release supply chain + +Tagged releases publish more than the Composer archive. + +Each release contains: + +- `elp-parser-.zip` — distributable package archive; +- `elp-parser-.cdx.json` — CycloneDX JSON software bill of materials; +- `SHA256SUMS` — SHA-256 checksums for the archive and SBOM. + +GitHub Artifact Attestations are also created for: + +- build provenance of the ZIP artifact; +- the CycloneDX SBOM bound to that ZIP artifact. + +The attestation actions use GitHub OIDC and repository-scoped workflow permissions; no additional signing key or long-lived secret is stored in the repository. + +## Verify checksums + +```bash +sha256sum --check SHA256SUMS +``` + +## Verify GitHub attestations + +Consumers with the GitHub CLI can verify release artifact attestations using GitHub's artifact-attestation commands against the repository identity. + +A dedicated pull-request workflow builds a test archive, generates the SBOM and validates checksums/CycloneDX structure so release packaging changes are checked before a tag is created. diff --git a/mkdocs.yml b/mkdocs.yml index e908822..78b2c1f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -59,6 +59,7 @@ nav: - Guides: - Validation: validation.md - Security: security.md + - Release Supply Chain: supply-chain.md - iDevices: idevices.md - Assets and Package Entries: assets.md - Performance: performance.md