Language-Specific
Adapt to Your Stack
These workflows demonstrate language-specific security patterns. Customize scanning tools, dependency managers, and build steps for your technology stack while preserving the core security controls.
Go CI¶
Hardened CI for Go projects with static analysis and vulnerability scanning.
name: Go CI
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
jobs:
test:
name: Test on Go ${{ matrix.go-version }}
runs-on: ubuntu-latest
permissions:
contents: read
strategy:
fail-fast: true
matrix:
go-version: ['1.21', '1.22']
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
persist-credentials: false
- uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
go-version: ${{ matrix.go-version }}
cache: true
# SECURITY: Download dependencies with verification
- name: Download dependencies
run: go mod download
# SECURITY: Verify dependencies match go.sum
- name: Verify dependencies
run: go mod verify
# SECURITY: Run Go security checker
- name: Run govulncheck
run: |
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...
# SECURITY: Run staticcheck for code quality and security issues
- name: Run staticcheck
run: |
go install honnef.co/go/tools/cmd/staticcheck@latest
staticcheck ./...
- name: Run tests
run: go test -race -coverprofile=coverage.out -covermode=atomic ./...
- name: Upload coverage
uses: codecov/codecov-action@e0b68c6749509c5f83f984dd99a76a1c1a231044 # v4.0.1
with:
files: ./coverage.out
fail_ci_if_error: false
security-scan:
name: Security Scanning
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
persist-credentials: false
# SECURITY: CodeQL for Go static analysis
- uses: github/codeql-action/init@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4
with:
languages: go
queries: security-extended
- uses: github/codeql-action/autobuild@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4
- uses: github/codeql-action/analyze@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4
# SECURITY: Gosec for Go-specific security issues
- name: Run Gosec
uses: securego/gosec@c6131d00402c4f9b60c815179b03bdad482e62c4 # v2.18.2
with:
args: '-no-fail -fmt sarif -out gosec.sarif ./...'
- name: Upload Gosec results
uses: github/codeql-action/upload-sarif@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4
with:
sarif_file: gosec.sarif
Advanced Security Patterns¶
Fork PR Security with Two-Stage Workflow¶
Safely handle fork PRs with staged execution: tests run on untrusted code, deployment requires approval.
name: Fork PR CI
on:
pull_request:
# SECURITY: pull_request runs fork code in isolated context
# Fork PRs get read-only token, no secrets
branches: [main]
permissions:
contents: read
jobs:
# Stage 1: Run untrusted code from forks with minimal permissions
test-fork:
runs-on: ubuntu-latest
permissions:
contents: read # Read-only access
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
# SECURITY: Check out PR head (untrusted code)
ref: ${{ github.event.pull_request.head.sha }}
persist-credentials: false
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
with:
node-version: '20'
- name: Install dependencies
run: npm ci
# SECURITY: Run tests without secrets or write permissions
- name: Run tests
run: npm test
# SECURITY: Save test results for trusted workflow
- name: Save test results
uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0
with:
name: test-results
path: test-results/
Post PR comment with test results (separate workflow)¶
# .github/workflows/fork-pr-comment.yml
# SECURITY: workflow_run trigger runs in repository context (trusted)
# This workflow can access secrets and write to PRs
name: Post Fork PR Results
on:
workflow_run:
workflows: ["Fork PR CI"]
types:
- completed
permissions:
pull-requests: write
actions: read
jobs:
comment:
runs-on: ubuntu-latest
if: github.event.workflow_run.conclusion == 'success'
steps:
- name: Download test results
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
script: |
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: ${{ github.event.workflow_run.id }},
});
const artifact = artifacts.data.artifacts.find(a => a.name === 'test-results');
if (artifact) {
const download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: artifact.id,
archive_format: 'zip',
});
// Process and post results
}
Secret Scanning Prevention¶
Prevent credential leaks before they reach the repository.
name: Pre-commit Secret Scanning
on:
pull_request:
branches: [main]
permissions:
contents: read
jobs:
secret-scan:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
# SECURITY: Fetch full history to scan all commits in PR
fetch-depth: 0
persist-credentials: false
# SECURITY: gitleaks scans for hardcoded secrets
- name: Run gitleaks
uses: gitleaks/gitleaks-action@cb7149a9c69f0f7c6a0c5b7b094889a91831ff7f # v2.3.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITLEAKS_ENABLE_COMMENTS: false # Don't expose findings in PR comments
# SECURITY: Fail workflow if secrets detected
- name: Fail on secret detection
if: steps.gitleaks.outputs.exitcode == 1
run: |
echo "::error::Secrets detected in commit history. Remove before merging."
exit 1
Security Checklist¶
Use this checklist to verify your CI workflow follows security best practices.
Action Security¶
- [ ] All third-party actions pinned to full SHA-256 commit hashes
- [ ] Version comments added for human readability (
# vX.Y.Z) - [ ] Dependabot configured to update action pins
- [ ] Actions from verified publishers or GitHub-maintained only
- [ ] No community actions without security review
Permission Security¶
- [ ] Workflow-level permissions set to minimal (
contents: read) - [ ] Job-level permissions escalate only when required
- [ ] No
permissions: write-allor default write permissions - [ ]
security-events: writeonly on security scan jobs