-
Notifications
You must be signed in to change notification settings - Fork 2
ci: Harden GitHub Actions workflows with security and performance fixes #313
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
cac7180
ci: harden GitHub Actions workflows with security and performance fixes
yacosta738 0000eaf
docs(ci): add GitHub Actions CI/CD best practices skill
yacosta738 5f25ec5
ci: fix pnpm cache configuration in Node workflows
yacosta738 abc5630
ci: address follow-up workflow review findings
yacosta738 866c285
ci: resolve remaining workflow review follow-ups
yacosta738 d906c66
Merge branch 'main' into ci/common-tools
yacosta738 d14c50b
test: isolate git operations temp repos from hook env
yacosta738 ac4d5c8
docs(ci): add actionlint to local workflow validation
yacosta738 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,225 @@ | ||
| --- | ||
| name: github-actions | ||
| description: > | ||
| GitHub Actions CI/CD best practices for workflow design, security hardening, and pipeline optimization. | ||
| Trigger: When creating, reviewing, or auditing GitHub Actions workflows (.github/workflows/*.{yml,yaml}), | ||
| fixing CI/CD issues, or hardening pipeline security. | ||
| license: Apache-2.0 | ||
| metadata: | ||
| author: "@dallay" | ||
| version: "1.0" | ||
| --- | ||
|
|
||
| # GitHub Actions CI/CD Skill | ||
|
|
||
| Expert guidance for designing, securing, and optimizing GitHub Actions CI/CD pipelines. | ||
|
|
||
| ## When to Use | ||
|
|
||
| - Creating or modifying `.github/workflows/*.{yml,yaml}` files | ||
| - Auditing existing CI/CD pipelines for security, performance, or reliability issues | ||
| - Fixing workflow triggers, permissions, or caching problems | ||
| - Setting up deployment pipelines (staging, production, rollback) | ||
| - Integrating security scanning (SAST, SCA, secret scanning) into CI | ||
| - Optimizing workflow execution time (caching, matrix, parallelism) | ||
|
|
||
| ## Critical Patterns | ||
|
|
||
| ### Security (Non-Negotiable) | ||
|
|
||
| - **Pin actions to full SHA**: Always use `uses: owner/action@<40-char-sha> # vX.Y.Z`. Tags | ||
| (`@v4`, `@main`) are mutable and vulnerable to supply chain attacks. Add version as comment for | ||
| readability. | ||
| - **Least-privilege permissions**: Set `permissions: {}` or `contents: read` at workflow level. | ||
| Override per-job only when writes are needed. Never leave permissions at default (broad write). | ||
| - **Secrets via `secrets.*` only**: Never hardcode sensitive data. Use environment-specific secrets | ||
| for deployment workflows with manual approval gates. | ||
| - **OIDC over static credentials**: For cloud auth (AWS, Azure, GCP), prefer OIDC short-lived | ||
| tokens over long-lived access keys stored as secrets. | ||
| - **Audit third-party actions**: Prefer `actions/` org. Review source for external actions. Enable | ||
| Dependabot for action version updates. | ||
| - **`pull_request_target` caution**: This trigger runs with write permissions against the base repo. | ||
| Never checkout untrusted PR code and execute it in this context. | ||
|
|
||
| ### Workflow Structure | ||
|
|
||
| - **Descriptive names**: Workflow `name` and step `name` must be clear for log readability. | ||
| - **Concurrency control**: Use `concurrency` with `cancel-in-progress: true` to prevent duplicate | ||
| runs and waste. | ||
| - **Conditional execution**: Use `if` conditions for branch-specific, actor-specific, or | ||
| event-specific logic. | ||
| - **Reusable workflows**: Extract common patterns into `workflow_call` workflows (prefix with `_` | ||
| for internal ones, e.g., `_publish.yml` or `_publish.yaml`). | ||
| - **Timeout**: Set `timeout-minutes` on long-running jobs to prevent hung runners. | ||
|
|
||
| ### Performance | ||
|
|
||
| - **Caching**: Use `actions/cache` (pinned SHA) with `hashFiles` keys for package managers and | ||
| build outputs. Use `restore-keys` for fallback. | ||
| - **Shallow clone**: Use `fetch-depth: 1` unless full history is needed (release tagging, blame). | ||
| - **Matrix parallelization**: Use `strategy.matrix` with `fail-fast: false` for comprehensive | ||
| multi-env testing. | ||
| - **Artifacts for inter-job data**: Use `actions/upload-artifact` / `actions/download-artifact` | ||
| (pinned SHAs) with appropriate `retention-days`. | ||
|
|
||
| ### Deployment | ||
|
|
||
| - **Environment protection**: Use GitHub `environment` with required reviewers, branch restrictions, | ||
| and deployment URLs. | ||
| - **Rollback strategy**: Keep previous artifacts. Implement automated rollback on health check | ||
| failure. | ||
| - **Version validation**: Verify version consistency across manifests before publishing. | ||
| - **Idempotent publishes**: Check if version already exists before publishing to registries. | ||
|
|
||
| ## Decision Tables | ||
|
|
||
| ### When to Use `fetch-depth: 0` (Full History) | ||
|
|
||
| | Use Case | `fetch-depth` | | ||
| |-----------------------------|---------------| | ||
| | Standard build/test | `1` | | ||
| | Release tagging/changelog | `0` | | ||
| | Commit message validation | `0` | | ||
| | CodeQL / deep analysis | `0` or omit | | ||
| | Dependency lockfile fix | `0` | | ||
|
|
||
| ### Choosing Deployment Strategy | ||
|
|
||
| | Risk Tolerance | Strategy | Rollback Speed | | ||
| |----------------|-----------------|----------------| | ||
| | Low | Blue/Green | Instant | | ||
| | Medium | Canary | Fast | | ||
| | High | Rolling Update | Moderate | | ||
| | Feature flags | Dark Launch | Instant | | ||
|
|
||
| ### Permission Mapping | ||
|
|
||
| | Workflow Need | Permission Required | | ||
| |----------------------------------|------------------------------| | ||
| | Read code only | `contents: read` | | ||
| | Push commits / create tags | `contents: write` | | ||
| | Comment on / update PRs | `pull-requests: write` | | ||
| | Upload SARIF security results | `security-events: write` | | ||
| | Deploy to GitHub Pages | `pages: write`, `id-token: write` | | ||
| | Publish to GitHub Packages | `packages: write` | | ||
| | Create/update issues | `issues: write` | | ||
| | Manage caches | `actions: write` | | ||
|
|
||
| ## Code Examples | ||
|
|
||
| ### Secure Workflow Skeleton | ||
|
|
||
| ```yaml | ||
| # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json | ||
| name: CI | ||
|
|
||
| on: | ||
| push: | ||
| branches: [main] | ||
| pull_request: | ||
| branches: [main] | ||
| workflow_dispatch: | ||
|
|
||
| concurrency: | ||
| group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| build: | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 30 | ||
| steps: | ||
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||
| with: | ||
| fetch-depth: 1 | ||
| # ... build steps | ||
| ``` | ||
|
|
||
| ### Effective Caching Pattern | ||
|
|
||
| ```yaml | ||
| - name: Cache dependencies | ||
| uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1 | ||
| with: | ||
| path: | | ||
| ~/.npm | ||
| ./node_modules | ||
| key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-node- | ||
| ``` | ||
|
|
||
| ### Environment-Protected Deployment | ||
|
|
||
| ```yaml | ||
| jobs: | ||
| deploy: | ||
| runs-on: ubuntu-latest | ||
| environment: | ||
| name: production | ||
| url: https://prod.example.com | ||
| permissions: | ||
| contents: read | ||
| id-token: write # For OIDC | ||
| steps: | ||
| - name: Configure cloud credentials (OIDC) | ||
| uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4 | ||
| with: | ||
| role-to-assume: arn:aws:iam::123456789:role/deploy | ||
| aws-region: us-east-1 | ||
| ``` | ||
|
|
||
| ## Audit Checklist | ||
|
|
||
| When reviewing workflows, check: | ||
|
|
||
| 1. **All `uses:` pinned to full SHA** with version comment | ||
| 2. **`permissions:` explicitly set** (workflow + job level) | ||
| 3. **`concurrency:` configured** for non-trivial workflows | ||
| 4. **`timeout-minutes:` set** on long-running jobs | ||
| 5. **Secrets accessed via `secrets.*`** only, never in logs | ||
| 6. **`fetch-depth: 1`** unless full history needed | ||
| 7. **Caching configured** for package manager dependencies | ||
| 8. **`if` conditions** for conditional steps (bot exclusions, branch filters) | ||
| 9. **Environment protection** for deployment jobs | ||
| 10. **No mutable action refs** (`@v4`, `@main`, `@latest`) | ||
|
|
||
| ## Troubleshooting Quick Reference | ||
|
|
||
| | Symptom | Likely Cause | Fix | | ||
| |--------------------------------|--------------------------------------|-----------------------------------------------| | ||
| | Workflow not triggering | Wrong `on` trigger or path filter | Verify branch/path/event config | | ||
| | `Resource not accessible` | Missing `permissions` | Add required permission at job level | | ||
| | Cache miss every run | Dynamic cache key | Use `hashFiles` for stable keys | | ||
| | Slow checkout | `fetch-depth: 0` on large repo | Use `fetch-depth: 1` | | ||
| | Flaky tests in CI | Race conditions, env differences | Use explicit waits, `services` for deps | | ||
| | `pull_request_target` exploit | Running untrusted code with perms | Never checkout PR head in `_target` trigger | | ||
|
|
||
| ## Commands | ||
|
|
||
| ```bash | ||
| # Validate workflow YAML syntax locally | ||
| npx yaml-lint .github/workflows/*.{yml,yaml} | ||
|
yacosta738 marked this conversation as resolved.
|
||
|
|
||
| # Mirror CodeRabbit's GitHub Actions validation locally with actionlint | ||
| # using the repository rule configuration in .github/actionlint.yml | ||
| actionlint -config-file .github/actionlint.yml .github/workflows/*.{yml,yaml} | ||
|
|
||
| # List action versions and check for updates | ||
| gh api repos/actions/checkout/releases/latest --jq '.tag_name' | ||
|
|
||
| # Resolve tag to immutable SHA (for pinning) | ||
| git ls-remote --tags https://github.com/actions/checkout.git | rg "refs/tags/v6.0.2(\^\{\})?$" | sort | tail -n 1 | awk '{print $1}' | ||
|
|
||
| # Check workflow run status (use workflow name or either file extension) | ||
| gh run list --workflow="PR Checks" --limit=5 | ||
| ``` | ||
|
|
||
| ## Resources | ||
|
|
||
| - [GitHub Actions Security Hardening](https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions) | ||
| - [GitHub Actions Reusable Workflows](https://docs.github.com/en/actions/sharing-automations/reusing-workflows) | ||
| - See [pinned-tag skill](../pinned-tag/SKILL.md) for SHA resolution workflow | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.