Disclosure: I maintain
ci-doctor. The comparison below describes each tool by what it documents and ships, not by my opinion of its authors. Run all four on the same workflow to see for yourself.
GitHub Actions YAML is small enough that one tool could in theory validate everything: syntax, secret hygiene, runner cost, supply-chain pinning, deprecated actions, untrusted inputs. In practice, the open-source landscape splits this work across four projects. They overlap in some areas and miss each other in others.
The short version
| Concern | actionlint | ci-doctor | sherif | octoscan |
|---|---|---|---|---|
| YAML / shell syntax | yes | no | no | no |
| Untrusted input injection | partial | no | no | yes |
| Action ref pinned to SHA | no | yes | no | yes |
Top-level permissions
|
no | yes | no | yes |
| Concurrency / cancel-in-progress | no | yes | no | no |
Job timeout-minutes
|
no | yes | no | no |
| Cache hint on setup-* actions | no | yes | no | no |
| Artifact retention-days | no | yes | no | no |
| Matrix combinatorial explosion | no | yes | no | no |
| Cost projection in dollars | no | via gha-budget
|
no | no |
| Auto-fix in place | no | yes | no | no |
| Auto-pin actions to SHA | no | via pin-actions
|
no | no |
| SARIF output for Code Scanning | via wrappers | yes | no | yes |
| Monorepo / multi-repo focus | no | no | yes | no |
actionlint
The reference syntax checker. Written in Go, fast, no dependencies. Catches expression syntax errors, unknown event names, unknown contexts, malformed shell scripts in run: blocks (it invokes shellcheck), and a small number of security patterns like ${{ github.event.pull_request.title }} in a shell context.
What it doesn't do: enforce policy. actionlint tells you whether your YAML parses and runs; it does not tell you whether your workflow is cheap, secure-by-default, or maintainable.
Use it for: pre-merge syntax validation. Catching shell quoting bugs.
ci-doctor
Policy and cost focus. Eleven rules grouped into cost, security, and maintenance. Includes --fix mode that auto-applies safe fixes (permissions, concurrency, timeouts, artifact retention) and --sarif for GitHub Code Scanning. Pairs with pin-actions for SHA pinning and gha-budget for dollar cost projection.
What it doesn't do: validate YAML or shell syntax (use actionlint), detect injection vulnerabilities at the expression level (use octoscan), or compare workflows across repos (use sherif).
Use it for: cost discipline. Default-secure policy. PR comments. Code Scanning ingestion. Auto-fixing the four common issues that have a single safe answer.
sherif
Cross-repo / monorepo lens. Sherif's value is comparing workflows across many repos under one org and surfacing inconsistency: same job, different timeout; same matrix, different runner; same checkout step, different version.
What it doesn't do: ship policy rules of its own. It tells you which workflows disagree; it does not say which one is right.
Use it for: standardising CI across an org once you've decided on a baseline.
octoscan
Security-first. Looks for untrusted-input injection patterns specifically (the ${{ github.event.* }} in shell context class), unpinned actions, missing top-level permissions, and similar hardening misses. Emits SARIF.
What it doesn't do: cost analysis, auto-fix, cache hints, retention policy, matrix sanity.
Use it for: security audit before a release. Quick check that your actions/checkout@... isn't shipping someone else's code.
How to combine them
None of these tools is a superset of the others. The cheapest composition that covers all four lenses is:
# syntax
actionlint
# cost + maintenance + auto-fix
npx ci-doctor --fix
npx ci-doctor --sarif > ci-doctor.sarif
# supply chain
npx pin-actions --check
# expression-level injection scanning
octoscan run
# cross-repo consistency, if you run an org
sherif --workspace .
Total runtime on a normal repo: under five seconds.
What I'd actually run in CI
In order, fail fast:
-
actionlint- syntax must be valid before policy makes sense. -
npx pin-actions --check- cheap, supply chain. -
npx ci-doctor --sarif > ci-doctor.sarif+codeql-action/upload-sarif- findings as PR annotations. -
octoscanif your workflow accepts external input.
The first three are non-negotiable for any repo with public CI. The fourth is non-negotiable for any workflow that runs against PRs from forks.
Try without installing
If you want to see what ci-doctor would say about your workflow without installing anything, paste it at https://depmedicdev-byte.github.io/audit.html. Same engine, runs entirely in your browser, share the result by URL.
See also
I priced the workflows of 20 famous OSS projects and counted what each linter would catch: https://depmedicdev-byte.github.io/benchmarks.html (229 workflows, 902 findings, all data linked).
Top comments (0)