| name | gha-lint |
| description | Lint and secure GitHub Actions workflows using pinact, actionlint, ghalint, and zizmor. Use when the user adds, updates, or reviews GitHub Actions workflows and wants to check correctness, security, or pin action versions. |
| user-invocable | true |
GitHub Actions Lint & Security
Static analysis and security checking tools for GitHub Actions workflows. All tools are available via nixpkgs for local use. Each tool covers different checks with no overlap, so using all of them together is recommended.
| Tool | Purpose | nixpkgs |
|---|
| actionlint | Workflow syntax checking | nixpkgs#actionlint |
| pinact | SHA-pin action references | nixpkgs#pinact |
| ghalint | Security best practices | nixpkgs#ghalint |
| zizmor | Security vulnerability analysis | nixpkgs#zizmor |
actionlint
Syntax and type checker for workflow files. Integrates with shellcheck / pyflakes to also inspect inline scripts.
Basic Commands
nix run nixpkgs#actionlint
nix run nixpkgs#actionlint -- .github/workflows/nix-build.yaml
nix run nixpkgs#actionlint -- -format '{{json .}}'
What It Detects
- Workflow syntax errors (missing required keys, duplicate keys, invalid values)
- Type checking of
${{ }} expressions (undefined context references, etc.)
- Shell script issues via shellcheck
- Python script issues via pyflakes
- Matrix inconsistencies, invalid glob patterns
- Deprecated commands (
set-output, etc.)
CI Usage
Use suzuki-shunsuke/actionlint-action (installs actionlint + reviewdog + shellcheck via aqua):
- uses: suzuki-shunsuke/actionlint-action@29e0b7cda52e51a495d15f22759745ef6e19583a
Or use the official download script:
- name: Install actionlint
run: bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)
- name: actionlint
run: ./actionlint
Custom Runner Labels
If workflows use self-hosted or custom runner labels, add .github/actionlint.yaml so actionlint can validate them correctly.
self-hosted-runner:
labels:
- blacksmith-4vcpu-ubuntu-2404
Without this file, actionlint often reports unknown runner labels even when the workflow is correct.
pinact
Converts GitHub Actions version references to commit SHAs. Prevents supply chain attacks via tag rewriting.
Configuration
Place .pinact.yml at the repository root:
version: 3
Initialize: nix run nixpkgs#pinact -- init
Basic Commands
nix run nixpkgs#pinact -- run
nix run nixpkgs#pinact -- run \
.github/actions/setup-nix/action.yaml \
.github/actions/setup-git-bot/action.yaml
nix run nixpkgs#pinact -- run --check
nix run nixpkgs#pinact -- run --diff
nix run nixpkgs#pinact -- run --update
Conversion Format
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- Tags are replaced with SHAs; the original version is appended as a comment
- Files outside
.github/workflows/ (composite actions, etc.) must be specified explicitly
Key Options
| Option | Description |
|---|
--check | Non-zero exit if unpinned references exist. No file changes |
--verify, -v | Verify SHA and version annotation consistency |
--update, -u | Update to latest versions |
--diff | Output diff only. No file changes |
--include, -i | Filter targets by regex |
--exclude, -e | Exclude targets by regex |
--min-age, -m | Skip releases newer than N days (use with -u) |
CI Usage
Use suzuki-shunsuke/pinact-action:
- uses: suzuki-shunsuke/pinact-action@1081f5ad49ac904b7d977784f338145150a32112
with:
skip_push: 'true'
ghalint
Linter for security best practices in workflow and action definitions.
Basic Commands
nix run nixpkgs#ghalint -- run
nix run nixpkgs#ghalint -- run-action
What It Detects
- Jobs missing explicit
permissions
- Action references not pinned to commit SHAs
actions/checkout without persist-credentials: false
- Other security best practice violations
CI Usage
Install via aqua:
- uses: aquaproj/aqua-installer@11dd79b4e498d471a9385aa9fb7f62bb5f52a73c
with:
aqua_version: v2.56.6
- run: ghalint run
env:
GHALINT_LOG_COLOR: always
Requires aqua.yaml in the repository with ghalint registered:
registries:
- type: standard
ref: v4.294.0
packages:
- name: suzuki-shunsuke/ghalint@v1.5.5
zizmor
Security vulnerability analyzer for GitHub Actions. Offers 3 personas to tune detection sensitivity.
Basic Commands
nix run nixpkgs#zizmor -- .
nix run nixpkgs#zizmor -- .github/workflows/nix-build.yaml
nix run nixpkgs#zizmor -- --pedantic .
nix run nixpkgs#zizmor -- --offline .
nix run nixpkgs#zizmor -- --format sarif .
Personas (Detection Sensitivity)
| Persona | Description |
|---|
regular (default) | Minimizes false positives |
pedantic | Also detects code smells |
auditor | Comprehensive detection, tolerates false positives |
What It Detects
- Excessive
permissions settings
- Code injection via template expansion (
${{ }})
- Direct use of untrusted inputs
- Potential commit spoofing
- Dangerous use of
pull_request_target
- Improper use of self-hosted runners
CI Usage
Use zizmorcore/zizmor-action:
- uses: zizmorcore/zizmor-action@0dce2577a4760a2749d8cfb7a84b7d5585ebcb7d
With GitHub Advanced Security:
- uses: zizmorcore/zizmor-action@0dce2577a4760a2749d8cfb7a84b7d5585ebcb7d
Without Advanced Security:
- uses: zizmorcore/zizmor-action@0dce2577a4760a2749d8cfb7a84b7d5585ebcb7d
with:
advanced-security: false
CI Integration: All Tools Combined
Example workflow combining all 4 tools using their official actions (no nixpkgs):
name: 'CI: GitHub Actions lint'
on:
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions: {}
jobs:
gha-lint:
runs-on: ubuntu-24.04-arm
timeout-minutes: 10
permissions:
contents: read
actions: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
persist-credentials: false
- name: actionlint
run: |
bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)
./actionlint
- uses: suzuki-shunsuke/pinact-action@1081f5ad49ac904b7d977784f338145150a32112
with:
skip_push: 'true'
- uses: aquaproj/aqua-installer@11dd79b4e498d471a9385aa9fb7f62bb5f52a73c
with:
aqua_version: v2.56.6
- name: ghalint
run: ghalint run
env:
GHALINT_LOG_COLOR: always
- uses: zizmorcore/zizmor-action@0dce2577a4760a2749d8cfb7a84b7d5585ebcb7d
with:
advanced-security: false
Workflow: When Adding or Modifying GitHub Actions
- Edit workflows or composite actions
- Run all tools locally:
nix run nixpkgs#actionlint
nix run nixpkgs#pinact -- run
nix run nixpkgs#ghalint -- run
nix run nixpkgs#zizmor -- .
- Review changes and commit:
git diff
- Open a PR and verify CI passes
Notes
- Setting
GITHUB_TOKEN / GH_TOKEN avoids API rate limits (pinact, zizmor)
- actionlint auto-integrates with shellcheck and pyflakes if they're in PATH (nixpkgs version bundles them)
- ghalint's checks partially overlap with pinact (SHA pinning), but ghalint also covers other best practices, so using both is worthwhile