关于 repoward
repoward 是面向 AI Agent 的 GitHub MCP 服务,通过基于读取记录的写入限制和人工审批,降低不可信内容导致私有数据外泄的风险。它适用于 GitHub 访问路径受限于该服务的 CI Bot 和托管 Agent;项目目前处于 alpha 阶段,尚未经过独立安全审查。
主要功能
- 01按默认拒绝策略控制访问
- 02私有仓库读取后禁止公开仓库写入
- 03对不可信内容触发人工审批
- 04记录带哈希链的审计日志
- 05按主体和工具实施速率限制
- 06支持策略回放与短期 GitHub App 令牌
适用场景
- 01限制 CI Bot 对 GitHub 的读写操作
- 02阻止读取私有内容后向公开仓库写入
- 03要求审批后再执行合并或删除分支
- 04审查策略变更对历史决策的影响
MCP 相关
repoward 是通过 stdio 运行的 GitHub MCP 服务,向客户端提供问题、代码、文件和 CI 状态读取工具,以及受策略控制的分支、提交、拉取请求和评论操作。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
repoward
一个 GitHub MCP 服务,可阻止 AI Agent 按攻击者 issue 中的指示行事。
状态:alpha。 尚未经过独立安全审查。请阅读 威胁模型,尤其是关于它运行位置的部分。
问题
拥有 GitHub 访问权限的 Agent 可以读取恶意的公开 issue、读取私有仓库,并在公开拉取请求中发布其中的内容。每次调用单独来看似乎都获准,因此,按工具设置权限无法阻止这一连串操作。这就是 Invariant Labs 发布的攻击,针对的是使用 GitHub MCP 服务的 Agent;repoward 的测试套件会在真实仓库上重现该攻击。
流程防护
每次写入前,repoward 都会根据 GitHub 提供的信息,检查该 Agent 之前读取过的内容:
| 如果 Agent 读取过 | 那么 |
|---|---|
| 私有仓库中的任何内容 | 禁止向公开仓库写入 |
作者不属于 OWNER、MEMBER 和 COLLABORATOR 的 issue、拉取请求或评论,或作业日志 | 每次写入都需要人工审批 |
这不依赖模型识别攻击,也不扫描文本,因此,改写或编码数据都无法绕过它。状态会在重启后保留,且没有任何工具可以清除它。 用户可以:
uvx repoward flow status
uvx repoward flow reset
它无法做到的事:
- 它只能监控此服务的工具。Shell、浏览器或其他 MCP 服务仍然是绕过途径,因此它只保护 GitHub 访问权限和出站路由均受限于此服务的 Agent。
- 默认情况下,窗口结束后(60 分钟)数据仍可能保留在 Agent 的上下文中。请重启长时间运行的 Agent 以清除其上下文。
- 读取一个私有仓库后写入另一个私有仓库是允许的,除非设置
single_repo: true。 - 仓库文件和代码搜索结果不计为不可信输入,因为它们已由拥有写入权限的人员合并。
author_association是一种粗略信号,遭到入侵的可信账户仍会被视为可信。- 公开仓库的维护者会看到更多审批请求。这是默认设置的代价;
trusted_associations可配置。
功能
- **策略:**默认拒绝的 YAML 策略,包含角色、仓库允许列表和路径拒绝列表。请参阅 policy.example.yaml。
- **单一受控路径:**每次工具调用尝试(包括未知工具和无效参数)都会经过同一条处理管线并记录。
- **审计日志:**记录每项决策的哈希链,支持可选的 HMAC 和检查点。
- **速率限制:**按主体和工具分别限制。
- 读取工具:
github_list_issues、github_get_issue、github_search_code、github_get_file、ci_get_status和ci_get_job_logs。 - 写入工具:
github_create_branch、github_commit_files、github_create_pull_request和github_comment。写入操作只能针对带有已配置前缀(默认为agent/)的分支;拉取请求默认只能创建为草稿;提交大小受限;不会强制推送或重试;包含可识别凭据的文本会被拒绝。 - 危险工具:
github_merge_pull_request和github_delete_branch需要人工批准。 批准仅适用于某次指定提交上的一次确切调用,只能使用一次,且会过期。除非策略启用合并,否则合并功能处于关闭状态。 - **GitHub App 令牌(可选):**每次调用都会获得一个短期令牌,该令牌仅限于一个仓库,且只包含该工具所需的权限;GitHub 会自行强制执行这些限制。
- **将拉取请求审查作为批准(可选):**可以通过对指定提交进行真实的 GitHub 审查来批准合并。
- **拉取请求回执(可选):**提交状态,用于总结 Agent 的运行情况。
- **策略重放:**在部署策略修改之前,查看它会改变哪些历史决策。
- **不可信内容处理:**GitHub 用户的文本会在返回时包裹在带有随机 ID 的标签中,并会先移除不可见字符、隐藏的 HTML 注释和图片,再对机密信息进行脱敏。这是一种缓解措施,而非保证:模型仍可能受骗,因此上述规则会在模型之外强制执行。
适用对象
仅通过此服务访问 GitHub 的 Agent,例如 CI 机器人和托管 Agent。具有 shell 访问权限的 Agent 可以直接运行 git 或读取本地凭据,因此此服务不能作为它的安全边界。
安装
需要 uv。创建初始策略,同时会打印一段客户端配置供粘贴:
uvx repoward init --repo your-org/your-repo
初始策略为服务提供只读工具。添加 --role contributor 可启用写入工具。文件位于 ~/.repoward,或 REPOWARD_HOME 指向的位置。已有策略不会被覆盖。
请先运行此命令,因为没有策略时服务无法启动。然后将其添加到 MCP 客户端,并使用按GitHub 令牌中的说明设置的令牌。 避免提交包含真实令牌的配置。
Claude Code
claude mcp add --env GITHUB_TOKEN=<your-token> --transport stdio repoward -- uvx repoward serve
Claude Desktop
在 claude_desktop_config.json 中,路径为 Settings > Developer > Edit Config:
{
"mcpServers": {
"repoward": {
"command": "uvx",
"args": ["repoward", "serve"],
"env": { "GITHUB_TOKEN": "<your-token>" }
}
}
}
Cursor
在 ~/.cursor/mcp.json 中,从环境变量读取令牌:
{
"mcpServers": {
"repoward": {
"command": "uvx",
"args": ["repoward", "serve"],
"env": { "GITHUB_TOKEN": "${env:GITHUB_TOKEN}" }
}
}
}
VS Code
在 .vscode/mcp.json 或用户配置文件中的 mcp.json 里配置;系统会提示输入一次令牌:
{
"inputs": [
{ "type": "promptString", "id": "github-token", "description": "GitHub token", "password": true }
],
"servers": {
"repoward": {
"type": "stdio",
"command": "uvx",
"args": ["repoward", "serve"],
"env": { "GITHUB_TOKEN": "${input:github-token}" }
}
}
}
Codex
在 ~/.codex/config.toml 中,从环境变量转发令牌:
[mcp_servers.repoward]
command = "uvx"
args = ["repoward", "serve"]
env_vars = ["GITHUB_TOKEN"]
Gemini CLI
在 ~/.gemini/settings.json 中,从环境变量读取令牌:
{
"mcpServers": {
"repoward": {
"command": "uvx",
"args": ["repoward", "serve"],
"env": { "GITHUB_TOKEN": "$GITHUB_TOKEN" }
}
}
}
OpenCode
在项目中的 opencode.json 或 ~/.config/opencode/opencode.json 中,从环境变量读取令牌:
{
"mcp": {
"repoward": {
"type": "local",
"command": ["uvx", "repoward", "serve"],
"environment": { "GITHUB_TOKEN": "{env:GITHUB_TOKEN}" }
}
}
}
任何能够启动 stdio MCP 服务的其他客户端都运行相同命令:uvx repoward serve。
如果客户端报告服务连接失败,请在终端中运行 uvx repoward serve 查看原因。通常是因为尚未运行 repoward init,因此没有策略。
常用命令:
uvx repoward validate-policy
uvx repoward audit verify
批准高风险操作
需要批准的调用不会执行。它会返回一个 approval_id,由人员在终端中作出决定:
uvx repoward approvals list
uvx repoward approvals show <id>
uvx repoward approvals approve <id>
随后 Agent 会重复相同的调用。推送新的提交会更改调用所指向的提交,因此先前的批准不再适用;如果拉取请求的头部已变更,GitHub 也会拒绝合并。
通过拉取请求审查批准
也可以通过常规 GitHub 审查批准合并,因此批准者需要拥有自己的 GitHub 身份,而不只是能够访问这台计算机。在策略中启用此功能:
approvals:
github_review: { enabled: true, min_permission: write }
只有同时满足以下条件的审查才计为批准:批准的正是要合并的提交;审查者既不是拉取请求的作者,也不是此服务自身的身份;并且该用户拥有所需的访问权限。任何审查者尚未解决的变更请求都会阻止合并。合并时会重新执行每项检查。
这要求服务以 GitHub App 或独立机器人账户的身份运行。使用你自己的令牌时,你自己的审查会被拒绝。如果 Agent 能访问审查者的 GitHub 凭据,例如同一台计算机上已登录的 gh,此保护就会失效。
批准的局限性
如果 Agent 与此服务以相同的操作系统用户运行,并且能够使用 shell,它就可以运行这些命令或直接编辑数据库。要让批准机制真正有效,请使用单独的用户或容器运行服务并存放其文件。
GitHub 令牌
使用细粒度个人访问令牌,将其限制为你允许访问的仓库,并设置较短的有效期。选择 Only select repositories,以显示仓库权限。读取工具需要以下权限,且都只需只读:
| 权限 | 用途 |
|---|---|
| Metadata | GitHub 要求 |
| Contents | github_get_file |
| Issues | github_list_issues、github_get_issue |
| Pull requests | 这些工具中的拉取请求 |
| Actions | ci_get_status、ci_get_job_logs |
| Commit statuses | ci_get_status |
如需使用写入工具,还要授予 Contents、Issues 和 Pull requests 的读写权限。不要开启 Workflows 权限:即使你的策略允许修改该路径,GitHub 仍会拒绝对工作流文件的更改。
Contents: write 允许令牌向任意分支推送和合并,而 GitHub 无法将其限制在 agent/ 分支。此限制由本服务负责,因此还应使用规则集保护默认分支,要求通过拉取请求进行更改,并且不允许令牌所有者绕过规则。如果令牌泄露或服务器配置错误,规则集仍会生效。
需要了解的限制:
ci_get_status不会显示仅通过 Checks API 报告的第三方检查;GitHub 文档说明,细粒度令牌无法使用该 API。github_search_code会搜索默认分支以及大小不超过 384 KB 的文件。GitHub 为新仓库建立索引前,搜索可能没有结果;截至目前,该工具对真实结果的处理仅通过模拟响应进行过测试。github_get_file会拒绝符号链接、子模块、二进制文件以及大于 1 MB 的文件。它会遍历 Git 树,因此每次读取都会针对路径中的每个文件夹发起一个请求。- 如果 GitHub 将日志存储迁移到无法识别的主机,
ci_get_job_logs会安全失败。 github_merge_pull_request不支持合并队列或堆叠式拉取请求。github_delete_branch会先检查提交,再删除分支,因为 GitHub 不支持有条件删除,所以两步之间仍存在一个很短的时间窗口。
改用 GitHub App
GitHub App 会为每次工具调用颁发一个短期令牌,并将令牌限制在单个仓库及该工具所需的权限范围内。App 还有自己的身份,审核批准需要使用该身份。
注册 App 时关闭 Webhook,并仅授予所需权限;然后将其安装到策略中指定的仓库,并生成私钥:
| 权限 | 级别 | 用途 |
|---|---|---|
| Contents | 读取;写入工具需读写 | 文件、搜索、分支、提交、合并 |
| Issues | 读取;发表评论需读写 | 问题和评论 |
| Pull requests | 读取;写入工具需读写 | 拉取请求、审核和评论 |
| Actions | 读取 | CI 状态和作业日志 |
| Commit statuses | 读取;记录凭据需读写 | CI 状态和凭据 |
然后在策略中设置以下内容,并在服务器环境中将 GITHUB_APP_PRIVATE_KEY_FILE 指向该密钥:
github_auth:
mode: app
client_id: Iv1.your-client-id
installation_id: 12345678
私钥是长期有效的机密,可用于签发新令牌。请勿让 Agent 接触该密钥。contents: write 令牌仍可向该仓库的任意分支推送,因此仍应遵循上述规则集建议。
审计日志能证明什么
日志具备防篡改能力,但并非绝对防篡改。
| 攻击 | 普通哈希链 | 使用攻击者无法读取的 HMAC 密钥 | 使用主机外检查点 |
|---|---|---|---|
| 修改一条记录 | 可检测 | 可检测 | 可检测 |
| 修改一条记录并重新计算其后所有哈希 | 无法检测 | 可检测 | 可检测到最近的检查点 |
| 删除最新记录 | 无法检测 | 无法检测 | 可检测到最近的检查点 |
设置 REPOWARD_AUDIT_HMAC_KEY 以使用 HMAC,并将 repoward audit checkpoint 的输出
存放在服务器无法写入的位置。评论正文、标题、提交消息和文件内容不会以文本形式记录,而是记录其长度和哈希。写入期间发生崩溃会留下不完整的最后一行,之后将拒绝新的
写入,直到操作员修复日志。秘密信息的脱敏只能尽力而为。
拉取请求回执
Agent 的拉取请求审核者可以查看其运行情况。在策略中启用此功能:
receipts:
enabled: true
Agent 每次提交或创建拉取请求后,repoward 都会发布名为
repoward/receipt 的提交状态:
policy 4c16a8364c1a; audit seq 12-40; 2 denied; 1 escalated; flow labels: yes
其中包含策略的哈希、此服务启动以来的审计记录、被拒绝或需要审批的调用次数,以及是否有任何限制 Agent 写入的规则。如果流守卫处于关闭状态,flow labels 也可能显示
no、unknown 或 off。
- 需要
Commit statuses: write。GitHub App 会为每条回执使用一个仅持有此权限的单独令牌。个人令牌则需要自身具备此权限,因此每个工具的调用都会携带该权限。 - 回执会在发布前进行审计。如果发布失败,Agent 的提交或拉取请求仍会成功,并且会记录此次失败。
- **回执不能证明更改是通过 repoward 提交的。**任何拥有该权限的人都可以发布相同的状态,因此不要将其设为必需检查。
- **任何能查看仓库的人都能读取回执。**在公开仓库中,这包括被拒绝的调用次数以及是否设置了流标签,这些信息可能暗示 Agent 读取过私密内容。
尝试更改策略
对照审计日志重放策略,查看它会产生哪些不同结果:
repoward policy replay --policy new-policy.yaml
3 decisions: 1 unchanged, 1 changed, 1 unknown
Changed:
seq 2 github_comment org/app: allow -> approval_required (flow.untrusted_author)
Unknown (the log does not hold what this decision depends on):
seq 5 github_commit_files org/app: allow -> unknown (it depends on file sizes or text the log does not hold)
此操作不会联系 GitHub,也不会更改任何内容;如果日志未通过验证,则会拒绝处理。如果日志使用了 HMAC 密钥,可添加
--key-env 指定保存该密钥的变量名。
日志不会包含内容,因此有些问题无法确定。决策可能取决于文件的确切大小、隐藏文本中是否包含凭据,或未记录的流守卫事实,例如日志从未读取的仓库是否公开。对于这些情况,重放会报告 unknown,不会进行猜测。
重放会假设 Agent 会进行相同的调用。现实中,被拒绝的 Agent 会采取其他行动,因此应将报告视为策略影响的参考,而非预测。
贡献与安全
请参阅 CONTRIBUTING.md。请按照 SECURITY.md 中的说明报告漏洞。
许可证
MIT。这是一个独立项目,与 GitHub 无关联,也未获得 GitHub 的认可。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。