AriESQ/gh-safe-repo
GitHub: AriESQ/gh-safe-repo
一款 Python CLI 工具,用于一键创建具备安全默认配置的 GitHub 仓库,并支持审计修复现有仓库和预推送机密扫描。
Stars: 7 | Forks: 1
# gh-safe-repo
自动创建应用了安全默认设置的 GitHub 仓库。用一个命令替代创建后长达五分钟的设置检查清单。
```
gh-safe-repo create
```
分支保护、不可变标签、Dependabot、受限 Actions 权限、带推送保护的机密扫描,以及禁用的 wiki 和 projects —— 全部在你编写第一行代码前配置完毕。
gh-safe-repo 正处于密集开发阶段。它非常适合用于创建具有安全默认设置的新仓库。我正在努力完善 CLI 选项,以最大程度符合用户期望。在我正式发布版本并完善 CI/CD 之前,请预期会有破坏性变更。✌️
## 目录
- [为什么做这个](#why)
- [变更内容](#what-it-changes)
- [环境要求](#requirements)
- [安装](#installation)
- [快速开始](#quick-start)
- [CLI 参考](#cli-reference)
- [空运行 / 计划输出](#dry-run--plan-output)
- [修复模式(审计现有仓库)](#fix-mode-audit-existing-repos)
- [镜像仓库 (`--from`)](#mirroring-repos---from)
- [从本地目录创建仓库 (`--local`)](#creating-a-repo-from-a-local-directory---local)
- [预飞行安全扫描器](#pre-flight-security-scanner)
- [独立扫描](#standalone-scan)
- [抑制误报](#suppressing-false-positives)
- [配置](#configuration)
- [GitHub 套餐限制](#github-plan-limitations)
- [工作原理](#how-it-works)
- [开发](#development)
## 为什么做这个
GitHub 的默认仓库设置针对可发现性和灵活性进行了优化,而非安全性。每个新仓库都默认包含:
- 启用的 Wiki 和 Projects(即使未使用,也会增加攻击面)
- 允许合并提交(历史记录杂乱,但这不是主要问题)
- 无分支保护(任何有写入权限的人都可以直接推送到 `main`)
- 无 Dependabot 警报
- GitHub Actions 拥有仓库的写入权限
- Actions 被允许批准拉取请求
手动修复所有这些问题每个仓库需要花费数分钟,而且很容易遗忘。`gh-safe-repo` 通过一次操作应用一组既定且实用的默认设置,并提供计划预览,让你在任何更改发生前确切知道会改变什么。
## 变更内容
### 仓库设置
| 设置 | GitHub 默认值 | 安全默认值 | 备注 |
|---|---|---|---|
| 可见性 | Public | **Private** | 传递 `--public` 以覆盖 |
| Wiki | Enabled | **Disabled** | |
| Projects | Enabled | **Disabled** | |
| Issues | Enabled | Enabled | |
| 合并时删除分支 | Off | Off | 在配置中设为 `true` 以自动清理 |
| 允许合并提交 | On | On | 在配置中设为 `false` 以仅使用 squash |
| 允许 Squash 合并 | On | On | |
| 允许变基合并 | On | On | |
### GitHub Actions
| 设置 | GitHub 默认值 | 安全默认值 |
|---|---|---|
| 允许的 actions | All | **Selected** (GitHub 拥有 + 已验证创建者;可自定义) |
| 默认工作流权限 | Read/write | **Read-only** |
| Actions 可以批准 PR | Yes | **No** |
| 需要 SHA 固定 | No | **Yes** (工作流必须将 actions 固定到 commit SHA,而不是可变标签) |
| Fork PR 批准策略 | GitHub 的新手首次贡献者 | **所有外部贡献者** — 要求 fork PR 工作流在运行 CI 之前获得批准。选项:仅限全新的 GitHub 账户(GitHub 默认)、仓库的首次贡献者或所有 fork PR(最安全) |
### 分支保护(公开仓库,或付费套餐上的任何仓库)
| 规则 | 值 |
|---|---|
| 合并前需要拉取请求 | Yes |
| 需要批准的审查 | 1 |
| 推送时忽略旧的审查 | Yes |
| 需要解决对话 | Yes |
| 允许强制推送 | No |
| 允许分支删除 | No |
| 对管理员强制执行 | No (允许所有者工具推送) |
### 标签保护(公开仓库,或付费套餐上的任何仓库)
| 规则 | 值 |
|---|---|
| 受保护的标签模式 | `*` (所有标签;可配置) |
| 防止标签删除 | Yes |
| 防止标签更新(重写) | Yes |
| 管理员绕过 | Yes (与分支保护一致) |
标签保护仅使用 Rulesets API —— 没有“经典”标签保护的等效项。这仅适用于公开仓库或付费 GitHub 套餐(与分支保护的限制相同)。免费套餐的私有仓库将在计划输出中看到此项被跳过。
### 安全
| 功能 | 行为 |
|---|---|
| Dependabot alerts | Enabled (公开仓库 / 付费套餐) |
| Dependabot 安全更新 | Enabled (自动为有漏洞的依赖项打开 PR) |
| 机密扫描 | 公开仓库上自动开启;在付费私有套餐上启用 |
| 推送保护 | Enabled (阻止包含受支持机密的提交) |
| 私有漏洞报告 | Enabled (允许安全研究人员私下报告) |
| 依赖图 | 公开仓库上自动开启;私有仓库无 REST API (仅 UI) |
## 环境要求
- Python 3.8+
- 已安装并通过认证的 [`gh` CLI](https://cli.github.com/) (`gh auth login`),**或者**在你的环境中设置 `GITHUB_TOKEN`
- 用于从源码安装的 [`uv`](https://docs.astral.sh/uv/) (推荐)
- `truffleHog` v3 (可选 —— 由预飞行扫描器使用;从 PATH 自动检测,或通过 podman/docker 运行;如果两者都不可用,则回退到正则表达式)
## 安装
### 使用 uv 从源码安装 (推荐)
```
git clone https://github.com/your-username/gh-safe-repo
cd gh-safe-repo
uv tool install .
```
这会将 `gh-safe-repo` 安装到 uv 的工具环境中,并将其添加到你的 `PATH`。
### 不安装直接运行
```
git clone https://github.com/your-username/gh-safe-repo
cd gh-safe-repo
uv sync # creates .venv
./gh-safe-repo create
```
### 验证
```
gh-safe-repo --help
```
## 快速开始
```
# 使用所有安全默认值创建私有 repo
gh-safe-repo create
# 预览将要发生的操作 — 不做任何更改
gh-safe-repo create --dry-run
# 创建公共 repo(应用分支保护 + 安全扫描)
gh-safe-repo create --public
# 将现有 repo 镜像到新的私有 repo(带预扫描)
gh-safe-repo create --from
# 将私有 repo 镜像到新的公共 repo(带预扫描)
gh-safe-repo create --from --public
# 从本地目录创建 repo(带预扫描)
gh-safe-repo create --local ~/projects/myapp
# 同上,但设为公开(推送前应用分支保护)
gh-safe-repo create --local ~/projects/myapp --public
# 审计现有 repo 并应用任何缺失的安全默认值
gh-safe-repo fix
# 审计但不做更改
gh-safe-repo fix --dry-run
# 应用修复且无需确认提示(脚本/批处理使用)
gh-safe-repo fix --yes
# 在推送到任何位置前扫描本地 repo 的机密信息
gh-safe-repo scan .
gh-safe-repo scan ~/projects/myapp
```
## CLI 参考
```
gh-safe-repo create [OPTIONS]
gh-safe-repo fix [OPTIONS]
gh-safe-repo scan [OPTIONS]
```
所有与 GitHub 交互的命令都需要 `owner/repo` 格式(例如 `myuser/my-repo`。系统会根据你认证的 GitHub 账户验证所有者,以防止在多账户系统上出错。
### `create` — 创建新仓库
| 选项 | 描述 |
|---|---|
| `--public` | 创建为公开仓库 (默认: 私有) |
| `--local PATH` | 将本地目录中的代码推送到新仓库。首先运行预飞行扫描。与 `--from` 互斥。 |
| `--from OWNER/REPO` | 将现有仓库中的代码镜像到新仓库。运行预飞行扫描。与 `--local` 互斥。 |
| `--yes` / `-y` | 跳过确认提示并立即应用 (用于脚本/批处理) |
| `--dry-run` | 打印计划而不进行任何更改 |
| `--json` | 将计划作为 JSON 发送到 stdout,而不是 ANSI 表格 |
| `--config PATH` | 配置文件路径 (默认: `./gh-safe-repo.ini` 或 `~/.config/gh-safe-repo/gh-safe-repo.ini`) |
| `--debug` | 打印每个 API 调用和响应 |
### `fix` — 审计并修复现有仓库
| 选项 | 描述 |
|---|---|
| `--yes` / `-y` | 跳过确认提示并立即应用 (用于脚本/批处理) |
| `--dry-run` | 显示设置差异而不应用更改 |
| `--json` | 将计划作为 JSON 发送到 stdout,而不是 ANSI 表格 |
| `--config PATH` | 配置文件路径 (默认: `./gh-safe-repo.ini` 或 `~/.config/gh-safe-repo/gh-safe-repo.ini`) |
| `--debug` | 打印每个 API 调用和响应 |
### `scan` — 本地机密扫描
| 选项 | 描述 |
|---|---|
| `--config PATH` | 配置文件路径 (默认: `./gh-safe-repo.ini` 或 `~/.config/gh-safe-repo/gh-safe-repo.ini`) |
| `--debug` | 显示扫描器详细信息 |
如果没有关键发现,退出代码为 `0`,如果发现关键问题则为 `1`。
## 空运行 / 计划输出
`--dry-run` 准确显示 `gh-safe-repo` 将执行的操作,而不进行任何更改或 API 调用。在正式运行之前使用它。结合 `--json` 获取机器可读的计划输出:
```
gh-safe-repo create --dry-run --json
gh-safe-repo fix --dry-run --json
```
当 `--json` 处于活动状态时,计划将作为 JSON 对象写入 stdout,所有其他消息(进度、警告、“Dry run”页脚)都会发送到 stderr,因此输出对于管道或脚本来说是干净的。
```
$ gh-safe-repo create --dry-run
Plan for my-project (private)
Category Action Setting Value
──────────────────────────────────────────────────────────────────
Repository ADD repository my-project (private)
Repository ADD has_wiki false
Repository ADD has_projects false
Actions ADD default_workflow_permissions read
Actions ADD can_approve_pull_request_reviews false
Branch Protection SKIP branch_protection Not available for private repos on free plan
Security SKIP dependabot_alerts Not available for private repos on free plan
1 setting skipped (GitHub plan limitation).
Dry run — no changes made.
```
**操作颜色:**
| 操作 | 含义 |
|---|---|
| `ADD` (绿色) | 正在应用的新设置 |
| `UPDATE` (黄色) | 正在更改的现有设置 (审计模式) |
| `DELETE` (红色) | 正在移除的设置 |
| `SKIP` (暗淡) | 功能在你的套餐/可见性组合下不可用 |
**JSON 输出** (`--json`):
```
{
"changes": [
{ "type": "add", "category": "repository", "key": "has_wiki", "old": null, "new": false, "reason": null },
{ "type": "skip", "category": "branch_protection", "key": "branch_protection", "old": null, "new": null, "reason": "Not available for private repos on free plan" }
],
"summary": { "add": 5, "skip": 2 }
}
```
`summary` 仅包含计划中存在的类型。使用者应使用 `.get("delete", 0)` 等,而不是假设所有四个键都存在。
## 修复模式(审计现有仓库)
`fix` 将现有仓库的当前设置与安全默认值进行比较,并应用所有修正。不进行机密扫描 —— `fix` 纯粹关于仓库设置。
```
# 查看不合规项
gh-safe-repo fix --dry-run
# 应用缺失的安全默认值
gh-safe-repo fix
# 应用且无需确认提示(脚本/批处理使用)
gh-safe-repo fix --yes
```
修复模式:
1. 通过 GitHub API 获取每个设置的当前值
2. 与所需的安全默认值进行比较
3. 显示包含 `UPDATE`(针对已更改设置)和 `SKIP`(针对已处于所需值的设置)的计划表(空操作检测 —— 它从不进行没有任何更改的 API 调用)
4. 在应用前提示确认 (使用 `--yes` 跳过)
已经正确的设置会被静默跳过。仅显示并应用真正的更改。
## 镜像仓库 (`--from`)
`--from` 将现有仓库镜像到具有安全默认值的新仓库。它适用于私有和公开目标:
```
# 镜像到新的私有 repo(默认)
gh-safe-repo create --from
# 将私有 repo 镜像到新的公共 repo(风险最高的操作 — 详尽扫描)
gh-safe-repo create --from --public
```
**按顺序发生的事情:**
1. 源仓库在本地克隆 (完整克隆,无 `--depth`,以便 truffleHog 可以遍历完整的提交历史)
2. [预飞行安全扫描器](#pre-flight-security-scanner) 在本地克隆上运行
3. 你查看发现并确认 (或中止)
4. 创建一个新仓库 (默认为私有,或使用 `--public` 公开)
5. 应用安全默认值 (分支保护、安全扫描等)
6. 镜像完整历史:`git clone --mirror` + `git push --mirror`
7. 配置 Dependabot 和机密扫描
如果扫描发现问题并且你中止,则代码永远不会被复制到 GitHub。
## 从本地目录创建仓库 (`--local`)
`--local PATH` 是 `--from` 的本地到 GitHub 对应项。它创建一个新的 GitHub 仓库并从你机器上的目录推送代码。
```
gh-safe-repo create --local ~/projects/myapp
gh-safe-repo create --local ~/projects/myapp --public
```
**按顺序发生的事情:**
1. [预飞行安全扫描器](#pre-flight-security-scanner) 直接在本地目录上运行 (无需克隆)
2. 你查看发现并确认 (或中止)
3. 使用应用的安全默认值创建新仓库
4. **在任何代码推送之前**应用分支保护 (当 `--public` 时)
5. 推送代码:
- 如果 `PATH` 是 git 仓库:完整历史在本地克隆并使用 `push --all --tags` 推送 (所有分支和标签)
- 如果 `PATH` 是普通目录:文件在全新仓库中暂存并作为初始提交推送
- 如果 `PATH` 是空目录:不推送任何内容 (静默跳过)
6. 如果 `PATH` 是 git 仓库,则将 `origin` 添加到指向新 GitHub URL 的**原始**本地仓库,并配置当前分支的上游跟踪 —— 因此 `git push` 和 `git pull` 无需额外设置即可立即工作。
`--local` 和 `--from` 均适用于私有和公开仓库。它们互斥。
当 `PATH` 是 git 仓库时,本地默认分支 (通过 `git -C PATH symbolic-ref HEAD`) 用于定位分支保护规则,因此即使不是 `main`,保护也会落在正确的分支上。
## 预飞行安全扫描器
扫描器在本地运行,从不向 GitHub 发送代码。在每次推送前单独使用它,或者它会作为 `--from` 和 `--local` 工作流程的一部分自动运行。
### 独立扫描
```
# 扫描当前目录
gh-safe-repo scan .
# 扫描指定路径
gh-safe-repo scan ~/projects/myapp
```
如果没有关键发现,退出代码为 `0`,如果发现关键问题则为 `1` —— 因此它可以与其他命令清晰地组合:
```
gh-safe-repo scan . && git push
```
完整的 `[pre_scan]` 配置适用:`banned_strings`、`max_file_size_mb`、`trufflehog_mode` 等。
### 它检测什么
| 类别 | 严重性 | 示例 |
|---|---|---|
| 硬编码机密 | Critical | AWS 密钥 (`AKIA…`)、GitHub 令牌 (`ghp_…`、`github_pat_…`)、私钥、数据库 URL |
| 禁止的字符串 | Critical | 你配置的任何文字字符串 (用户名、内部主机名、代号) |
| AI 上下文文件 | Critical | `CLAUDE.md`, `AGENTS.md`, `.cursorrules`, `copilot-instructions.md`, `.cursor/` — 可能包含内部开发说明;git 历史可能比当前版本更敏感 |
| 电子邮件地址 | Warning | 工作树和 git 历史中的任何 `user@domain.tld` 模式 |
| 大文件 | Warning | 超过配置大小阈值 (默认: 100 MB) 的文件 |
| TODO/FIXME 注释 | Info | `# TODO`, `# FIXME`, `# HACK`, `# XXX` |
### 扫描器引擎
`gh-safe-repo` 使用三步发现链自动选择最佳可用扫描器:
1. **PATH 上的 truffleHog v3** — 运行 `trufflehog --version`,验证它是 v3,并使用它。v2 安装或无法识别的版本会打印警告并回退到第 2 步。
2. **podman 或 docker** — 如果未找到原生 truffleHog,扫描器将在容器 (`ghcr.io/trufflesecurity/trufflehog:latest`) 中使用 `podman run` 或 `docker run` 运行 truffleHog,并以只读方式挂载扫描路径到相同的绝对路径,以便 JSON 输出路径与原生运行相同。
3. **正则回退** — 如果原生安装和容器运行时都不可用,将打印警告并运行正则扫描器。它也总是在 truffleHog 之外运行以检查电子邮件和 TODO,并捕获 truffleHog 故意跳过的单独 key-ID 模式 (truffleHog 需要凭证对的两半,例如 AWS Key ID *和* Secret Access Key,然后才会标记发现)。
选定的扫描器显示在“Running pre-flight security scan...”标题和计划表的 SCAN 条目中,例如:
```
Running pre-flight security scan... (truffleHog v3.93.4)
Running pre-flight security scan... (truffleHog via podman)
Running pre-flight security scan... (regex only — see warning above)
```
容器路径支持的环境变量:`CONTAINER_RUNTIME` 以覆盖运行时选择 (例如 `CONTAINER_RUNTIME=docker`),以及 `TRUFFLEHOG_IMAGE` 以固定特定的镜像标签。
### 通过 podman 或 Docker 运行 truffleHog (无本地安装)
无需手动设置。`gh-safe-repo` 自动检测 podman 或 docker (上面的第 2 步),并在具有正确卷挂载的容器中运行 truffleHog。支持 `CONTAINER_RUNTIME` 和 `TRUFFLEHOG_IMAGE` 环境变量。
[`tools/`](tools/README.md) 中提供了 shell 包装器 (`tools/trufflehog`) 和用于构建固定本地镜像的 `Containerfile`,供希望在整个系统范围内使用基于容器的 truffleHog 或需要离线镜像的用户使用。
### 交互式审查
```
Pre-flight scan: my-private-project
CRITICAL my_private_project/config.py:12 AWS Access Key ID
[redacted]
WARNING my_private_project/setup.py:3 Email address
author_email="alice@example.com"
1 critical finding, 1 warning.
Critical findings detected. Continue anyway? [y/N]:
```
- **关键发现:** 默认为中止 (`N`)。你必须明确输入 `y` 才能继续。
- **仅警告:** 默认为继续 (`Y`)。按 Enter 继续 或输入 `n` 中止。
- **无发现:** 扫描静默完成,工作流程继续。
机密在输出中被编辑。电子邮件地址和 TODO 显示匹配的行。
### 扫描覆盖范围
构建产物目录 (`node_modules`, `__pycache__`, `.venv`, `venv`, `dist`, `build`) 默认被跳过以保持扫描快速。在 git 仓库中,此跳过是有条件的:在修剪目录之前,扫描器运行 `git ls-files -- ` 以检查内部是否有任何文件被跟踪。如果是,则正常扫描目录。
这意味着已提交的 `node_modules` 或 `dist` 树 —— 不常见,但会发生 —— 不会被静默遗漏。未提交的目录(正常情况)继续像以前一样被跳过。
当在克隆的源仓库中发现 SKIP_DIRS 子目录时,仍会打印警告,因为它们的存在可能表明提交的内容比预期的多。
### 抑制误报
两个配置键允许你抑制已知安全的发现,而无需禁用整个检查类别。
**`scan_exclude_paths`** — 完全跳过文件或目录。值是与相对文件路径匹配的换行/逗号分隔正则表达式模式。匹配的文件从每个检查中排除:机密、电子邮件、TODO、大文件和 AI 上下文文件检测。相同的模式也通过 `--exclude-paths` 传递给 truffleHog,因此无论哪个扫描器引擎处于活动状态,覆盖范围都是一致的。
```
[pre_flight_scan]
# 排除 GitHub API spec(示例 token)和所有测试 fixtures
scan_exclude_paths = docs/api\.github\.com\.json
tests/fixtures/
```
**`exclude_emails`** — 抑制特定地址或整个域的电子邮件发现。值是换行/逗号分隔的,不区分大小写。以 `@` 开头的条目匹配该域的所有电子邮件;否则,条目必须完全匹配完整地址。适用于工作树和 git 历史发现。
```
[pre_flight_scan]
# 屏蔽机器人地址和占位符域名
exclude_emails = action@github.com, noreply@github.com, @example.com
```
### 扫描器配置
```
[pre_flight_scan]
scan_for_secrets = true
scan_for_emails = true
scan_for_todos = true
max_file_size_mb = 100
# 扫描 git 历史记录中的电子邮件地址(需要 scan_for_emails = true)
# scan_email_history = true
# 扫描器选择:auto | native | docker | off
# auto — 尝试原生 truffleHog,回退到容器,然后是 regex(默认)
# native — 仅原生 truffleHog;无容器回退
# docker — 仅容器;跳过原生 PATH 检查
# off — 仅 regex 扫描器,不尝试 truffleHog
# trufflehog_mode = auto
# 将 AI 上下文文件(CLAUDE.md, AGENTS.md, .cursorrules 等)标记为关键发现。
# 其 git 历史记录可能包含比当前版本更敏感的内容。
# warn_ai_context_files = true
# 标记为关键发现的字面字符串(不区分大小写)。
# 逗号分隔或每行一个(续行必须缩进)。
# banned_strings = secret
# password
# credential
# 从所有扫描检查中排除文件/目录(regex 模式,逗号/换行分隔)。
# 相同的模式通过 --exclude-paths 传递给 truffleHog。
# scan_exclude_paths = docs/api\.github\.com\.json
# tests/fixtures/
# 屏蔽特定地址或整个域名的电子邮件发现(不区分大小写)。
# 以 @ 开头的条目匹配该域下的所有电子邮件;否则为精确地址匹配。
# exclude_emails = action@github.com, noreply@github.com, @example.com
```
当发现禁止的字符串或 AI 上下文文件时,扫描器会打印一个可直接运行的 `git filter-repo` 命令,以便在重新运行之前从源仓库的历史记录中删除它们。
## 配置
`gh-safe-repo` 按此顺序查找配置 (第一个匹配获胜):
1. **`--config PATH`** — 显式覆盖
2. **`./gh-safe-repo.ini`** — 当前工作目录
3. **`$XDG_CONFIG_HOME/gh-safe-repo/gh-safe-repo.ini`** — 当 `$XDG_CONFIG_HOME` 未设置时默认为 `~/.config`
所有值都有安全默认值 —— 开始使用不需要配置文件。
仓库中包含一个完全注释的示例配置 `gh-safe-repo.ini.example`。复制它以开始:
```
# 用户级配置 (XDG)
mkdir -p "${XDG_CONFIG_HOME:-$HOME/.config}/gh-safe-repo"
cp gh-safe-repo.ini.example "${XDG_CONFIG_HOME:-$HOME/.config}/gh-safe-repo/gh-safe-repo.ini"
# 或项目级配置(当前目录)
cp gh-safe-repo.ini.example ./gh-safe-repo.ini
```
### 完整配置参考
```
[repo]
# 新 repo 是否默认为私有
private = true
# 禁用未使用时会造成混乱的功能
has_wiki = false
has_projects = false
has_issues = true
# 合并后自动删除 head 分支(默认:off,与 GitHub 一致)
delete_branch_on_merge = false
# 合并策略(默认全部启用,与 GitHub 一致)
# 设置 allow_merge_commit = false 以实现仅 squash 工作流
allow_squash_merge = true
allow_merge_commit = true
allow_rebase_merge = true
# 不要使用 README 初始化 — 保持远程为空以便无缝推送
auto_init = false
[actions]
# 允许运行哪些 actions:all | local_only | selected
allowed_actions = selected
# 当 allowed_actions = selected 时,控制允许哪些外部 actions:
github_owned_allowed = true # actions maintained by GitHub (e.g. actions/checkout)
verified_allowed = true # actions from Marketplace verified creators
# patterns_allowed = myorg/* # 逗号分隔的允许列表(支持通配符)
# 最小权限原则:默认只读
# 选项:read | write
default_workflow_permissions = read
# 防止 Actions 自我批准 pull requests
can_approve_pull_request_reviews = false
# 要求 workflows 将 actions 固定到特定的 commit SHA 而不是可变 tag
sha_pinning_required = true
[branch_protection]
# 应用于任何付费计划的公共 repo,以及付费计划的私有 repo。
# 要保护的分支
protected_branch = main
# 合并前需要 pull request
require_pull_request = true
# 所需的批准数量
required_approving_reviews = 1
# 推送新 commits 时取消现有的批准
dismiss_stale_reviews = true
# 合并前要求解决所有审查评论
require_conversation_resolution = true
# 不对管理员强制执行规则
# false = repo 所有者仍可直接推送(--from mirror 工作流需要)
enforce_admins = false
# 阻止强制推送
allow_force_pushes = false
# 阻止分支删除
allow_deletions = false
# 使用 Rulesets API 而非经典分支保护
# 规则相同,但支持绕过参与者,且是现代 GitHub API
# use_rulesets = false
[tag_protection]
# 通过 Rulesets API 实现不可变 tags。
# 仅适用于公共 repo 或付费 GitHub 计划(与分支保护限制相同)。
# 要保护的 tags 的 Glob 模式 — 逗号分隔。
protected_tags = *
# 防止删除匹配的 tags (git tag -d / git push --delete)
prevent_tag_deletion = true
# 防止重写匹配的 tags (git tag -f / force-push)
prevent_tag_update = true
[security]
# 启用 Dependabot 漏洞警报
enable_dependabot_alerts = true
# 自动打开 PR 以修复易受攻击的依赖项
enable_dependabot_security_updates = true
# 允许安全研究人员私下报告漏洞
enable_private_vulnerability_reporting = true
# 阻止包含受支持机密的 commits
enable_secret_scanning_push_protection = true
# 注意:以下功能没有 REST API,必须通过 UI 或 dependabot.yml 配置:
# - 分组安全更新:使用 dependabot.yml groups 配合 applies-to: security-updates
# - 自动依赖提交:通过 repo 设置 UI 启用
# - 依赖图:公共 repo 自动启用;私有 repo 通过 UI 启用
[pre_flight_scan]
scan_for_secrets = true
scan_for_emails = true
scan_for_todos = true
# 标记超过此阈值的文件
max_file_size_mb = 100
# 扫描 git 历史记录中的电子邮件地址(需要 scan_for_emails = true)
# scan_email_history = true
# 扫描器选择:auto | native | docker | off
# auto = 尝试原生 truffleHog,回退到容器,然后是 regex
# native = 仅原生 PATH
# docker = 仅容器
# off = 仅 regex
# trufflehog_mode = auto
# 将 AI 上下文文件(CLAUDE.md, AGENTS.md, .cursorrules 等)标记为关键发现。
# warn_ai_context_files = true
# 标记为关键发现的字面字符串(不区分大小写)。
# 逗号分隔,或每行一个并使用续行缩进。
# banned_strings = secret
# password
# credential
# 从所有扫描检查中排除文件/目录(regex 模式,逗号/换行分隔)。
# 传递给 truffleHog via --exclude-paths 并应用于 regex 遍历。
# scan_exclude_paths = docs/api\.github\.com\.json
# tests/fixtures/
# 屏蔽特定地址或整个域名的电子邮件发现(不区分大小写)。
# 以 @ 开头的条目匹配该域下的所有电子邮件;否则为精确地址匹配。
# exclude_emails = action@github.com, noreply@github.com, @example.com
```
## GitHub 套餐限制
某些功能仅根据仓库可见性和你的 GitHub 套餐可用。
| 功能 | 免费 + 公开 | 免费 + 私有 | 专业版/团队 + 私有 |
|---|:---:|:---:|:---:|
| 分支保护 / Rulesets | Yes | No | Yes |
| 标签保护 | Yes | No | Yes |
| Dependabot alerts | Yes | No | Yes |
| Dependabot 安全更新 | Yes | No | Yes |
| 机密扫描 | Auto | No | Yes |
| 推送保护 | Yes | No | Yes |
| 私有漏洞报告 | Yes | Yes | Yes |
| 依赖图 | Auto | No | Yes |
`gh-safe-repo` 在运行时检测你的套餐级别和仓库可见性。不可用的功能在计划输出中显示为 `SKIP`,并附带明确的原因 —— 该工具从不静默失败。
## 工作原理
```
gh-safe-repo create
│
├─ Parse owner/repo, validate owner matches authenticated user
├─ Load config (./gh-safe-repo.ini or $XDG_CONFIG_HOME/gh-safe-repo/gh-safe-repo.ini)
├─ Apply CLI flag overrides (--public, etc.)
├─ Authenticate via gh CLI or GITHUB_TOKEN
├─ GET /user → owner login + plan level (single cached call)
│
├─ Build plan (each plugin compares desired vs. current state)
│ ├─ RepositoryPlugin → repo creation + basic settings
│ ├─ ActionsPlugin → allowed actions, workflow permissions, SHA pinning
│ ├─ BranchProtectionPlugin → classic or Rulesets API
│ ├─ SecurityPlugin → Dependabot, secret scanning, push protection, private vuln reporting
│ └─ TagProtectionPlugin → immutable tags via Rulesets API
│
├─ Print plan table
│
└─ Apply (unless --dry-run)
├─ POST /user/repos
├─ PATCH /repos/{owner}/{repo} (settings)
├─ PUT /repos/{owner}/{repo}/actions/permissions/workflow
├─ PUT /repos/{owner}/{repo}/branches/main/protection
│ or POST /repos/{owner}/{repo}/rulesets (if use_rulesets = true)
├─ PUT /repos/{owner}/{repo}/vulnerability-alerts
├─ PUT /repos/{owner}/{repo}/automated-security-fixes
├─ PUT /repos/{owner}/{repo}/private-vulnerability-reporting
├─ PATCH /repos/{owner}/{repo} (security_and_analysis: push protection)
├─ POST /repos/{owner}/{repo}/rulesets (tag protection ruleset)
├─ git clone --mirror + git push --mirror (if --from)
└─ git clone + git push --all --tags (if --local, git repo)
or git init + add -A + commit + push (if --local, plain dir)
```
### 插件架构
每个设置类别都是一个自包含的插件类 (`gh_safe_repo/plugins/`)。每个插件:
1. 通过 GitHub API 获取当前状态
2. 与来自配置的所需状态进行比较
3. 返回一个 `Plan` (`Change` 对象列表: ADD / UPDATE / DELETE / SKIP)
4. 仅应用真正的更改 —— 空操作不进行 API 调用
这意味着审计模式和创建模式使用相同的计划/应用路径。唯一的区别是从现有仓库获取当前状态还是假定为 GitHub 默认值。
### 认证
1. `gh auth token` — 首选;使用 `gh auth login` 设置的任何内容
2. `GITHUB_TOKEN` 环境变量 — CI/CD 回退
3. 如果两者都不可用则报错
令牌作为 `GH_TOKEN` 在子进程环境中传递给子 `gh api` 进程,并且从不被记录。
### API 方法
所有 GitHub API 调用都通过 `subprocess` 经过 `gh api`。这使得认证完全保留在 `gh` CLI 中 —— 无令牌管理代码,无 OAuth 流程,无 PyGithub 版本固定。JSON 请求体通过 `--input -` (stdin) 传递,而不是 `--field` 标志。
## 开发
```
# Clone 并设置
git clone https://github.com/your-username/gh-safe-repo
cd gh-safe-repo
uv sync # creates .venv, installs pytest
# 运行测试
uv run pytest tests/ -v
# 直接运行工具(不安装)
./gh-safe-repo create --dry-run
# 全局安装(获取当前源代码)
uv tool install .
```
有关测试文件描述、模拟约定以及如何添加新测试,请参阅 [`tests/README.md`](tests/README.md)。
### 项目结构
```
gh-safe-repo/
├── gh-safe-repo # Thin launcher (entry point for direct use)
├── gh_safe_repo/ # Package — see gh_safe_repo/README.md for internals
│ ├── cli.py # Subparser dispatch (create, fix, scan)
│ ├── commands/ # Subcommand implementations
│ │ ├── _common.py # Shared helpers, CLIContext, plan formatting
│ │ ├── create.py # create subcommand
│ │ ├── fix.py # fix subcommand
│ │ └── scan.py # scan subcommand
│ └── plugins/ # Settings plugins (one per category)
├── pyproject.toml # Build config, entry points
├── gh-safe-repo.ini.example # Fully annotated example config
└── tests/
```
有关模块映射、插件架构和添加新设置的指南,请参阅 [`gh_safe_repo/README.md`](gh_safe_repo/README.md)。
### 依赖策略
**没有运行时依赖**。一切都使用 Python 标准库 (`argparse`, `configparser`, `subprocess`, `json`, `re`)。未经讨论,不要添加第三方包。
`pytest` 是唯一的开发依赖项,在 `pyproject.toml` 中声明为 UV 原生 `[dependency-groups]` 条目。
## 先前艺术
这些项目在设计期间进行了研究,并影响了 `gh-safe-repo` 的架构。它们是具有不同范围和用户模型的独特工具 —— 有关如何调整模式的详细技术说明,请参阅 [docs/LEARNINGS.md](docs/LEARNINGS.md)。
- **[github/safe-settings](https://github.com/github/safe-settings)** — 组织级 GitHub App (Node.js/Probot),从中央配置强制执行仓库设置。插件架构模式(每个设置类别一个类,获取 → 比较 → 应用)和 `mergeDeep` 比较方法的来源。
- **[repository-settings/app](https://github.com/repository-settings/app)** — safe-settings 的更简单的单仓库变体,也是 Node.js/Probot。为 `Diffable` 基础插件模式提供了更清晰的参考。
- **[nicholasgasior/gh-repo-settings](https://github.com/nicholasgasior/gh-repo-settings)** — 用 Go 编写的 CLI 扩展,具有 `plan`/`apply` 工作流程。`gh api` 子进程包装器模式和空运行计划输出设计的主要灵感来源。
标签:CCS 2025, CI/CD安全, Dependabot, DevOps工具, DevSecOps, GitHub Actions, Golang-esque CLI, Llama, Python, SAST, Secrets Detection, StruQ, 上游代理, 代码仓库管理, 分支保护, 安全合规, 无后门, 盲注攻击, 网络代理, 自动化配置, 自动笔记, 请求拦截, 软件开发工具包, 逆向工具, 防御性安全, 预提交扫描, 默认安全