A lightweight .NET CLI that scans GitHub Actions workflows for security, reliability, performance, and cost issues.
git clone https://github.com/Wezylnia/gh-actions-doctor.git
cd gh-actions-doctor
dotnet run --project src/GhActionsDoctor.Cli -- scan --path samples/bad --fail-on noneGitHub Actions workflows are often copied from project to project and then left alone for months. They may still work, but small problems pile up:
- overly broad
GITHUB_TOKENpermissions, - actions referenced by mutable branches like
@main, - third-party actions not pinned to commit SHAs,
- risky
pull_request_targetusage, - jobs without timeouts,
- workflows without concurrency controls,
- dependency installs without caching,
- broad triggers that burn CI minutes unnecessarily.
gh-actions-doctor catches those issues early and prints practical suggestions.
Current release: 1.0.0
This is the first stable release. It can scan workflow files, report findings in text, JSON, GitHub annotations, and SARIF, apply conservative safe fixes, suppress known findings with baselines, and return CI-friendly exit codes.
Looking for a place to help? Start here:
- .NET SDK 10
This project currently targets net10.0.
The current package version is 1.0.0.
When published to NuGet:
dotnet tool install --global gh-actions-doctor --version 1.0.0From a locally packed package:
dotnet pack src/GhActionsDoctor.Cli --configuration Release
dotnet tool install --tool-path .tmp/tools gh-actions-doctor --version 1.0.0 --add-source src/GhActionsDoctor.Cli/bin/Release
.tmp/tools/gh-actions-doctor scanScan the default workflow directory:
dotnet run --project src/GhActionsDoctor.Cli -- scanScan a custom directory:
dotnet run --project src/GhActionsDoctor.Cli -- scan --path samples/badPrint JSON:
dotnet run --project src/GhActionsDoctor.Cli -- scan --path samples/bad --format jsonFail a build on warnings or errors:
dotnet run --project src/GhActionsDoctor.Cli -- scan --fail-on warningRun only selected rules:
dotnet run --project src/GhActionsDoctor.Cli -- scan --include missing-permissions,missing-timeoutSkip selected rules:
dotnet run --project src/GhActionsDoctor.Cli -- scan --exclude action-not-sha-pinnedStrict mode promotes selected security findings:
dotnet run --project src/GhActionsDoctor.Cli -- scan --strictGitHub Actions Doctor
Scanned workflows: 2
build.yml
[warning] Workflow does not define a top-level permissions block. (missing-permissions)
Suggestion: Add a top-level permissions block and grant only the permissions required by the workflow.
[warning] Job 'build' does not define timeout-minutes. (missing-timeout)
Suggestion: Add a timeout-minutes value that reflects the expected maximum runtime for this job.
[info] actions/setup-node is used without dependency caching. (setup-node-cache-missing)
Suggestion: Add a cache value such as npm, yarn, or pnpm when the workflow installs Node.js dependencies.
Summary:
0 errors
2 warnings
1 info| Rule | Severity | Category | Description |
|---|---|---|---|
missing-permissions |
warning | security | Reports workflows without a top-level permissions block. |
mutable-action-reference |
warning | security | Reports actions using mutable refs such as @main, @master, or @latest. |
action-not-sha-pinned |
info | security | Reports third-party actions that are not pinned to a full commit SHA. |
risky-pull-request-target |
warning/error | security | Reports pull_request_target, with errors for high-risk patterns. |
missing-timeout |
warning | reliability | Reports jobs without timeout-minutes. |
missing-concurrency |
info | cost | Reports workflows that are likely to benefit from concurrency. |
setup-node-cache-missing |
info | performance | Reports actions/setup-node usage without dependency caching. |
broad-push-trigger |
info | cost | Reports workflows that run on every push without branch, tag, or path filters. |
duplicate-workflow-name |
info | maintainability | Reports repeated workflow names across files. |
overbroad-id-token-permission |
warning | security | Reports id-token: write permissions that do not appear to be used. |
pull-request-target-untrusted-checkout |
error | security | Reports pull_request_target workflows that check out untrusted pull request head code. |
untrusted-expression-in-run |
warning | security | Reports untrusted GitHub event data interpolated directly into shell commands. |
release-workflow-overprivileged-token |
warning | security | Reports release workflows that grant overly broad token permissions. |
remote-script-execution |
warning | security | Reports run steps that pipe remote scripts directly to a shell. |
secret-echo-risk |
warning | security | Reports steps that appear to echo or redirect secret values. |
yaml-parse-error |
error | correctness | Reports invalid workflow YAML without crashing the scan. |
Want to add the next rule? The rule system is intentionally small: one rule class, focused tests, one docs page, and a README update. See Adding a Rule.
gh-actions-doctor scan [options]
gh-actions-doctor fix [options]
Scan options:
--path <path> Workflow directory or file. Defaults to ./.github/workflows.
--format <text|json|github-annotations|sarif>
Output format. Defaults to text.
--fail-on <error|warning|info|none>
Controls non-zero exit code. Defaults to error.
--include <rule-id,...> Run only selected rules.
--exclude <rule-id,...> Skip selected rules.
--strict Promote selected security findings.
--config <path|none> Config file. Defaults to .gh-actions-doctor.yml if present.
--baseline <path|none> Baseline file for suppressing known findings.
--write-baseline <path> Write current findings to a baseline file.
--prune-baseline Remove stale entries from the baseline file.
--show-suppressions Include suppressed findings in output.
Fix options:
--path <path> Workflow directory or file. Defaults to ./.github/workflows.
--dry-run Print safe fixes without changing files. Default.
--apply Apply safe fixes.
--rule <rule-id,...> Limit fixes to missing-timeout and/or missing-permissions.gh-actions-doctor automatically reads .gh-actions-doctor.yml or .gh-actions-doctor.yaml from the current repository root when present. Use --config <path> to select a file or --config none to disable config loading.
path: .github/workflows
format: text
failOn: warning
strict: true
exclude:
- action-not-sha-pinned
severity:
missing-permissions: error
baseline: .gh-actions-doctor-baseline.jsonCLI-provided values take precedence over the matching config fields. See Configuration for the full reference.
Use JSON output for automation:
dotnet run --project src/GhActionsDoctor.Cli -- scan --format jsonThe JSON payload includes:
- summary counts,
- finding file path,
- severity,
- rule ID,
- category,
- message,
- suggestion,
- source line and column when available.
Use SARIF for GitHub Code Scanning:
dotnet run --project src/GhActionsDoctor.Cli -- scan --format sarif --fail-on none > gh-actions-doctor.sarifUse GitHub annotations inside Actions jobs:
dotnet run --project src/GhActionsDoctor.Cli -- scan --format github-annotations --fail-on warningPreview safe fixes without changing files:
dotnet run --project src/GhActionsDoctor.Cli -- fix --path .github/workflows --dry-runApply conservative fixes for missing top-level permissions and job timeouts:
dotnet run --project src/GhActionsDoctor.Cli -- fix --path .github/workflows --applyAdd gh-actions-doctor to your CI workflow to catch issues in every PR:
- uses: actions/setup-dotnet@v5
with:
dotnet-version: 10.0.x
- run: dotnet run --project src/GhActionsDoctor.Cli -- scan --fail-on warningFor a full example, see the CI workflow that scans this repository.
Before merging workflow changes, run through the Workflow Hardening Checklist for a concise review of permissions, pinning, timeouts, triggers, and secrets.
Restore, build, and test:
dotnet restore
dotnet build GhActionsDoctor.sln
dotnet test GhActionsDoctor.slnTry the sample workflows:
dotnet run --project src/GhActionsDoctor.Cli -- scan --path samples/good --fail-on none
dotnet run --project src/GhActionsDoctor.Cli -- scan --path samples/bad --fail-on nonePack as a local .NET tool:
dotnet pack src/GhActionsDoctor.Cli --configuration Release
dotnet tool install --tool-path .tmp/tools gh-actions-doctor --version 1.0.0 --add-source src/GhActionsDoctor.Cli/bin/Release
.tmp/tools/gh-actions-doctor scanContributions are welcome. See CONTRIBUTING.md and docs/contributing/adding-a-rule.md for guidance.
Good contribution paths:
- Add a focused workflow rule with tests and docs.
- Improve source locations for findings.
- Improve reporter output such as SARIF or GitHub annotations.
- Add sample workflows for common project types.
- Improve parser resilience for real-world workflow YAML.
Good first issues | Help wanted
See docs/roadmap.md for the release roadmap, docs/project-status.md for current project status, and docs/rules/README.md for the rule catalog.
Adopting the tool in an existing repository? See docs/adoption-guide.md.
The public repository is set up for small, reviewable contributions. main is protected, CI is required, and CODEOWNERS routes review to the maintainer. Stale-review dismissal, last-push approval, conversation resolution, disabled force pushes/deletions, admin emergency bypass, and GitHub Copilot automatic PR review are configured for pull requests targeting main.
MIT. See LICENSE.