GitHub Actions Hardening
A practical, layered strategy for hardening GitHub Actions across every repository in an organization. Each layer is additive: together they enforce SHA-pinning, run security audits on every pull request, scan committed shell for destructive commands, gate merges, and keep pinned SHAs fresh.
The five layers
Section titled “The five layers”| Layer | What it does | Where it lives |
|---|---|---|
| 1 — SHA pinning | Runtime block on unpinned actions | Organization → Policies → Actions |
| 2 — Static analysis | zizmor audit on every PR | Central security-workflows repo |
| 2b — Command scanning | Scans committed shell for destructive operations | Central reusable workflow + ruleset |
| 3 — Rulesets & merge gates | Requires the audit workflows + gates merges | Organization → Policies → Repository |
| 4 — Dependabot | Keeps pinned SHAs up to date | Per-repo .github/dependabot.yml |
A staged rollout ties them together — critically, Layer 1 has no audit mode, so it must come after static analysis has surfaced and fixed the bulk of unpinned references.
Prerequisites
Section titled “Prerequisites”- Admin role — you need owner-level access to set organization or enterprise policies and rulesets.
- A platform org — repositories live inside organizations, not directly under an
enterprise. Designate one organization (for example
platform-org) to host shared workflows and tooling. See Key concepts below. - A central repo — in the platform org, create one internal repository to host
shared workflows. A common convention is
security-workflowswith internal visibility, so every internal/private repo in the organization can consume it. - A rollout window — plan to run rulesets in Evaluate (audit) mode for roughly two weeks before switching them to Active.
Key concepts
Section titled “Key concepts”Enterprises don’t hold repositories
Section titled “Enterprises don’t hold repositories”There is no such thing as an “enterprise repository.” Repositories only exist inside organizations (or under user accounts). An enterprise is a container for organizations, billing, identity, and policies — not a place that holds repos directly.
Enterprise (e.g. example-inc)├── Organization: example-product ← repos live here│ ├── repo: frontend│ └── repo: backend├── Organization: platform-org ← and here│ ├── repo: security-workflows ← shared workflows go here│ └── repo: terraform-modules└── Organization: example-data └── repo: warehouseEnterprise-level features (policies, rulesets, allowlists, SSO, billing) cascade down to all organizations, but you cannot create a repo at the enterprise level.
The platform-org pattern
Section titled “The platform-org pattern”For shared workflows, tooling, and platform code, designate one organization to act as
the “platform” or “shared infrastructure” org. Common naming patterns include
*-platform, *-infra, *-shared, *-ci, or *-tooling. Throughout this guide,
platform-org refers to whichever org you designate.
Keeping shared tooling in a dedicated org has clear benefits:
- Blast radius — a compromise of the platform org affects every CI run. Minimal membership reduces that risk.
- Audit clarity — activity logs for shared tooling stay separate from product noise.
- Permission model — engineers don’t need write access to platform tooling to ship features.
When creating the shared workflows repo, set it to Internal visibility and, under Settings → Actions → General → Access, choose Accessible from repositories in the organization. Without this, other repos cannot invoke the workflows and the ruleset in Layer 3 will fail to run them. Protect its default branch strictly — a bad commit there propagates to every repo that requires the workflow.
Where config and workflows live
Section titled “Where config and workflows live”Three distinct locations are easy to mix up — the two .github ones especially:
your-org/.github— a repository literally named.githubat the org level. GitHub recognizes it as a source of org-wide defaults: community health files (SECURITY.md,CONTRIBUTING.md, issue/PR templates), the org profile (profile/README.md), and internal governance docs like this runbook.any-repo/.github/— a directory inside every repo for that repo’s own workflows, templates, and configs.platform-org/security-workflows— a separately named internal repo that hosts the shared workflow YAML consumed by other repos. Give it a descriptive name (not.github-*) so it is never confused with the two.githublocations above.
Keep the governance-docs repo (.github) and the shared-workflows repo
(security-workflows) separate: they have different change cadence, reviewers, and
branch-protection needs.
Where to go next
Section titled “Where to go next”Start with Layer 1 — Pin actions to SHAs, then work through the layers in order. When you’re ready to deploy, follow the staged rollout plan.