Skip to content

Layer 3 — Rulesets & Merge Gates

This layer ties Layer 2 and Layer 2b to merge protection: pull requests cannot merge unless the required audit workflows run and pass. Start in Evaluate (audit) mode, then switch to Active after the rollout window.

  1. Organization → Settings → Rulesets → New ruleset → New branch ruleset (or the enterprise-level equivalent under Policies → Repository).
  2. Name: default-branch-protection.
  3. Enforcement status: start in Evaluate (audit mode); flip to Active after the rollout window.
  4. Bypass list: owners only, used sparingly.
  5. Target: all repositories, default branch.
  6. Rules — enable:
    • Restrict deletions
    • Require linear history
    • Require a pull request before merging (1 approval, dismiss stale approvals on push, require Code Owner review if applicable)
    • Require status checks to pass — add the zizmor check
    • Block force pushes
    • Require workflows to run — add both platform-org/security-workflows/.github/workflows/zizmor-audit.yml@<SHA> and platform-org/security-workflows/.github/workflows/dcg-scan.yml@<SHA>
  7. Save.

For repeatable, version-controlled provisioning, POST to the rulesets endpoint:

Terminal window
gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
/orgs/<org>/rulesets \
--input - <<'JSON'
{
"name": "default-branch-protection",
"target": "branch",
"enforcement": "evaluate",
"conditions": {
"ref_name": { "include": ["~DEFAULT_BRANCH"], "exclude": [] }
},
"rules": [
{ "type": "deletion" },
{ "type": "non_fast_forward" },
{ "type": "required_linear_history" },
{
"type": "pull_request",
"parameters": {
"required_approving_review_count": 1,
"dismiss_stale_reviews_on_push": true,
"require_code_owner_review": false,
"require_last_push_approval": true,
"required_review_thread_resolution": true
}
},
{
"type": "workflows",
"parameters": {
"workflows": [
{
"repository_id": <REPO_ID_OF_SECURITY_WORKFLOWS_REPO>,
"path": ".github/workflows/zizmor-audit.yml",
"ref": "refs/heads/main"
},
{
"repository_id": <REPO_ID_OF_SECURITY_WORKFLOWS_REPO>,
"path": ".github/workflows/dcg-scan.yml",
"ref": "refs/heads/main"
}
]
}
}
]
}
JSON

Get the repository ID with:

Terminal window
gh api /repos/platform-org/security-workflows --jq .id

Review insights at Rulesets → [ruleset] → Insights. Fix repos that would have been blocked, then PATCH the ruleset to "enforcement": "active".

Required workflows still run on fork PRs, but secrets are unavailable. Make sure the audit path does not depend on secrets, so external contributions are still gated.