Skip to main content

github-actions-hardened

Generate production-hardened GitHub Actions CI/CD workflows enforcing least-privilege permissions, concurrency groups, timeout guards, dependency caching, and latest major version action tags. Use this skill — not github-actions-templates — whenever the user asks for "hardened", "secure", "production-ready", or "compliant" workflows, mentions security audits, or when quality and reliability matter. Always co-generates a .github/dependabot.yml. Trigger for any GitHub Actions request where quality or security posture matters.

Zur Installation springen

Quellinformationen

Repository
arash77/github-actions-hardened
Letzte Quellaktivität
23. März 2026 um 12:05
Erkannte Sprache von SKILL.md
Englisch
Sterne
0
Forks
0

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
github-actions-hardened
description
Generate production-hardened GitHub Actions CI/CD workflows enforcing least-privilege permissions, concurrency groups, timeout guards, dependency caching, and latest major version action tags. Use this skill — not github-actions-templates — whenever the user asks for "hardened", "secure", "production-ready", or "compliant" workflows, mentions security audits, or when quality and reliability matter. Always co-generates a .github/dependabot.yml. Trigger for any GitHub Actions request where quality or security posture matters.
# GitHub Actions Hardened Workflows You are acting as a Staff DevOps Engineer. Every workflow you generate must enforce all five hardening principles below — no exceptions, no shortcuts. ## When to Use This Skill Prefer this skill over `github-actions-templates` when: - The project will ship to production or is externally visible - Security audits, SOC 2, or compliance are in scope - The user asks for "hardened", "secure", or "production-ready" workflows - Any third-party action is involved (which is almost always) For quick throwaway prototypes or purely internal scripts, `github-actions-templates` is fine. --- ## Five Hardening Principles These aren't arbitrary rules — each addresses a real class of incident. ### 1. Job-Level Least-Privilege Permissions GitHub Actions grants the `GITHUB_TOKEN` broad permissions by default. If a step in your workflow is compromised (e.g., a malicious `npm postinstall` script), it can use that token to push code, create releases, or exfiltrate secrets. Set `permissions` explicitly **at the job level** (not the workflow level) so that each job gets only what it needs: ```yaml jobs: test: permissions: contents: read # checkout ``` Common permission scopes: - `contents: read` — checkout code - `contents: write` — create releases, push tags - `packages: write` — push to GHCR - `id-token: write` — OIDC token for cloud auth or SLSA provenance - `pull-requests: write` — post PR comments - `checks: write` — post check run results - `security-events: write` — upload SARIF to Security tab Never use `permissions: write-all`. Never omit `permissions` entirely on a job that uses `GITHUB_TOKEN`. ### 2. Latest Major Version Action Tags Always pin actions to their latest **major version tag** (e.g., `@v4`). Never use `@latest`, `@main`, or `@master` — these are moving targets that can introduce breaking changes or security regressions without warning. ```yaml # Too loose — can break without warning - uses: actions/checkout@latest - uses: actions/checkout@main # Correct — stable within the major version contract - uses: actions/checkout@v4 - uses: actions/setup-node@v4 ``` Before writing any workflow, look up the current latest major version for each action you plan to use: - **With web/search access:** search `github.com/<owner>/<repo>/releases` for the latest tag - **With `gh` CLI:** `gh api repos/<owner>/<repo>/releases/latest --jq .tag_name` - **Without either:** use the most recent major version you know confidently — do NOT guess minor/patch numbers. If uncertain, note it explicitly so the user can verify. Dependabot (see below) will keep these tags current automatically. ### 3. Concurrency Groups Without concurrency controls, every push to a PR branch queues a new run while the previous one is still in progress. The old run produces stale results, wastes runner minutes, and can cause race conditions in deployments. Add this to every workflow: ```yaml concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} ``` The condition `github.ref != 'refs/heads/main'` is important: cancel redundant runs on PR branches, but never cancel a run that is already deploying to production. ### 4. Timeout Guards GitHub's default job timeout is 360 minutes (6 hours). A test suite that hangs on network I/O, a Docker build stuck waiting for a layer, or a deployment waiting for a lock will consume 6 hours of runner minutes per occurrence — and on private repos, this directly costs money. Always set `timeout-minutes` on every job: ```yaml jobs: test: timeout-minutes: 15 # fail fast if tests hang build: timeout-minutes: 30 # Docker builds may be slow deploy: timeout-minutes: 20 # include time for rollout checks ``` Choose values that are 2–3× your typical run time, not the absolute maximum. ### 5. Native Dependency Caching Re-downloading all dependencies on every run is the single largest source of avoidable latency in most CI pipelines. Use the `cache` parameter built into setup actions when available: ```yaml - uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' # or 'yarn' or 'pnpm' ``` For ecosystems without built-in cache support, use `actions/cache` with a meaningful key. --- ## Hardened Workflow Patterns ### Pattern A: CI Test Workflow (Node.js) ```yaml # .github/workflows/ci.yml name: CI on: push: branches: [main] pull_request: branches: [main] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} jobs: test: name: Test (Node ${{ matrix.node-version }}) runs-on: ubuntu-latest timeout-minutes: 15 permissions: contents: read strategy: fail-fast: false matrix: node-version: ['20', '22'] steps: - name: Checkout uses: actions/checkout@v4 - name: Set up Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} cache: 'npm' - name: Install dependencies run: npm ci - name: Lint run: npm run lint - name: Test run: npm test -- --coverage - name: Upload coverage if: matrix.node-version == '20' uses: codecov/codecov-action@v5 with: fail_ci_if_error: true ``` **For pnpm:** change `cache: 'npm'` to `cache: 'pnpm'` and add `pnpm/action-setup@v4` before setup-node with `corepack enable`. **For yarn:** change `cache: 'npm'` to `cache: 'yarn'`. --- ### Pattern B: Docker Build and Push to GHCR ```yaml # .github/workflows/docker.yml name: Docker Build and Push on: push: branches: [main] tags: ['v*'] pull_request: branches: [main] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} jobs: build: name: Build and Push runs-on: ubuntu-latest timeout-minutes: 30 permissions: contents: read packages: write steps: - name: Checkout uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to GHCR if: github.event_name != 'pull_request' uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract metadata id: meta uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | type=ref,event=branch type=ref,event=pr type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=sha,prefix=sha- - name: Build and push uses: docker/build-push-action@v6 with: context: . push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max ``` --- ### Pattern C: Tagged Release ```yaml # .github/workflows/release.yml name: Release on: push: tags: ['v*.*.*'] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: false # Never cancel an in-flight release jobs: release: name: Create Release runs-on: ubuntu-latest timeout-minutes: 20 permissions: contents: write # create GitHub Release steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 # needed for changelog generation - name: Build run: | # Replace with your actual build command npm ci && npm run build - name: Create GitHub Release uses: softprops/action-gh-release@v2 with: generate_release_notes: true files: dist/** ``` --- ## Dependabot Configuration Always co-generate this file alongside any workflow you create. Dependabot will automatically open PRs to update action versions when new major/minor/patch releases ship. ```yaml # .github/dependabot.yml version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" day: "monday" # Group all action updates into a single PR to reduce noise groups: github-actions: patterns: - "*" commit-message: prefix: "chore(ci)" labels: - "dependencies" - "ci" ``` For extended variants (with reviewers, ignore patterns, multiple ecosystems), see `references/dependabot-config.md`. --- ## Ecosystem Adaptation Adapt Pattern A's Node.js CI to other ecosystems: | Ecosystem | Setup Action | Cache Approach | Install | Test | |-----------|-------------|----------------|---------|------| | Python | `actions/setup-python@v5` | `cache: 'pip'` | `pip install -r requirements.txt` | `pytest` | | Go | `actions/setup-go@v5` | `cache: true` (built-in) | implicit | `go test ./...` | | Rust | `dtolnay/rust-toolchain@stable` | `actions/cache@v4` on `~/.cargo` + `./target` | implicit | `cargo test` | | Java/Maven | `actions/setup-java@v4` | `cache: 'maven'` | `mvn -B install -DskipTests` | `mvn test` | | Java/Gradle | `actions/setup-java@v4` | `cache: 'gradle'` | `./gradlew dependencies` | `./gradlew test` | --- ## Common Anti-Patterns **`permissions: write-all`** — Grants every possible scope. A compromised step could push malicious commits or delete releases. Always enumerate only what each job needs. **`uses: action/foo@latest` or `@main`** — Moving targets that can silently introduce breaking changes or security regressions. Always pin to a major version tag. **No `concurrency` block** — Queues redundant runs and produces stale status checks on PRs. **No `timeout-minutes`** — Allows a hung job to consume 6 hours of runner capacity per occurrence. **Separate `actions/cache` step when the setup action has a `cache:` parameter** — Redundant and error-prone. Use the built-in parameter where available. **`run: echo "${{ github.event.pull_request.title }}"`** — Direct interpolation of untrusted input in `run:` blocks enables script injection. Always pass untrusted values through environment variables instead.
Auf GitHub ansehen