# 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.

```text
GitHub repo security settings checklist
```

## Use 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.

```bash
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.

```bash
gh api repos/[owner]/[repo] --jq '.security_and_analysis'
```

Expected: secret_scanning and secret_scanning_push_protection 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.

```bash
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.

```bash
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.

```bash
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/pst_GEv9bO_M0FXNqg9AzF1Ptg
