Skip to content

Repository files navigation

workflow-permissions-lint

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).

Features (v0.1)

  • Scans .github/workflows/*.{yml,yaml}
  • Config file: .permissions-lint.yml
  • Detects missing permissions, write-all, and denied write scopes
  • CLI exit code 1 on findings, markdown report on stdout, optional SARIF
  • Composite GitHub Action wrapper
  • Windows- and Linux-safe paths

60-second quickstart

bash

# 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=$?"

PowerShell

# 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.

Config (.permissions-lint.yml)

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.

CLI

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.

Composite Action

- uses: ./
  with:
    root: ${{ github.workspace }}
    report: permissions-lint-report.md
    sarif: permissions-lint.sarif

Action inputs

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

  • fixtures/clean — top-level contents: read → exit 0
  • fixtures/violating — denied writes, write-all, missing permissions → exit 1

Install / test / demo

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

v0.2 limitations (honest)

  • Does not evaluate whether declared permissions are sufficient for the steps used (only policy shape).
  • Does not expand reusable workflow / workflow_call permission 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.

License

MIT

About

Maintainer and platform teams who need a **narrow, deterministic** check that every GitHub Actions workflow declares ...

Topics

Resources

Stars

10 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages