GitHub Actions Workflow Standards
Intermediate
Enforce security and performance standards for GitHub Actions workflows — SHA-pinned actions, least-privilege permissions, timeouts, concurrency, and caching requirements.
File Patterns
**/.github/workflows/*.yml**/.github/workflows/*.yaml
This rule applies to files matching the patterns above.
Rule Content
rule-content.md
# GitHub Actions Workflow Standards
## Rule
All GitHub Actions workflows MUST follow these security and performance requirements.
## Format
```yaml
name: Descriptive Workflow Name
on:
<trigger>:
branches: [main]
concurrency:
group: <workflow>-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
<job-name>:
runs-on: ubuntu-latest
timeout-minutes: <limit>
steps:
- uses: <owner>/<repo>@<full-sha> # vX.Y.Z
```
## Requirements
### Security
- Pin ALL third-party actions to full SHA (not tags)
- Set `permissions: contents: read` at workflow level
- Grant additional permissions per-job only when needed
- Never use `pull_request_target` with PR code checkout
- Store secrets in GitHub Secrets, never in workflow files
- Use OIDC for cloud provider authentication when available
### Performance
- Set `timeout-minutes` on every job
- Use `concurrency` groups with `cancel-in-progress: true`
- Cache dependencies using setup-*/cache actions
- Use `paths` filter to skip irrelevant workflows
### Naming
- Workflow file: kebab-case (`deploy-production.yml`)
- Workflow name: Title Case (`Deploy Production`)
- Job names: kebab-case (`build-and-test`)
- Step names: Sentence case (`Install dependencies`)
## Examples
### Good
```yaml
name: CI
on:
pull_request:
branches: [main]
paths:
- 'src/**'
- 'package.json'
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.7
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npm test
```
### Bad
```yaml
name: test
on: push
# No permissions block (defaults to write-all)
# No concurrency (wastes Actions minutes)
jobs:
test:
runs-on: ubuntu-latest
# No timeout (can run forever)
steps:
- uses: actions/checkout@v4 # Tag, not SHA
- run: npm install # No caching
- run: npm test
```
## Enforcement
Use actionlint for local validation and add it as a CI check.
Configure organization-level policies to restrict allowed Actions.FAQ
Discussion
Loading comments...