Maintainer and platform teams who need a narrow, deterministic check that every GitHub Actions workflow declares least-privilege permissions — without adopting a full workflow security suite (zizmor, CodeQL Actions queries, or broad SAST). Pain: default GITHUB_TOKEN write and accidental permissions: write-all ship quietly until token abuse; nearest tools either scan many issue classes or only document best practice. This package only policy-lints workflow permissions (require-permissions, deny-scopes, job-allowlist).
- Scans
.github/workflows/*.{yml,yaml} - Config file:
.permissions-lint.yml - Detects missing
permissions,write-all, and denied write scopes - CLI exit code
1on findings, markdown report on stdout, optional SARIF - Composite GitHub Action wrapper
- Windows- and Linux-safe paths
# from the repository root
npm install
npm test
node --import tsx src/cli.ts --help
node --import tsx src/cli.ts --root fixtures/clean
node --import tsx src/cli.ts --root fixtures/violating --sarif out.sarif; echo "exit=$?"# from the repository root
npm install
npm test
node --import tsx src/cli.ts --help
node --import tsx src/cli.ts --root fixtures/clean
node --import tsx src/cli.ts --root fixtures/violating --sarif out.sarif; Write-Host "exit=$LASTEXITCODE"After npm run build, the same commands work with node dist/cli.js.
require-permissions: true
deny-scopes:
- contents
- packages
- actions
- id-token
job-allowlist:
- docs| Key | Meaning |
|---|---|
require-permissions |
Every non-allowlisted job must have an effective permissions block (job or workflow level). |
deny-scopes |
Scopes that must not be set to write. |
job-allowlist |
Job ids allowed to omit permissions when the workflow also omits them. |
workflow-permissions-lint [options] [root]
-r, --root <dir> Repository root (default: cwd)
-c, --config <file> Config path
--report <file> Write markdown report
--sarif <file> Write SARIF 2.1.0
-h, --help
-V, --version
Exit codes: 0 clean, 1 findings, 2 usage/runtime error.
- uses: ./
with:
root: ${{ github.workspace }}
report: permissions-lint-report.md
sarif: permissions-lint.sarif| Input | Default | Description |
|---|---|---|
root |
${{ github.workspace }} |
Repo root to scan |
config |
"" |
Optional config path |
report |
permissions-lint-report.md |
Markdown report path |
sarif |
"" |
Optional SARIF path |
fixtures/clean— top-levelcontents: read→ exit0fixtures/violating— denied writes,write-all, missing permissions → exit1
npm install
npm test
npm run build
node dist/cli.js --help
node dist/cli.js --root fixtures/clean
node dist/cli.js --root fixtures/violating --report report.md --sarif out.sarif- Does not evaluate whether declared permissions are sufficient for the steps used (only policy shape).
- Does not expand
reusable workflow/workflow_callpermission inheritance graphs across repos. - Does not lint org/repo Actions default-permission settings (UI/API), only workflow YAML.
- No auto-fix / PR suggestions yet; report-only.
- YAML anchors/aliases and uncommon permission expressions are not specially modeled.
MIT