Advanced & Checklist
Complete All Checklist Items
This checklist represents minimum security requirements for production CI workflows. Every unchecked item increases attack surface. Address all items before merging workflows to default branch.
Trigger Security¶
- [ ]
pull_requesttrigger used (notpull_request_targetfor untrusted code) - [ ] Fork PR builds isolated with read-only token
- [ ] No secrets exposed to fork PRs
- [ ] Artifact uploads require repository match check
- [ ] Two-stage workflow for fork PR comments
Secret Security¶
- [ ] No hardcoded credentials in workflow files
- [ ] Secrets accessed via
${{ secrets.SECRET_NAME }} - [ ] Secrets not logged or echoed in workflow steps
- [ ] OIDC preferred over long-lived credentials
- [ ] Pre-commit secret scanning enabled
Dependency Security¶
- [ ] Dependency review on PRs
- [ ] Vulnerability scanning (npm audit, safety, govulncheck)
- [ ] Dependency pinning in lock files (package-lock.json, requirements.txt, go.sum)
- [ ] SARIF upload for security findings
- [ ] Critical/high vulnerabilities block merge
Build Security¶
- [ ]
persist-credentials: falseon checkout - [ ] Shallow clones where possible (
depth: 1) - [ ] Minimal artifact retention (7-14 days)
- [ ] Build artifacts uploaded only on non-fork PRs
- [ ] No sensitive data in build artifacts
Common Mistakes and Fixes¶
Mistake 1: Over-Privileged Default Permissions¶
Bad:
# DANGER: Workflow inherits repository default permissions (often write-all)
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Good:
# SECURITY: Explicit minimal permissions
on: [push, pull_request]
permissions:
contents: read # Deny all except contents read
jobs:
test:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
Mistake 2: Unpinned Actions¶
Bad:
# DANGER: Tag references are mutable, allow supply chain attacks
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
Good:
# SECURITY: SHA pinning prevents tag hijacking
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
Mistake 3: Exposing Secrets to Fork PRs¶
Bad:
# DANGER: pull_request_target runs in repo context with full secret access
on:
pull_request_target:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
ref: ${{ github.event.pull_request.head.sha }} # Checks out untrusted code
- run: npm ci
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }} # Secret exposed to fork!
Good:
# SECURITY: pull_request isolates fork code from secrets
on:
pull_request: # Untrusted code, read-only token, no secrets
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- run: npm ci # No secrets needed for public dependencies
Mistake 4: Script Injection via Untrusted Input¶
Bad:
# DANGER: PR title injected directly into shell command
on:
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- run: echo "Testing PR: ${{ github.event.pull_request.title }}"
Good:
# SECURITY: Untrusted input passed via environment variable
on:
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Echo PR title safely
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: echo "Testing PR: $PR_TITLE"
Related Patterns¶
- Action Pinning: SHA pinning patterns and Dependabot configuration
- Token Permissions: GITHUB_TOKEN permission templates for all workflow types
- Fork PR Security: Safe handling of fork pull requests with two-stage workflows
- Secret Management: Secret exposure prevention and OIDC patterns
- Security Scanning: Comprehensive security scanning with SAST, dependency review, and SARIF upload
Summary¶
Hardened CI workflows require defense in depth:
- Pin all actions to SHA-256 hashes with version comments
- Minimize permissions at workflow and job level
- Isolate fork PRs with
pull_requesttrigger and two-stage workflows - Scan for secrets before commits reach the repository
- Scan dependencies for vulnerabilities on every PR
- Upload SARIF findings to GitHub Security tab for visibility
Copy these templates as starting points. Adjust permissions and scanning tools based on your language stack and security requirements.