Exercise 1: Policy Guardrails
Exercise 1: Policy Guardrails (~10 min)
Goal: Learn to SELECT the right rules for your context — then prove that policy blocks merges before any scan runs.
🎯 “Don’t rely on heroics. Encode guardrails into the workflow.” Ask yourself: “What if the senior security reviewer is on vacation?”
📝 Open
docs/rule-selection-guide.md— use the decision framework to classify your repository and select rules.
Step 1: Classify Your Repository and Select Rules (~3 min)
Before configuring anything, DECIDE which rules to apply. GitHub rulesets offer 15+ available rules — choosing the right ones depends on your context.
1a. Classify the workshop repository:
| Factor | Your Answer |
|---|---|
| Repository type | 🔴 Production / 🟡 Staging / 🟢 Experimental / 🔵 Library |
| Regulatory requirements? | Yes (financial/healthcare/govt) / No |
| Team size | Solo / Small (2-5) / Large (5+) |
| CI/CD maturity | None / Basic (tests) / Full (tests + scanning + deploy) |
For this workshop, classify as 🔴 Production (regulated enterprise scenario).
1b. Review the full rule menu and select rules for your classification. Key rules to consider:
| Rule | Purpose | Mitigates Threat | Select? |
|---|---|---|---|
| Require PR before merging | No direct commits | Row 6, 7 | ✅ |
| Require 2+ approvals | Multi-reviewer gate | Row 6 | ✅ |
| Dismiss stale reviews | Re-review after changes | Row 7 | ✅ |
| Require status checks (CodeQL) | Automated vulnerability check | Row 2 | ✅ |
| Block force pushes | Prevent history rewriting | Row 7 | ✅ |
| Require signed commits | Identity verification | Row 6 | Consider |
| Require linear history | Clean audit trail | Row 7 | Consider |
| Require conversation resolution | All review comments addressed | Row 8, 9 | ✅ |
[!TIP] Each rule traces back to a threat model row. This is NOT a checklist to blindly enable everything — it’s a decision framework. For experimental repos, you’d select fewer rules. For production, more.
Step 2: Create an Org-Level Ruleset (~4 min)
2a. Navigate to Organization Rulesets
- Go to your Organization Settings → Repositories → Rulesets
- Click New ruleset → New branch ruleset
2b. Configure Ruleset Metadata
| Setting | Value |
|---|---|
| Name | production-security-guardrails |
| Enforcement | Active |
| Target repositories | Dynamic list by name → *-demo |
| Target branches | Default branch |
2c. Enable Rules Based on Your Selection
Enable the rules you selected in Step 1. For the 🔴 Production classification:
- Require a pull request before merging
- Required approvals:
1(for this workshop; in production, use2+) - Dismiss stale reviews when new commits are pushed: ✅
- Required approvals:
- Require status checks to pass
- Add required check:
CodeQL
- Add required check:
- Block force pushes
- Require conversation resolution before merging (ensures review comments are addressed)
For a TIERED strategy, you would create additional rulesets:
staging-standard→ Target:staging-*→ 1 approval, CodeQL onlyexperimental-minimal→ Target:experimental-*→ 1 approval, no status checks
Click Create to save the ruleset.
Step 3: Test the Policy (~3 min)
# Create a trivial change
echo "// policy test" >> README.md
git add README.md
git commit -m "Test policy guardrails"
git push origin workshop/secure-by-design
- Open a Pull Request from
workshop/secure-by-design→main - Observe: The merge button is blocked — status checks must pass AND approvals are required
- Attempt to merge without passing checks → ❌ Blocked
- Once checks pass and approvals are given → ✅ Merge allowed
Verify the merge was blocked until ALL conditions were met:
- ✅ Required number of approvals received
- ✅ CodeQL status check passed
- ✅ No force push attempted
- ✅ Conversations resolved (if enabled)
Key Insight
Security policy means a PR cannot merge even if every human involved agrees — if the automated checks fail, the merge is blocked. That’s stronger than any code review. A human can be persuaded; a policy check cannot.
The decision framework ensures rules are JUSTIFIED, not arbitrary. Each rule traces to a threat model row. A production repo needs strict rules; an experimental repo needs fewer. One size does NOT fit all.
[!TIP] Run
scripts/verify-exercise1.shto validate your Exercise 1 completion.