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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions