Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions skills/github-project/references/repo-bootstrap.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,74 @@ GitHub branch protection cannot block on *requested-but-pending* reviews (Copilo
| 3 | Repo not found or no API access |
| 4 | Default branch ref does not yet exist (empty repo — push first) |
| 5 | `--from-current-checks`: no completed CI run on default branch |

## Actions & Security Hardening (apply alongside branch protection)

`init-branch-protection.sh` covers branch protection only. A repo's **Actions permissions** and **security toggles** are a separate API surface that GitHub ships at insecure defaults: `GITHUB_TOKEN` read/write, workflows may approve PRs, no SHA-pinning, all actions allowed. Apply these in the same bootstrap step.

> **Org members:** if the org already sets these at `orgs/ORG/actions/permissions` (see [`org-security-settings.md`](org-security-settings.md)) the repo inherits them — the per-repo commands below are for standalone repos, or repos under a permissive org default. (Endpoint shapes verified against [AriESQ/gh-safe-repo](https://github.com/AriESQ/gh-safe-repo).)

### Workflow token: read-only, no self-approval

```bash
gh api repos/OWNER/REPO/actions/permissions/workflow -X PUT \
-f default_workflow_permissions=read \
-F can_approve_pull_request_reviews=false
```

- `default_workflow_permissions=read` — `GITHUB_TOKEN` defaults to read-only; jobs needing write opt in at job level (see [`security-config.md`](security-config.md) § Least-Privilege Workflow Permissions). GitHub's default is `write`.
- `can_approve_pull_request_reviews=false` — stops a workflow from approving its own PRs (which would otherwise satisfy `required_approving_review_count` without a real reviewer).

### Restrict which actions can run + require SHA pinning

```bash
# Allow only GitHub-owned + verified-creator actions, and require SHA pins.
# enabled=true is REQUIRED in this body or the PUT returns 422.
gh api repos/OWNER/REPO/actions/permissions -X PUT \
-F enabled=true \
-f allowed_actions=selected \
-F sha_pinning_required=true
Comment thread
CybotTM marked this conversation as resolved.

# Allow-list — only valid AFTER allowed_actions=selected (apply order matters):
gh api repos/OWNER/REPO/actions/permissions/selected-actions -X PUT --input - <<'EOF'
{ "github_owned_allowed": true, "verified_allowed": true, "patterns_allowed": ["OWNER/*"] }
EOF
```

- `sha_pinning_required=true` is the repo-level twin of the org setting — workflows must pin actions to a full commit SHA. Reusable-workflow `@main`/`@vX` refs are exempt; see [`reusable-workflow-security.md`](reusable-workflow-security.md).
Comment thread
CybotTM marked this conversation as resolved.

### Fork-PR approval (public repos only)

Holds fork PRs — and the Actions minutes they would burn — until a maintainer approves. The endpoint exists only for public repos (returns null/404 on private):

```bash
gh api repos/OWNER/REPO/actions/permissions/fork-pr-contributor-approval -X PUT \
-f approval_policy=all_external_contributors
```

| `approval_policy` | Who needs approval before fork-PR workflows run |
|---|---|
| `first_time_contributors_new_to_github` | Brand-new GitHub accounts only (GitHub default) |
| `first_time_contributors` | First-time contributors to this repo |
| `all_external_contributors` | Every external contributor (safest) |

### Security toggles

```bash
# Dependabot alerts + automatic security-update PRs (bodyless PUTs)
gh api repos/OWNER/REPO/vulnerability-alerts -X PUT
gh api repos/OWNER/REPO/automated-security-fixes -X PUT

# Private vulnerability reporting — a STANDALONE PUT, not a security_and_analysis field
gh api repos/OWNER/REPO/private-vulnerability-reporting -X PUT

# Secret-scanning push protection (paid/private; auto-on for public).
# secret_scanning MUST be sent alongside push protection or the PATCH is silently ignored.
gh api repos/OWNER/REPO -X PATCH --input - <<'EOF'
{ "security_and_analysis": {
"secret_scanning": { "status": "enabled" },
"secret_scanning_push_protection": { "status": "enabled" } } }
EOF
```

> **Plan gate:** rulesets, Dependabot, and secret scanning are unavailable on **free-plan private** repos — the API returns 403 (you can't even `GET .../rulesets`). Public repos and paid plans (the Netresearch org) get all of it. If a toggle 403s, check the repo's plan/visibility before debugging the call. More scripting quirks: [`security-config.md`](security-config.md) § GitHub Security API.
10 changes: 10 additions & 0 deletions skills/github-project/references/security-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -341,3 +341,13 @@ If your Scorecard score is low, check these common issues:
| Pinned-Dependencies | All actions pinned to full SHA | Pin with `uses: action@SHA # vX.Y.Z` comment. Use `pin-github-action` tool for batch pinning (see [`org-security-settings.md`](./org-security-settings.md)). Note: composite action sub-actions must also be pinned/allowed (see [Composite Action Sub-Action Allow-List Gotcha](#composite-action-sub-action-allow-list-gotcha)). For transitive dependency risks, see [`reusable-workflow-security.md`](./reusable-workflow-security.md). |
| Code-Review | PRs reviewed before merge | Auto-approve + `required_approving_review_count >= 1` satisfies this |
| SAST | Static analysis enabled | CodeQL workflow (see above) |

## GitHub Security API: Scripting Gotchas

Endpoint quirks when scripting repo security settings, verified against the GitHub REST spec (source: [AriESQ/gh-safe-repo](https://github.com/AriESQ/gh-safe-repo)). For the bootstrap commands these annotate, see [`repo-bootstrap.md`](repo-bootstrap.md) § Actions & Security Hardening.

- **`PATCH /repos/{o}/{r}` can 404 immediately after `POST /user/repos`.** Repo creation is eventually consistent — the object exists but isn't routable for settings updates for a brief window. Retry the PATCH with backoff (e.g. 1s/2s/4s) when it directly follows a create; standalone PATCHes don't need it.
- **"Enabled?" probe status is not uniformly 204.** `GET .../vulnerability-alerts` returns **204** when enabled, but `GET .../private-vulnerability-reporting` and `GET .../automated-security-fixes` return **200**. Test the **2xx range** (`200 <= status < 300`), not `== 204`, or the probe is wrong for half the endpoints.
Comment thread
CybotTM marked this conversation as resolved.
- **`secret_scanning_push_protection` is silently ignored unless `secret_scanning` is in the same PATCH.** Send both keys in the `security_and_analysis` body together.
- **Grouped security updates, automatic dependency submission, and dependency graph have no per-repo REST API.** They exist only as org-level code-security configuration fields (`/orgs/{org}/code-security/configurations`) and in the UI. A `PATCH /repos security_and_analysis` with `dependency_graph_autosubmit_action` returns 200 but is silently ignored. For per-repo grouped security updates use `dependabot.yml` groups (see [`dependency-management.md`](dependency-management.md)).
- **Free-plan private repos 403 on all ruleset / Dependabot / secret-scanning APIs** — you can't even `GET .../rulesets`. Public repos and paid plans (the Netresearch org) are unaffected.
Loading