Skip to content

feat: manage org-level Actions fork-PR-approval policy and allowed-actions allowlist via config.yml #59

@avrabe

Description

@avrabe

Background

Temper currently manages repository-level settings: branch protection, rulesets, merge strategy, labels, dependabot config. It does not manage org-level GitHub settings, so a chunk of organisation hardening still has to be clicked through in the GitHub UI by hand.

Two specific org-level settings worth bringing under temper's control:

1. Fork-PR approval policy

  • Setting: Org Settings → Actions → General → "Fork pull request workflows from outside collaborators"
  • Recommended value: Require approval for all outside collaborators
  • Why: an outside collaborator opening a fork PR can otherwise trigger workflows that read repo secrets / write to the cache. The default in newer orgs is already this, but the setting drifts (especially when a sub-org is migrated from a personal account) and there's no way to verify it short of clicking through.
  • REST API: GET/PATCH /orgs/{org}/actions/permissions/workflow covers a related toggle (default_workflow_permissions); the fork-PR-approval policy specifically is GET/PATCH /orgs/{org}/actions/permissions with enabled_repositories and the new default_workflow_permissions shape. The fork-PR approval setting itself is exposed via GET/PATCH /orgs/{org}/actions/permissions/fork-pr-workflows-from-outside-collaborators (recently added — verify the path against the docs at implementation time).

2. Allowed actions allowlist

  • Setting: Org Settings → Actions → General → "Allow actions and reusable workflows"
  • Recommended value: Allow [pulseengine, actions, github] actions and reusable workflows plus an explicit allowlist of vetted third parties (e.g. Swatinem/rust-cache@*, bazel-contrib/setup-bazel@*, anchore/sbom-action@*, actions/attest-build-provenance@*).
  • Why: without an allowlist, any new workflow file (whether on main, on a PR, or in a fork) can pull a fresh action from an arbitrary publisher and run it inside the repo's runner. Combined with the cache poisoning surface, this is one of the easier supply-chain footguns in GitHub Actions today.
  • REST API: GET/PATCH /orgs/{org}/actions/permissions/selected-actions returns/accepts the patterns_allowed array.

Proposed config shape

# config.yml
org_actions:
  fork_pr_approval:
    # 'all' | 'collaborators' | 'first_time_contributors' | 'never'
    require_approval_for: all
  allowed_actions:
    # If null, allow all (the github default). If a list, becomes the
    # exclusive allowlist plus the org's own actions plus github + actions.
    patterns_allowed:
      - actions/*
      - github/*
      - pulseengine/*
      - Swatinem/rust-cache@*
      - bazel-contrib/setup-bazel@*
      - anchore/sbom-action@*
      - actions/attest-build-provenance@*

Implementation notes

  • These are org-level API calls — only the org admin's installation token can apply them. Verify the bot's app installation has organization_administration: write (it currently doesn't; needs a permission update + re-consent during install).
  • Apply in the same scheduled sweep that already does branch-protection drift correction. Compare current → desired, PATCH only on diff, log every change with a clear "applied org policy" log line.
  • Idempotency: read-back-and-assert after PATCH (same pattern temper uses for repository-level settings).
  • A chatops:check-org-policy issue form would be useful to dry-run the diff without applying.

Out of scope for this issue

  • Per-repo Actions permission overrides (repos/{owner}/{repo}/actions/permissions) — a sibling feature, would track separately.
  • The existing repo-level fork-PR approval (some repos have a per-repo override of the org default) — defer until the org-level path is solid.

Why now

Came up while applying smithy required-checks defaults to temper's config.yml (PR #57). The branch-protection floor is now config-managed; the org-level Actions policy is still drift-prone UI-only. Closing that gap means a single source of truth for "what the org's CI can do".

🤖 Issue authored with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions