ISSUE-801 Medium Quick CLI Workflow triggers and permissions
Workflow does not declare permissions
Control: Workflow permissions must be declared · Config key: workflowsMustDeclarePermissions
📋 What is this?
A workflow has no top-level permissions: block. The job inherits whatever the repo / organisation default is, which is often write-all.
⚠️ Impact
Implicit permissions are silently broad. A workflow that legitimately needs contents: read may end up with contents: write, id-token: write, and a dozen other scopes — any one of which a compromised step can use to push to the repo or impersonate the workflow.
🔧 How to fix
Add a top-level permissions: block listing only the scopes the workflow actually needs. Most workflows only need contents: read.
✗ Before No explicit `permissions:` — relies on the repo default.
# .github/workflows/test.yml — ❌ Inherits whatever the repo default isname: teston: [push, pull_request]jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm test✓ After `contents: read` is sufficient for a test workflow.
# .github/workflows/test.yml — ✅ Least privilegename: teston: [push, pull_request]permissions: contents: readjobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - run: npm test
# .plumber.yamlgithub: controls: workflowMustDeclarePermissions: enabled: true💡 Tips
- Override per-job when a specific job needs more (e.g.
releaseneedingcontents: write). - Org admins can also enforce
permissions: read-allas the org default — a useful safety net. - Pair with ISSUE-803 to catch the over-broad case explicitly.
⚙️ Configuration
This control is configured in .plumber.yaml under the github section:
github:
controls:
workflowsMustDeclarePermissions:
enabled: trueSee the CLI documentation for the full configuration reference.