diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 02f2d89..cb2410d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -92,7 +92,8 @@ jobs: --cov-report=xml - name: Upload coverage if: matrix.python-version == '3.12' - uses: codecov/codecov-action@v6 + # codecov/codecov-action pinned to full commit SHA — v6.0.0 + uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 with: files: coverage.xml fail_ci_if_error: false diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 613206e..56d6a18 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -36,10 +36,12 @@ jobs: VERSION="${GITHUB_REF_NAME#v}" echo "version=$VERSION" >> "$GITHUB_OUTPUT" - - uses: docker/setup-buildx-action@v4 + # docker/setup-buildx-action pinned to full commit SHA — v4.0.0 + - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd - name: Log in to GHCR - uses: docker/login-action@v4 + # docker/login-action pinned to full commit SHA — v4.1.0 + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 with: registry: ghcr.io username: ${{ github.actor }} @@ -47,13 +49,15 @@ jobs: - name: Log in to Docker Hub if: vars.DOCKERHUB_USERNAME != '' - uses: docker/login-action@v4 + # docker/login-action pinned to full commit SHA — v4.1.0 + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 with: username: ${{ vars.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push - uses: docker/build-push-action@v7 + # docker/build-push-action pinned to full commit SHA — v7.1.0 + uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f with: context: . push: true diff --git a/.github/workflows/docker-smoke.yml b/.github/workflows/docker-smoke.yml index e0039ec..cacf96b 100644 --- a/.github/workflows/docker-smoke.yml +++ b/.github/workflows/docker-smoke.yml @@ -48,10 +48,12 @@ jobs: - uses: actions/checkout@v6 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + # docker/setup-buildx-action pinned to full commit SHA — v3.12.0 + uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f - name: Build image (no push) - uses: docker/build-push-action@v6 + # docker/build-push-action pinned to full commit SHA — v6.19.2 + uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 with: context: . push: false diff --git a/CHANGELOG.md b/CHANGELOG.md index 884cc42..68614d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Security +- **Third-party GitHub Actions pinned to full commit SHAs** (instead of floating `@vN` major-version tags). A moving major-version tag on a third-party action is an unreviewed supply-chain channel — the upstream owner can move the tag to any commit at any time, including after a repo compromise. Pinning to the commit SHA freezes the code under review. Affected references in `.github/workflows/`: + - `codecov/codecov-action@v6` → `@57e3a136…` (v6.0.0) in `ci.yml` + - `docker/setup-buildx-action@v3` → `@8d2750c6…` (v3.12.0) in `docker-smoke.yml` + - `docker/build-push-action@v6` → `@10e90e36…` (v6.19.2) in `docker-smoke.yml` + - `docker/setup-buildx-action@v4` → `@4d04d5d9…` (v4.0.0) in `docker-publish.yml` + - `docker/login-action@v4` → `@4907a6dd…` (v4.1.0) in `docker-publish.yml` (two invocations) + - `docker/build-push-action@v7` → `@bcafcacb…` (v7.1.0) in `docker-publish.yml` + + Each pinned line carries a trailing `# pinned to full commit SHA — ` comment so the human-readable version stays visible and Dependabot's `github-actions` ecosystem can still propose updates. First-party `actions/*` (checkout, setup-python) remain tag-pinned — GitHub's own actions are in a separate trust boundary. Closes medium-severity gap #5 from the 2026-04-21 contribution-safety audit (`audit-contribution-safety-2026-04-21`). + ### Added - **`.github/PULL_REQUEST_TEMPLATE.md`** — standard template for incoming PRs. Sections: linked-issue (engagement-first), summary, scope, AI-assistance disclosure checkboxes, `## QA` block (prerequisites + MCP-tool-driven manual tests), and a contributor checklist (CHANGELOG entry, README/data-dictionary updates, no-secrets affirmation, local CI, CLA signature). Mirrors the shape already expected by `CLAUDE.md` and `CONTRIBUTING.md` §"Pull request guidelines" — the template just makes it discoverable at PR-authoring time instead of requiring contributors to find the conventions themselves. - **README `## Contributing` section** — one paragraph between "How it's built" and "License" pointing at `CONTRIBUTING.md` for the PR flow and `SECURITY.md` for vulnerability reports. Previously neither was linked from the README, so contributors had to discover them by file listing.