mirror of
https://github.com/basecamp/once-campfire.git
synced 2026-04-09 14:37:47 +09:00
ci: harden GitHub Actions workflows (#185)
* Add GitHub Actions audit job (actionlint + zizmor) to CI Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Configure dependabot for GitHub Actions, bundler, and Docker Batches all action updates into a single weekly PR. Adds cooldown periods to all ecosystems. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add local GitHub Actions linting (actionlint + zizmor) to bin/setup and bin/ci Install actionlint, shellcheck, and zizmor in bin/setup. Run both linters as CI steps in config/ci.rb alongside existing style checks. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Pin all GitHub Actions to SHA hashes Run pinact to pin action versions to specific commit SHAs, preventing supply chain attacks from tag mutation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix high severity zizmor findings - Suppress unpinned-images for redis service containers (digest pinning is nontrivial for service containers) - Move workflow-level permissions to job-level in publish-image.yml (build gets full set, manifest gets only what it needs) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix medium severity zizmor findings - Add persist-credentials: false to all checkout steps - Add permissions: {} at workflow level in ci.yml - Add job-level permissions (contents: read) to all CI jobs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix informational template-injection findings in publish-image.yml Move steps.meta.outputs.tags from inline ${{ }} expressions to env vars in both the manifest creation and cosign signing steps. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Update brakeman to 8.0.4 bin/brakeman uses --ensure-latest which fails if not on the newest version. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
57
.github/workflows/ci.yml
vendored
57
.github/workflows/ci.yml
vendored
@@ -6,16 +6,22 @@ on:
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
scan:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
uses: ruby/setup-ruby@319994f95fa847cf3fb3cd3dbe89f6dcde9f178f # v1.295.0
|
||||
with:
|
||||
ruby-version: .ruby-version
|
||||
bundler-cache: true
|
||||
@@ -25,12 +31,16 @@ jobs:
|
||||
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
uses: ruby/setup-ruby@319994f95fa847cf3fb3cd3dbe89f6dcde9f178f # v1.295.0
|
||||
with:
|
||||
ruby-version: .ruby-version
|
||||
bundler-cache: true
|
||||
@@ -38,11 +48,32 @@ jobs:
|
||||
- name: Lint code for consistent style
|
||||
run: bin/rubocop
|
||||
|
||||
lint-actions:
|
||||
name: GitHub Actions audit
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Run actionlint
|
||||
uses: rhysd/actionlint@393031adb9afb225ee52ae2ccd7a5af5525e03e8 # v1.7.11
|
||||
|
||||
- name: Run zizmor
|
||||
uses: zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2
|
||||
with:
|
||||
advanced-security: false
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
services:
|
||||
redis:
|
||||
image: redis
|
||||
image: redis # zizmor: ignore[unpinned-images] -- version tag is fine for service containers
|
||||
ports:
|
||||
- 6379:6379
|
||||
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
|
||||
@@ -51,10 +82,12 @@ jobs:
|
||||
run: sudo apt-get update && sudo apt-get install --no-install-recommends -y libsqlite3-0 libvips curl ffmpeg
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
uses: ruby/setup-ruby@319994f95fa847cf3fb3cd3dbe89f6dcde9f178f # v1.295.0
|
||||
env:
|
||||
REDIS_URL: redis://localhost:6379/0
|
||||
with:
|
||||
@@ -66,9 +99,11 @@ jobs:
|
||||
|
||||
test_system:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
services:
|
||||
redis:
|
||||
image: redis
|
||||
image: redis # zizmor: ignore[unpinned-images] -- version tag is fine for service containers
|
||||
ports:
|
||||
- 6379:6379
|
||||
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
|
||||
@@ -77,10 +112,12 @@ jobs:
|
||||
run: sudo apt-get update && sudo apt-get install --no-install-recommends -y libsqlite3-0 libvips curl ffmpeg
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
uses: ruby/setup-ruby@319994f95fa847cf3fb3cd3dbe89f6dcde9f178f # v1.295.0
|
||||
env:
|
||||
REDIS_URL: redis://localhost:6379/0
|
||||
with:
|
||||
|
||||
45
.github/workflows/publish-image.yml
vendored
45
.github/workflows/publish-image.yml
vendored
@@ -13,11 +13,7 @@ concurrency:
|
||||
group: publish-${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
permissions: {}
|
||||
|
||||
env:
|
||||
IMAGE_DESCRIPTION: Campfire is a web-based chat application with multiple rooms, direct messages, file attachments with previews, search, web push notifications, @mentions, and bot integrations. Single-tenant; production-ready image with web app, background jobs, caching, file serving, and SSL.
|
||||
@@ -27,6 +23,11 @@ jobs:
|
||||
build:
|
||||
name: Build and push image (${{ matrix.arch }})
|
||||
runs-on: ${{ matrix.runner }}
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
timeout-minutes: 45
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -43,13 +44,15 @@ jobs:
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5.0.0
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3.11.1
|
||||
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@v3.5.0
|
||||
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
@@ -66,7 +69,7 @@ jobs:
|
||||
|
||||
- name: Extract Docker metadata (tags, labels) with arch suffix
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5.8.0
|
||||
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
|
||||
with:
|
||||
images: ${{ steps.vars.outputs.canonical }}
|
||||
tags: |
|
||||
@@ -84,7 +87,7 @@ jobs:
|
||||
|
||||
- name: Build and push (${{ matrix.platform }})
|
||||
id: build
|
||||
uses: docker/build-push-action@v6.18.0
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile
|
||||
@@ -102,7 +105,7 @@ jobs:
|
||||
|
||||
- name: Attest image provenance (per-arch)
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: actions/attest-build-provenance@v3.0.0
|
||||
uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3.0.0
|
||||
with:
|
||||
subject-name: ${{ steps.vars.outputs.canonical }}
|
||||
subject-digest: ${{ steps.build.outputs.digest }}
|
||||
@@ -113,16 +116,20 @@ jobs:
|
||||
needs: build
|
||||
if: github.event_name != 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
timeout-minutes: 20
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
steps:
|
||||
- name: Set up Docker Buildx (for imagetools)
|
||||
uses: docker/setup-buildx-action@v3.11.1
|
||||
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@v3.5.0
|
||||
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
@@ -139,7 +146,7 @@ jobs:
|
||||
|
||||
- name: Compute base tags (no suffix)
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5.8.0
|
||||
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
|
||||
with:
|
||||
images: ${{ steps.vars.outputs.canonical }}
|
||||
tags: |
|
||||
@@ -157,9 +164,11 @@ jobs:
|
||||
|
||||
- name: Create multi-arch manifests
|
||||
shell: bash
|
||||
env:
|
||||
TAGS: ${{ steps.meta.outputs.tags }}
|
||||
run: |
|
||||
set -eu
|
||||
tags="${{ steps.meta.outputs.tags }}"
|
||||
tags="$TAGS"
|
||||
echo "Creating manifests for tags:"
|
||||
printf '%s\n' "$tags"
|
||||
while IFS= read -r tag; do
|
||||
@@ -185,13 +194,15 @@ jobs:
|
||||
done <<< "$tags"
|
||||
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@v3.9.2
|
||||
uses: sigstore/cosign-installer@d58896d6a1865668819e1d91763c7751a165e159 # v3.9.2
|
||||
|
||||
- name: Cosign sign all tags (keyless OIDC)
|
||||
shell: bash
|
||||
env:
|
||||
TAGS: ${{ steps.meta.outputs.tags }}
|
||||
run: |
|
||||
set -eu
|
||||
tags="${{ steps.meta.outputs.tags }}"
|
||||
tags="$TAGS"
|
||||
printf '%s\n' "$tags"
|
||||
while IFS= read -r tag; do
|
||||
[ -z "$tag" ] && continue
|
||||
|
||||
Reference in New Issue
Block a user