GitHub repo security settings checklist
A checklist skill for GitHub repo security settings: branch protection, required reviews, secret scanning with push protection, Dependabot alerts, code scanning, and token permissions. Use when securing a new repo, auditing org repos, or meeting a compliance baseline. Triggers: 'github repo security checklist', 'branch protection setup', 'enable secret scanning', 'github security settings'. Not for: GitHub Actions pipeline hardening, org-level SSO config, general git workflows.
GitHub repo security settings checklist
TL;DR
Lock the default branch (require PRs, reviews, and status checks; no force pushes), turn on secret scanning with push protection, enable Dependabot alerts and security updates, add code scanning, and restrict token permissions. These are checkboxes, not projects; a new repo should get all of them in ten minutes.
GitHub repo security settings checklistUse this when
- You are creating or inheriting a repo and want it secure by default
- You are auditing repos across an org for baseline compliance
- A security review asks for the repo's protection settings
- You need a checklist to hand to team leads
Not for this skill when
- You need CI pipeline supply-chain hardening (different skill)
- The task is org-level SSO or identity config
- You are learning git branching workflows in general
Steps
1. Protect the default branch
Require pull requests, at least one review, passing status checks, and no force pushes or deletions. Direct pushes to main should be impossible for humans and bots alike.
gh api repos/[owner]/[repo]/branches/main/protection --jq '{required_pull_request_reviews, enforce_admins, allow_force_pushes}'Expected: JSON showing reviews required, admins included, force pushes disallowed. If the endpoint 404s, there is no protection at all; add a ruleset in Settings, then Rules, then Rulesets, covering the default branch.
2. Turn on secret scanning and push protection
Secret scanning finds committed credentials; push protection blocks the push before the secret lands. The second one is the control that actually prevents incidents.
gh api repos/[owner]/[repo] --jq '.security_and_analysis'Expected: secretscanning and secretscanningpushprotection both showing enabled. Find them under Settings, then Code security; push protection occasionally blocks a legitimate-looking string, and bypassing it should require a documented reason, not a habit.
3. Enable Dependabot alerts and security updates
Alerts tell you about vulnerable dependencies; security updates open the fix PRs automatically. Alerts without updates is just a growing backlog nobody reads.
gh api repos/[owner]/[repo]/dependabot/alerts --jq 'length'Expected: a count of open alerts (zero on a fresh repo, a number on an old one). Enable both under Settings, then Code security; triage the backlog oldest-first by severity, not newest-first.
4. Add code scanning with CodeQL
Default setup scans PRs for the common vulnerability patterns and comments findings right on the diff. It is free for public repos and cheap for private ones relative to the bugs it catches.
gh api repos/[owner]/[repo]/code-scanning/alerts --jq '[.[] | {rule: .rule.id, state: .state}] | .[0:5]'Expected: a list of recent alerts or an empty array on a clean repo. If code scanning was never enabled, turn on the default setup and expect a first-scan backlog; triage it like the Dependabot backlog.
5. Restrict tokens, require 2FA, and set up private vulnerability reporting
Set the workflow token to read-only by default at the org level, require 2FA for the org (see the MFA skill), and enable private vulnerability reporting so researchers can reach you without opening a public issue.
gh api orgs/[org-name] --jq '{two_factor_requirement_enabled}' && gh api repos/[owner]/[repo] --jq '.security_and_analysis.secret_scanning_non_provider_patterns.status // "n/a"'Expected: 2FA required true, and non-provider secret patterns enabled if available. Private vulnerability reporting lives under Settings, then Code security; add a SECURITY.md pointing reporters at it so they dont file public issues.
Variant: github branch protection ruleset setup
Rulesets are the modern way: one ruleset can cover main plus release branches across repos. Require PRs, reviews, status checks, signed commits if your org wants them, and block force pushes. Apply at the org level so new repos inherit it automatically.
Variant: enable secret scanning push protection org-wide
Do it at the org level under Code security, not repo by repo. Expect an initial wave of blocked pushes from people with secrets in local history; thats the control working, and each block is a coaching moment, not a punishment.
Variant: repo security audit with gh cli
Script the five checks above across all org repos and output the non-compliant ones. Run it monthly; repos drift as settings get toggled during incidents and never restored.
Why this happens
GitHub's defaults favor getting started fast: unprotected branches, permissive tokens, optional scanning. Every default assumes a side project, not production code with credentials and dependencies. The checklist exists because nobody remembers to revisit defaults, and attackers specifically look for repos where nobody did.
Edge cases and pitfalls
- Admin bypass habits: enforce_admins must include admins or the protection is decorative; admins pushing directly "just this once" becomes the norm.
- Status check sprawl: requiring every check ever added slows merges; require the security-relevant ones and keep the list pruned.
- Push protection false positives: long random strings in tests trip it; use clearly fake example values in fixtures instead of bypassing.
- Dependabot alert fatigue: hundreds of alerts get ignored wholesale; enable grouped security updates so fixes arrive as few PRs, not hundreds.
- Fork PRs and secrets: fork PRs cant access secrets by design; workflows that need secrets for external contributors need explicit label-gated approval.
- Archived and dormant repos: they still show up in audits and still get scanned; archive what is dead so the checklist applies to what is alive.
Provenance
Resolved from the public thread: https://vectle.com/posts/pstGEv9bOM0FXNqg9AzF1Ptg
Maintainer review
No maintainer verification is recorded for this version.
This records the version a maintainer checked. It does not assert that the version is the latest upstream release.