Skip to content

Hardening: adopt permissions: {} (deny-all) as top-level default in GitHub Actions workflows #424

@WilliamBerryiii

Description

@WilliamBerryiii

Summary

Follow-up hardening to PR #408 (OSSF Token-Permissions). Consider replacing the top-level permissions: { contents: read } default across workflows with permissions: {} (deny-all) and relying exclusively on job-level permissions: blocks to grant scopes.

OSSF Scorecard scores both patterns the same on the Token-Permissions check, so this is a preference, not a requirement — tracked here as an explicit defense-in-depth refinement.

Motivation

  • Unambiguous intent: permissions: {} declares "the workflow grants nothing by default; every scope is an explicit job-level opt-in." This matches the per-job pattern the repo already uses across all 30 workflows.
  • Fail-loud on future changes: A new job added without a permissions: block would inherit nothing (and fail loudly on e.g. actions/checkout) rather than silently inheriting contents: read with unintended scope.
  • Strictly less privilege than contents: read at the top level.

Current State

Pattern Count Examples
permissions: { contents: read } only 22 security-scan.yml, docs-check-terraform.yml, terraform-lint.yml, scorecard.yml, …
contents: read + additional top-level scopes 6 pages-deploy.yml (pages/id-token/attestations), docs-automation-commenter.yml (issues/pull-requests), matrix-folder-check.yml, security-comprehensive.yml, security-deployment.yml, security-staleness-check.yml (each adds actions: read)
permissions: {} 0

Scope

  1. Straightforward swap (22 workflows): replace top-level permissions: { contents: read } with permissions: {}. Every job in these workflows already declares its own permissions: block per PR chore: vulnerability remediation (#409 phases A-G), OSSF hardening, and Docusaurus migration completion #408.
  2. Refactor required (6 workflows): push top-level write/read scopes down into the specific job(s) that need them before swapping. This is the bulk of the work.
  3. Verify: no reusable-workflow callers or workflow templates rely on inherited top-level scopes.

Acceptance Criteria

  • All workflows under .github/workflows/ use permissions: {} at the top level
  • Top-level write scopes from the 6 combined-permissions workflows are moved into specific jobs
  • CI passes on dev after the change (no jobs lose required scopes)
  • OSSF Scorecard Token-Permissions sub-score unchanged (expected) or improved
  • Workflow author guidance in .github/copilot-instructions.md (or equivalent) notes permissions: {} as the convention

References

Priority

Low — non-blocking style/hardening refinement. Good candidate for a single follow-up PR after #408 merges.

Metadata

Metadata

Labels

securitySecurity-related changes or concerns

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions