diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000000..d9275dc521 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,5 @@ +# Release-critical paths require review from the repository owner. +/.github/workflows/ @Widthdom +/install.sh @Widthdom +/src/CodeIndex/CodeIndex.csproj @Widthdom +/nuget.config @Widthdom diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 22c9962ad1..12072072b9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,6 +18,8 @@ on: permissions: contents: write + id-token: write + attestations: write jobs: release: @@ -388,6 +390,52 @@ jobs: cd release-files sha256sum * > sha256sums.txt + - name: Import release GPG key + env: + RELEASE_GPG_PRIVATE_KEY: ${{ secrets.RELEASE_GPG_PRIVATE_KEY }} + RELEASE_GPG_PASSPHRASE: ${{ secrets.RELEASE_GPG_PASSPHRASE }} + run: | + set -euo pipefail + if [ -z "${RELEASE_GPG_PRIVATE_KEY}" ]; then + echo "RELEASE_GPG_PRIVATE_KEY secret is required to sign sha256sums.txt." >&2 + exit 1 + fi + + mkdir -p ~/.gnupg + chmod 700 ~/.gnupg + printf '%s' "${RELEASE_GPG_PRIVATE_KEY}" | gpg --batch --import + if [ -n "${RELEASE_GPG_PASSPHRASE}" ]; then + printf '%s' "${RELEASE_GPG_PASSPHRASE}" > ~/.gnupg/release-passphrase + chmod 600 ~/.gnupg/release-passphrase + fi + + - name: Sign release checksum manifest + env: + RELEASE_GPG_KEY_ID: ${{ secrets.RELEASE_GPG_KEY_ID }} + RELEASE_GPG_PASSPHRASE: ${{ secrets.RELEASE_GPG_PASSPHRASE }} + run: | + set -euo pipefail + sign_args=(--batch --yes --armor --detach-sign) + if [ -n "${RELEASE_GPG_KEY_ID}" ]; then + sign_args+=(--local-user "${RELEASE_GPG_KEY_ID}") + fi + if [ -n "${RELEASE_GPG_PASSPHRASE}" ]; then + sign_args+=(--pinentry-mode loopback --passphrase-file ~/.gnupg/release-passphrase) + fi + + gpg "${sign_args[@]}" --output release-files/sha256sums.txt.asc release-files/sha256sums.txt + test -s release-files/sha256sums.txt.asc + + - name: Attest release artifacts + uses: actions/attest-build-provenance@v2 + with: + subject-path: | + release-files/*.tar.gz + release-files/*.zip + release-files/*.cdx.json + release-files/sha256sums.txt + release-files/sha256sums.txt.asc + - name: Write release install notes run: | cat > release-install-notes.md <<'EOF' @@ -472,7 +520,7 @@ jobs: # リリースが "Failed to download sha256sums.txt ... HTTP 404" で # 失敗した。verify step が取得する全 asset をポーリングし、伝播の # 遅い asset があってもリリースを失敗させず待ち切る。 - VERIFY_ASSET_NAMES: CodeIndex-linux-x64.tar.gz sha256sums.txt + VERIFY_ASSET_NAMES: CodeIndex-linux-x64.tar.gz sha256sums.txt sha256sums.txt.asc run: | set -euo pipefail mapfile -t expected_assets < <(cd release-files && printf '%s\n' *) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d705095cfc..9802724505 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -62,6 +62,20 @@ Keep changes focused and follow the existing style of the files you touch: contracts change; - add or update tests when behavior changes. +## Release-Critical Paths + +Changes to release-critical files require CODEOWNERS review once branch +protection is configured to require it: + +- `.github/workflows/` +- `install.sh` +- `src/CodeIndex/CodeIndex.csproj` +- `nuget.config` + +When adding a new file that can affect release artifact contents, signing, +publishing, installer behavior, or package restore trust, add it to +`.github/CODEOWNERS` in the same change. + Before opening a pull request, run the checks that match the change. For code changes, the default full validation is: diff --git a/USER_GUIDE.md b/USER_GUIDE.md index 24b1a62d65..6a60f225fc 100644 --- a/USER_GUIDE.md +++ b/USER_GUIDE.md @@ -436,6 +436,32 @@ Choose the install channel based on runtime ownership and network shape: For a full comparison, package maintainer guidance, and planned channels such as winget, apt, rpm, Snap, and Flatpak, see [DISTRIBUTION.md](DISTRIBUTION.md). +### Release artifact verification + +GitHub releases publish `sha256sums.txt` for every archive and SBOM asset, +plus a detached GPG signature at `sha256sums.txt.asc`. Verify the checksum +manifest before trusting downloaded release artifacts: + +```bash +gpg --verify sha256sums.txt.asc sha256sums.txt +sha256sum -c sha256sums.txt +``` + +The GPG signature verifies the checksum manifest through the release signing +key. + +Release workflows also emit GitHub build provenance attestations for the +published archives, SBOM, checksum manifest, and checksum signature. Verify +that an artifact was produced by this repository's GitHub Actions release +workflow: + +```bash +gh attestation verify CodeIndex-linux-x64.tar.gz -R Widthdom/CodeIndex +``` + +The GitHub attestation verifies that the artifact was generated by the +repository workflow identity. + ### Option A: One-liner install (no .NET required) Works in containers, CI, and any Linux/macOS environment — no .NET SDK needed. diff --git a/changelog.d/unreleased/1674.security.md b/changelog.d/unreleased/1674.security.md new file mode 100644 index 0000000000..534e749cf9 --- /dev/null +++ b/changelog.d/unreleased/1674.security.md @@ -0,0 +1,16 @@ +--- +category: security +issues: + - 1674 +affected: + - .github/workflows/release.yml + - USER_GUIDE.md +--- + +## English + +- **Release artifacts now receive GitHub provenance attestations (#1674)** - release archives, SBOMs, checksum manifests, and checksum signatures now get GitHub build provenance attestations so users can verify they came from the repository release workflow. + +## 日本語 + +- **リリース成果物に GitHub provenance attestation を付与するようになりました (#1674)** - リリースアーカイブ、SBOM、チェックサム manifest、チェックサム署名に GitHub build provenance attestation を付与し、リポジトリの release workflow 由来であることを検証できるようにしました。 diff --git a/changelog.d/unreleased/1677.security.md b/changelog.d/unreleased/1677.security.md new file mode 100644 index 0000000000..1552fa2426 --- /dev/null +++ b/changelog.d/unreleased/1677.security.md @@ -0,0 +1,16 @@ +--- +category: security +issues: + - 1677 +affected: + - .github/CODEOWNERS + - CONTRIBUTING.md +--- + +## English + +- **Release-critical paths now have CODEOWNERS coverage (#1677)** - workflow, installer, package project, and NuGet configuration changes now route through repository-owner review once branch protection requires CODEOWNERS approval. + +## 日本語 + +- **リリース重要パスを CODEOWNERS でカバーしました (#1677)** - workflow、installer、package project、NuGet 設定の変更が、branch protection で CODEOWNERS 承認を必須化したときに repository owner review へ回るようになりました。 diff --git a/changelog.d/unreleased/1847.security.md b/changelog.d/unreleased/1847.security.md new file mode 100644 index 0000000000..805eaf3b16 --- /dev/null +++ b/changelog.d/unreleased/1847.security.md @@ -0,0 +1,16 @@ +--- +category: security +issues: + - 1847 +affected: + - .github/workflows/release.yml + - USER_GUIDE.md +--- + +## English + +- **Release checksum manifests are now GPG-signed (#1847)** - the release workflow now publishes `sha256sums.txt.asc` alongside `sha256sums.txt` and documents manual verification before trusting downloaded assets. + +## 日本語 + +- **リリース checksum manifest を GPG 署名するようになりました (#1847)** - release workflow が `sha256sums.txt` と一緒に `sha256sums.txt.asc` を公開し、ダウンロードした成果物を信頼する前の手動検証手順を文書化しました。