diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 428430d..4098a00 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2025 OpenCHAMI a Series of LF Projects, LLC +# SPDX-License-Identifier: MIT + name: CI on: @@ -9,6 +12,6 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6.0.3 - name: actionlint - uses: raven-actions/actionlint@v2.0.1 + uses: raven-actions/actionlint@v2.1.2 diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 0000000..1eba6c8 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,55 @@ +# SPDX-FileCopyrightText: 2025 OpenCHAMI a Series of LF Projects, LLC +# SPDX-License-Identifier: MIT + +name: dependency-review + +# Reusable workflow that runs actions/dependency-review-action on a PR. +# Compares the PR's dependency changes against GitHub's vulnerability +# database and license policies. Fails the check when a new dep with a +# severity at or above `fail-on-severity` is introduced. +# +# Caller MUST trigger this on `pull_request` — the action compares +# head vs base and has no meaning outside that event. + +on: + workflow_call: + inputs: + fail-on-severity: + description: 'Minimum severity that fails the check (low | moderate | high | critical)' + required: false + type: string + default: 'high' + allow-licenses: + description: 'Comma-separated SPDX license identifiers to allow (default: empty = no license gate)' + required: false + type: string + default: '' + deny-licenses: + description: 'Comma-separated SPDX license identifiers to deny (default: empty = no license gate)' + required: false + type: string + default: '' + comment-summary-in-pr: + description: 'Post a summary comment on the PR (always | on-failure | never)' + required: false + type: string + default: 'on-failure' + +permissions: + contents: read + pull-requests: write # for comment-summary-in-pr + +jobs: + dependency-review: + name: dependency-review + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + - uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0 + with: + fail-on-severity: ${{ inputs.fail-on-severity }} + allow-licenses: ${{ inputs.allow-licenses }} + deny-licenses: ${{ inputs.deny-licenses }} + comment-summary-in-pr: ${{ inputs.comment-summary-in-pr }} diff --git a/.github/workflows/docker-build-release.yml b/.github/workflows/docker-build-release.yml index 59b474a..3913ee0 100644 --- a/.github/workflows/docker-build-release.yml +++ b/.github/workflows/docker-build-release.yml @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2025 OpenCHAMI a Series of LF Projects, LLC +# SPDX-License-Identifier: MIT + name: Build image run-name: Dockeer image build for ${{ github.event.push.ref }} @@ -56,12 +59,12 @@ jobs: runs-on: ubuntu-latest steps: - name: checkout repository - uses: actions/checkout@v4.2.2 + uses: actions/checkout@v6.0.3 with: fetch-depth: 0 - name: Setup golang - uses: actions/setup-go@v5.5.0 + uses: actions/setup-go@v6.4.0 with: go-version-file: go.mod @@ -84,10 +87,10 @@ jobs: done - name: Set up Docker Buildkit - uses: docker/setup-buildx-action@v3.11.1 + uses: docker/setup-buildx-action@v4.1.0 - name: Cache Docker layers - uses: actions/cache@v4.2.3 + uses: actions/cache@v5.0.5 with: path: /tmp/.buildx-cache key: ${{ runner.os }}-buildx-${{ github.sha }} @@ -96,7 +99,7 @@ jobs: - name: Fill image meta id: meta - uses: docker/metadata-action@v5.8.0 + uses: docker/metadata-action@v6.1.0 with: images: ${{ inputs.registry-name }} tags: | @@ -112,7 +115,7 @@ jobs: org.opencontainers.image.description=${{ inputs.image-description }} - name: Auth to registry - uses: docker/login-action@v3.5.0 + uses: docker/login-action@v4.2.0 with: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} @@ -120,7 +123,7 @@ jobs: - name: Build and push image id: docker_build - uses: docker/build-push-action@v6.18.0 + uses: docker/build-push-action@v7.2.0 with: push: true file: ${{ inputs.docker-file }} @@ -156,7 +159,7 @@ jobs: with: input_string: ${{ github.event.ref }} version_extractor_regex: 'refs/tags/v(.*)$' - - uses: ncipollo/release-action@v1.18.0 + - uses: ncipollo/release-action@v1.21.0 with: # by default this will use the tag push tag as the tag and name # if we want to trigger tagging from the workflow, "tag" and "commit" diff --git a/.github/workflows/go-build-release.yml b/.github/workflows/go-build-release.yml index 5e66f32..451fece 100644 --- a/.github/workflows/go-build-release.yml +++ b/.github/workflows/go-build-release.yml @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2025 OpenCHAMI a Series of LF Projects, LLC +# SPDX-License-Identifier: MIT + name: GoReleaser run-name: GoReleaser ${{ (inputs.snapshot == 'true' || (inputs.snapshot != 'false' && !startsWith(github.ref, 'refs/tags/v'))) && 'Snapshot' || 'Release' }} @@ -76,22 +79,22 @@ jobs: runs-on: ubuntu-latest steps: - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@v6.4.0 with: go-version: ${{ inputs.go-version }} - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@v4.1.0 - name: Docker Login - uses: docker/login-action@v3 + uses: docker/login-action@v4.2.0 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6.0.3 with: fetch-tags: ${{ inputs.fetch-tags }} fetch-depth: ${{ inputs.fetch-depth }} @@ -151,7 +154,7 @@ jobs: - name: GoReleaser ${{ inputs.goreleaser-args }} ${{ steps.snapshot_flags.outputs.flags }} id: goreleaser - uses: goreleaser/goreleaser-action@v6 + uses: goreleaser/goreleaser-action@v7.2.2 env: GITHUB_TOKEN: ${{ github.token }} with: @@ -174,13 +177,13 @@ jobs: echo "digest=${digest}" >> "$GITHUB_OUTPUT" - name: Attest Binaries - uses: actions/attest-build-provenance@v1 + uses: actions/attest-build-provenance@v4.1.0 with: subject-path: ${{ inputs.attestation-binary-path }} - name: Generate build provenance for container if: ${{ !contains(steps.snapshot_flags.outputs.flags, '--snapshot') && steps.process_goreleaser_output.outputs.digest != '' && steps.process_goreleaser_output.outputs.digest != 'undefined' }} - uses: actions/attest-build-provenance@v1 + uses: actions/attest-build-provenance@v4.1.0 with: subject-name: ${{ inputs.registry-name }} subject-digest: ${{ steps.process_goreleaser_output.outputs.digest }} diff --git a/.github/workflows/govulncheck.yml b/.github/workflows/govulncheck.yml new file mode 100644 index 0000000..dbb3b8c --- /dev/null +++ b/.github/workflows/govulncheck.yml @@ -0,0 +1,42 @@ +# SPDX-FileCopyrightText: 2025 OpenCHAMI a Series of LF Projects, LLC +# SPDX-License-Identifier: MIT + +name: govulncheck + +# Reusable workflow that runs the Go team's vulnerability scanner against +# the caller's module. Detects known CVEs in the import graph (including +# transitive deps) and reports them as workflow annotations. +# +# Pair with the actionlint/zizmor lint workflow for a "static security +# baseline" wave; pair with dependency-review for PR-time gating. + +on: + workflow_call: + inputs: + go-version: + description: 'Go version to scan against (default: read from go.mod)' + required: false + type: string + default: '' + go-package: + description: 'Package pattern to scan (default: ./... — entire module)' + required: false + type: string + default: './...' + +permissions: + contents: read + +jobs: + govulncheck: + name: govulncheck + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + - uses: golang/govulncheck-action@b625fbe08f3bccbe446d94fbf87fcc875a4f50ee # v1.0.4 + with: + go-version-file: ${{ inputs.go-version == '' && 'go.mod' || '' }} + go-version-input: ${{ inputs.go-version }} + go-package: ${{ inputs.go-package }} diff --git a/.github/workflows/lint-workflows.yml b/.github/workflows/lint-workflows.yml new file mode 100644 index 0000000..44353e6 --- /dev/null +++ b/.github/workflows/lint-workflows.yml @@ -0,0 +1,38 @@ +# SPDX-FileCopyrightText: 2025 OpenCHAMI a Series of LF Projects, LLC +# SPDX-License-Identifier: MIT + +name: lint-workflows + +# Reusable workflow that lints GitHub Actions workflow files in the caller repo. +# Runs actionlint (syntax / shellcheck / action-version sanity) and zizmor +# (security-focused static analysis with SARIF upload to GHAS). + +on: + workflow_call: + +permissions: + contents: read + +jobs: + actionlint: + name: actionlint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + - uses: raven-actions/actionlint@01fce4f43a235f1d04a3d6b1b202aa0eb09cdfd9 # v2.1.2 + with: + flags: -color + + zizmor: + name: zizmor + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write # for SARIF upload to GHAS + steps: + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + - uses: zizmorcore/zizmor-action@5f14fd08f7cf1cb1609c1e344975f152c7ee938d # v0.5.6 diff --git a/.github/workflows/trivy-image-scan.yml b/.github/workflows/trivy-image-scan.yml new file mode 100644 index 0000000..6d5b143 --- /dev/null +++ b/.github/workflows/trivy-image-scan.yml @@ -0,0 +1,66 @@ +# SPDX-FileCopyrightText: 2025 OpenCHAMI a Series of LF Projects, LLC +# SPDX-License-Identifier: MIT + +name: trivy-image-scan + +# Reusable workflow that scans an already-built and -pushed container image +# with Trivy. Emits SARIF and uploads it to GitHub Advanced Security so +# findings land in the caller's Security tab. +# +# Expects the image to already exist (either in a registry or as a local +# `docker save` tar at `image-path`). To run as a build-time gate inside +# docker-build-release, call this workflow after the build step with the +# fully-qualified `/@` ref. + +on: + workflow_call: + inputs: + image-ref: + description: 'Image to scan (e.g., ghcr.io/openchami/foo@sha256:...)' + required: true + type: string + severity: + description: 'Comma-separated severities to report (default: HIGH,CRITICAL)' + required: false + type: string + default: 'HIGH,CRITICAL' + ignore-unfixed: + description: 'Skip vulnerabilities with no fix available' + required: false + type: boolean + default: false + exit-code: + description: 'Exit code when findings are present (0 = report only, 1 = fail)' + required: false + type: string + default: '1' + +permissions: + contents: read + security-events: write # for SARIF upload to GHAS + +jobs: + trivy: + name: trivy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + + - name: Run Trivy + uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 + with: + image-ref: ${{ inputs.image-ref }} + format: sarif + output: trivy-results.sarif + severity: ${{ inputs.severity }} + ignore-unfixed: ${{ inputs.ignore-unfixed }} + exit-code: ${{ inputs.exit-code }} + + - name: Upload SARIF to GHAS + if: always() # upload findings even when scan fails the job + uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 + with: + sarif_file: trivy-results.sarif + category: trivy-image-scan diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..7f4fd0b --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,25 @@ +# SPDX-FileCopyrightText: 2025 OpenCHAMI a Series of LF Projects, LLC +# SPDX-License-Identifier: MIT + +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: check-added-large-files + - id: check-merge-conflict + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace + - id: mixed-line-ending + args: [--fix=lf] + + - repo: https://github.com/rhysd/actionlint + rev: v1.7.8 + hooks: + - id: actionlint + files: ^\.github/workflows/.*\.ya?ml$ + + - repo: https://github.com/fsfe/reuse-tool + rev: v5.1.1 + hooks: + - id: reuse \ No newline at end of file diff --git a/.reuse/dep5 b/.reuse/dep5 new file mode 100644 index 0000000..2067b04 --- /dev/null +++ b/.reuse/dep5 @@ -0,0 +1,7 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: OpenCHAMI GitHub Actions +Source: https://github.com/OpenCHAMI/github-actions + +Files: bin/act subkey.asc subkey.b64 +Copyright: 2025 OpenCHAMI a Series of LF Projects, LLC +License: MIT diff --git a/LICENSES/MIT.txt b/LICENSES/MIT.txt new file mode 100644 index 0000000..4e101cc --- /dev/null +++ b/LICENSES/MIT.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 openchami + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 3379d99..a398d65 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,8 @@ + + # GitHub Actions Monorepo for `OpenCHAMI` Reusable GitHub Actions for CI/CD. @@ -7,6 +12,11 @@ Reusable GitHub Actions for CI/CD. - `actions/gpg-ephemeral-key`: Ephemeral key generation for RPM/GPG signing - `actions/sign-rpm`: RPM signing with ephemeral keys - `.github/workflows/go-build-release.yml`: Reusable workflow for GoReleaser builds +- `.github/workflows/docker-build-release.yml`: Reusable workflow for multi-arch container image builds +- `.github/workflows/lint-workflows.yml`: Reusable workflow that lints workflow files (actionlint + zizmor) +- `.github/workflows/govulncheck.yml`: Reusable workflow that scans Go modules for known CVEs +- `.github/workflows/dependency-review.yml`: Reusable workflow that gates PRs introducing CVE-flagged deps +- `.github/workflows/trivy-image-scan.yml`: Reusable workflow that scans built container images for CVEs ## Versioning & Usage @@ -62,6 +72,79 @@ jobs: See the [workflow](.github/workflows/go-build-release.yml) for additional input parameters. +### lint-workflows (Reusable Workflow) +Lints the caller repo's GitHub Actions workflow files. + +- **actionlint** — syntax validation, shellcheck on `run:` steps, deprecated-action checks. +- **zizmor** — security-focused static analysis (script injection, excessive permissions, unpinned third-party actions). Uploads SARIF findings to the caller's GitHub Advanced Security tab. + +**Usage:** +```yaml +name: Lint Workflows +on: + pull_request: + push: + branches: [main] + +jobs: + lint: + uses: OpenCHAMI/github-actions/.github/workflows/lint-workflows.yml@v3.4 +``` + +### govulncheck (Reusable Workflow) +Runs the Go team's vulnerability scanner against the caller's module. Detects known CVEs in the import graph (direct and transitive). Reads the Go version from the caller's `go.mod` by default. + +**Usage:** +```yaml +name: govulncheck +on: + pull_request: + push: + branches: [main] + schedule: + - cron: '0 6 * * 1' # weekly catch-up for newly-disclosed CVEs + +jobs: + govulncheck: + uses: OpenCHAMI/github-actions/.github/workflows/govulncheck.yml@v3.4 +``` + +### dependency-review (Reusable Workflow) +PR gate that compares the dependency changes between the PR head and base against GitHub's vulnerability database. Fails the check when the PR introduces a dependency at or above `fail-on-severity` (default: `high`). Optionally enforces license policy and posts a summary comment on the PR. + +**Usage:** +```yaml +name: Dependency Review +on: + pull_request: + +jobs: + dependency-review: + uses: OpenCHAMI/github-actions/.github/workflows/dependency-review.yml@v3.4 + # Optional overrides: + # with: + # fail-on-severity: moderate + # deny-licenses: GPL-3.0,AGPL-3.0 +``` + +### trivy-image-scan (Reusable Workflow) +Scans an already-pushed container image with Trivy and uploads SARIF findings to GitHub Advanced Security. Designed to chain after `docker-build-release` with a digest-pinned image reference. + +**Usage:** +```yaml +jobs: + build: + uses: OpenCHAMI/github-actions/.github/workflows/docker-build-release.yml@v3.4 + with: + registry-name: ghcr.io/openchami/foo + + scan: + needs: build + uses: OpenCHAMI/github-actions/.github/workflows/trivy-image-scan.yml@v3.4 + with: + image-ref: ghcr.io/openchami/foo:${{ github.sha }} +``` + ### gpg-ephemeral-key Generates a short‑lived RSA key (default 3072‑bit, 1 day) using an isolated `GNUPGHOME`, signs it with a repo‑scoped subkey you provide, and outputs: - `ephemeral-fingerprint` diff --git a/actions/gpg-ephemeral-key/README.md b/actions/gpg-ephemeral-key/README.md index ac8b0a1..69dc2a5 100644 --- a/actions/gpg-ephemeral-key/README.md +++ b/actions/gpg-ephemeral-key/README.md @@ -1,3 +1,8 @@ + + # 🛡️ GPG Ephemeral Key Generator This GitHub composite action generates a new ephemeral GPG key on every build, signs it using a repo‑scoped subkey, and exports the fingerprint and public key. It’s designed for use in CI pipelines where artifacts need secure signing without long‑lived keys in GitHub Actions. @@ -97,4 +102,4 @@ jobs: ## 📝 License -MIT \ No newline at end of file +MIT diff --git a/actions/gpg-ephemeral-key/action.yml b/actions/gpg-ephemeral-key/action.yml index 0d6df0b..87b3507 100644 --- a/actions/gpg-ephemeral-key/action.yml +++ b/actions/gpg-ephemeral-key/action.yml @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2025 OpenCHAMI a Series of LF Projects, LLC +# SPDX-License-Identifier: MIT + name: 'Generate and Sign Ephemeral GPG Key' author: 'OpenCHAMI' branding: diff --git a/actions/sign-rpm/README.md b/actions/sign-rpm/README.md index 2fbc374..f50ca24 100644 --- a/actions/sign-rpm/README.md +++ b/actions/sign-rpm/README.md @@ -1,3 +1,8 @@ + + # 📦 RPM Signing Action Signs an RPM file using a GPG key fingerprint (typically an ephemeral key produced by the `gpg-ephemeral-key` action). Designed to pair with ephemeral, short‑lived keys to reduce long‑term key exposure in CI. diff --git a/actions/sign-rpm/action.yml b/actions/sign-rpm/action.yml index 46208d5..72381d8 100644 --- a/actions/sign-rpm/action.yml +++ b/actions/sign-rpm/action.yml @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2025 OpenCHAMI a Series of LF Projects, LLC +# SPDX-License-Identifier: MIT + name: 'Sign RPM Package' author: 'OpenCHAMI' branding: