feat(gitea): establish MCP security baseline (phase 0)
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
# Local Gitea MCP credentials and diagnostics must never enter Git.
|
||||||
|
.codex/gitea.env
|
||||||
|
gitea.env
|
||||||
|
gitea.env.*
|
||||||
|
!gitea.env.example
|
||||||
|
*.stderr.log
|
||||||
|
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
| [`tasks.md`](tasks.md) | 本样本库自身的维护任务列表 |
|
| [`tasks.md`](tasks.md) | 本样本库自身的维护任务列表 |
|
||||||
| [`init.sh`](init.sh) | 标准启动与验证入口脚本(Unix shell / WSL / Git Bash),统一安装 + 验证 + 打印启动命令 |
|
| [`init.sh`](init.sh) | 标准启动与验证入口脚本(Unix shell / WSL / Git Bash),统一安装 + 验证 + 打印启动命令 |
|
||||||
| [`init.ps1`](init.ps1) | 标准启动与验证入口脚本(Windows 原生 PowerShell),与 `init.sh` 等价,按操作系统二选一 |
|
| [`init.ps1`](init.ps1) | 标准启动与验证入口脚本(Windows 原生 PowerShell),与 `init.sh` 等价,按操作系统二选一 |
|
||||||
|
| [`gitea.env.example`](gitea.env.example) | Gitea MCP 本机私有配置示例;复制后替换,真实文件不得入库 |
|
||||||
| [`progress.md`](progress.md) | 可选:历史归档 / 项目级大事记;执行记录默认写各任务文件 |
|
| [`progress.md`](progress.md) | 可选:历史归档 / 项目级大事记;执行记录默认写各任务文件 |
|
||||||
| [`docs/README.md`](docs/README.md) | 文档导航,总览所有项目文档 |
|
| [`docs/README.md`](docs/README.md) | 文档导航,总览所有项目文档 |
|
||||||
| [`docs/00-ai-start-here.md`](docs/00-ai-start-here.md) | AI coding agent 的入口、阅读顺序、任务领取规则 |
|
| [`docs/00-ai-start-here.md`](docs/00-ai-start-here.md) | AI coding agent 的入口、阅读顺序、任务领取规则 |
|
||||||
@@ -28,6 +29,7 @@
|
|||||||
| [`docs/routes.md`](docs/routes.md) | 页面路由、组件归属、导航规则 |
|
| [`docs/routes.md`](docs/routes.md) | 页面路由、组件归属、导航规则 |
|
||||||
| [`docs/clean-state-checklist.md`](docs/clean-state-checklist.md) | 会话收尾检查清单,保证下一轮无需人工修复即可开工 |
|
| [`docs/clean-state-checklist.md`](docs/clean-state-checklist.md) | 会话收尾检查清单,保证下一轮无需人工修复即可开工 |
|
||||||
| [`docs/current-state.md`](docs/current-state.md) | 当前实现状态快照,防止计划和代码现实脱节 |
|
| [`docs/current-state.md`](docs/current-state.md) | 当前实现状态快照,防止计划和代码现实脱节 |
|
||||||
|
| [`docs/gitea-mcp.md`](docs/gitea-mcp.md) | 可选:Gitea MCP 共享文档与任务协调接入、安全和降级规则 |
|
||||||
| [`docs/method-map.md`](docs/method-map.md) | 失败模式 → 首要修复 → 工件的诊断 / 导航对照表 |
|
| [`docs/method-map.md`](docs/method-map.md) | 失败模式 → 首要修复 → 工件的诊断 / 导航对照表 |
|
||||||
| [`docs/evaluator-rubric.md`](docs/evaluator-rubric.md) | 单次会话输出的结构化评审评分表 |
|
| [`docs/evaluator-rubric.md`](docs/evaluator-rubric.md) | 单次会话输出的结构化评审评分表 |
|
||||||
| [`docs/quality-document.md`](docs/quality-document.md) | 代码库长期健康度追踪(产品域 × 架构层评级) |
|
| [`docs/quality-document.md`](docs/quality-document.md) | 代码库长期健康度追踪(产品域 × 架构层评级) |
|
||||||
@@ -67,6 +69,7 @@
|
|||||||
|
|
||||||
项目进入多轮长期开发后,再按需启用:
|
项目进入多轮长期开发后,再按需启用:
|
||||||
|
|
||||||
|
- `docs/gitea-mcp.md`:需要跨 agent 读取 Gitea 文档、Issue 和 PR 时启用。
|
||||||
- `docs/clean-state-checklist.md`:每轮结束前检查仓库是否可恢复。
|
- `docs/clean-state-checklist.md`:每轮结束前检查仓库是否可恢复。
|
||||||
- `docs/method-map.md`:遇到失败模式时定位该补哪个工件。
|
- `docs/method-map.md`:遇到失败模式时定位该补哪个工件。
|
||||||
- `docs/evaluator-rubric.md`:评审单次 agent 输出质量。
|
- `docs/evaluator-rubric.md`:评审单次 agent 输出质量。
|
||||||
@@ -74,4 +77,4 @@
|
|||||||
|
|
||||||
`tasks.md` 是本样本库自身的维护任务;复制到新项目后,项目任务默认以一任务一文件写在 `docs/tasks/`,阶段路线图维护在 `docs/06-tasks.md`。
|
`tasks.md` 是本样本库自身的维护任务;复制到新项目后,项目任务默认以一任务一文件写在 `docs/tasks/`,阶段路线图维护在 `docs/06-tasks.md`。
|
||||||
|
|
||||||
核心原则:文档不是给人看的装饰,而是给 agent 执行时用的约束、事实来源和验收标准。
|
核心原则:文档不是给人看的装饰,而是给 agent 执行时用的约束、事实来源和验收标准。
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
- [API 合约](api.md):前后端接口形状、错误格式、鉴权约定。
|
- [API 合约](api.md):前后端接口形状、错误格式、鉴权约定。
|
||||||
- [路由与页面结构](routes.md):页面路由、页面职责、组件归属。
|
- [路由与页面结构](routes.md):页面路由、页面职责、组件归属。
|
||||||
- [当前实现状态](current-state.md):可覆盖的当前快照,记录仓库现实状态、可运行命令和下一步可做任务。
|
- [当前实现状态](current-state.md):可覆盖的当前快照,记录仓库现实状态、可运行命令和下一步可做任务。
|
||||||
|
- [Gitea MCP 接入](gitea-mcp.md):可选的共享文档、Issue / PR 协调、安全配置和断连降级规则。
|
||||||
- [收尾检查清单](clean-state-checklist.md):会话结束前逐项检查,保证下一轮无需人工修复即可继续。
|
- [收尾检查清单](clean-state-checklist.md):会话结束前逐项检查,保证下一轮无需人工修复即可继续。
|
||||||
- [方法对照表](method-map.md):失败模式 → 首要修复 → 工件;出问题先查这里对症补工件。
|
- [方法对照表](method-map.md):失败模式 → 首要修复 → 工件;出问题先查这里对症补工件。
|
||||||
- [评审评分表](evaluator-rubric.md):单次会话输出的结构化评审(6 维 0-2 分 + 校准说明)。
|
- [评审评分表](evaluator-rubric.md):单次会话输出的结构化评审(6 维 0-2 分 + 校准说明)。
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
# Gitea MCP 接入
|
||||||
|
|
||||||
|
> 可选增强:让 agent 通过 Gitea 读取共享文档、Issue、分支和 PR。Git checkout 仍是本地编辑与离线降级入口,MCP 不取代 Git。
|
||||||
|
|
||||||
|
## 适用边界
|
||||||
|
|
||||||
|
- Gitea Git 仓库保存版本化文档和代码。
|
||||||
|
- Gitea Issue / PR 保存实时协调状态。
|
||||||
|
- Gitea MCP 提供受控的远端读取和写入工具。
|
||||||
|
- `AGENTS.md`、`docs/00-ai-start-here.md` 等最小启动文件仍保留在项目 checkout 中。
|
||||||
|
|
||||||
|
## 私有配置
|
||||||
|
|
||||||
|
从根目录 [`gitea.env.example`](../gitea.env.example) 复制一份到 `$HOME/.codex/gitea.env`,替换示例值。也可用 `GITEA_ENV_FILE` 指向其他本机私有路径:
|
||||||
|
|
||||||
|
```text
|
||||||
|
GITEA_URL=【Gitea 实例根地址,不含 /api/v1】
|
||||||
|
GITEA_TOKEN=【最小权限 Personal Access Token】
|
||||||
|
|
||||||
|
# 仅当团队明确接受 HTTP 下 Token 明文传输风险时设置:
|
||||||
|
GITEA_ALLOW_INSECURE_HTTP=1
|
||||||
|
|
||||||
|
# 仅当该实例必须绕过本机代理直连时设置:
|
||||||
|
GITEA_DIRECT=1
|
||||||
|
```
|
||||||
|
|
||||||
|
规则:
|
||||||
|
|
||||||
|
- 不把 `gitea.env`、Token、Authorization header、私有实例地址提交到仓库或粘贴到 Issue。
|
||||||
|
- Token 一旦出现在聊天、日志或提交历史中,立即撤销并轮换。
|
||||||
|
- 推荐 HTTPS;如果项目长期使用 HTTP,必须在项目安全决策中记录风险接受人、网络边界和轮换策略。
|
||||||
|
- `GITEA_URL` 填实例根地址;`gitea-mcp` 会自动追加 `/api/v1`。
|
||||||
|
|
||||||
|
## Codex 配置
|
||||||
|
|
||||||
|
复制 [`../scripts/gitea-mcp.ps1`](../scripts/gitea-mcp.ps1) 到稳定的本机路径,然后在全局 `~/.codex/config.toml` 或可信项目的 `.codex/config.toml` 注册:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[mcp_servers.gitea]
|
||||||
|
command = "pwsh.exe"
|
||||||
|
args = ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "【gitea-mcp.ps1 的绝对路径】"]
|
||||||
|
default_tools_approval_mode = "writes"
|
||||||
|
startup_timeout_sec = 30
|
||||||
|
tool_timeout_sec = 60
|
||||||
|
```
|
||||||
|
|
||||||
|
包装脚本固定使用 `gitea-mcp==0.5.1`,避免 `uvx` 自动升级造成协议或工具集合漂移。升级版本时先在独立分支验证 `initialize`、`tools/list` 和一条只读 API,再更新版本号。
|
||||||
|
|
||||||
|
## 工具审批
|
||||||
|
|
||||||
|
默认策略:
|
||||||
|
|
||||||
|
- 自动允许只读:`list_repos`、`read_file`、`list_issues`、`get_issue`、`list_branches`、`list_pull_requests`。
|
||||||
|
- 写入前确认:`create_issue`、`update_issue`、`add_comment`、`create_branch`、`commit_changes`、`create_pr`。
|
||||||
|
- 破坏性动作再次确认:`merge_pr`、关闭 Issue、覆盖文件、批量操作。
|
||||||
|
|
||||||
|
如果 Codex 版本支持 `enabled_tools` / `disabled_tools`,应再用 allowlist 收窄工具,而不是只依靠提示词。
|
||||||
|
|
||||||
|
## 本地验证
|
||||||
|
|
||||||
|
只检查文件格式,不连接 Gitea、不显示 Token:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
./scripts/gitea-mcp.ps1 -CheckConfig
|
||||||
|
```
|
||||||
|
|
||||||
|
连接预检:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
./scripts/gitea-mcp.ps1 doctor
|
||||||
|
```
|
||||||
|
|
||||||
|
预检至少确认:实例可达、Token 有效、当前用户正确、MCP 版本固定。失败时查看系统临时目录中的 `gitea-mcp-<PID>.stderr.log`;日志不得复制 Token 或敏感正文。
|
||||||
|
|
||||||
|
## 降级规则
|
||||||
|
|
||||||
|
- Gitea / MCP 不可用:允许继续已领取任务的本地工作,不允许领取新任务或猜测远端状态。
|
||||||
|
- 恢复连接后:先拉取默认分支并重新读取任务 Issue,再提交或更新状态。
|
||||||
|
- MCP 读取结果与本地 checkout 冲突:以明确记录的提交 SHA 为比较基准,不静默覆盖本地未提交改动。
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# Copy to $HOME/.codex/gitea.env and replace every example value locally.
|
||||||
|
GITEA_URL=http://gitea.example.invalid:3000
|
||||||
|
GITEA_TOKEN=【replace-with-minimum-scope-token】
|
||||||
|
|
||||||
|
# Required only when the team explicitly accepts HTTP credential exposure risk.
|
||||||
|
GITEA_ALLOW_INSECURE_HTTP=1
|
||||||
|
|
||||||
|
# Optional: bypass local HTTP/SOCKS proxy for this Gitea instance.
|
||||||
|
GITEA_DIRECT=1
|
||||||
|
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
#!/usr/bin/env pwsh
|
||||||
|
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[string]$EnvFile = $(
|
||||||
|
if ($env:GITEA_ENV_FILE) {
|
||||||
|
$env:GITEA_ENV_FILE
|
||||||
|
} else {
|
||||||
|
Join-Path $HOME ".codex/gitea.env"
|
||||||
|
}
|
||||||
|
),
|
||||||
|
[string]$Version = "0.5.1",
|
||||||
|
[switch]$CheckConfig,
|
||||||
|
[Parameter(ValueFromRemainingArguments = $true)]
|
||||||
|
[string[]]$ServerArgs
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
$utf8 = [System.Text.UTF8Encoding]::new($false)
|
||||||
|
[Console]::OutputEncoding = $utf8
|
||||||
|
$OutputEncoding = $utf8
|
||||||
|
|
||||||
|
if (-not (Test-Path -LiteralPath $EnvFile -PathType Leaf)) {
|
||||||
|
throw "Gitea MCP 配置文件不存在:$EnvFile"
|
||||||
|
}
|
||||||
|
|
||||||
|
$values = @{}
|
||||||
|
foreach ($rawLine in Get-Content -LiteralPath $EnvFile) {
|
||||||
|
$line = $rawLine.Trim()
|
||||||
|
if (-not $line -or $line.StartsWith("#")) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
$pair = $line -split "=", 2
|
||||||
|
if ($pair.Count -ne 2) {
|
||||||
|
throw "Gitea MCP 配置行必须使用 KEY=VALUE 格式。"
|
||||||
|
}
|
||||||
|
|
||||||
|
$values[$pair[0].Trim()] = $pair[1].Trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($name in @("GITEA_URL", "GITEA_TOKEN")) {
|
||||||
|
if (-not $values.ContainsKey($name) -or [string]::IsNullOrWhiteSpace($values[$name])) {
|
||||||
|
throw "$name 未配置或为空。"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$giteaUri = [Uri]$values["GITEA_URL"]
|
||||||
|
} catch {
|
||||||
|
throw "GITEA_URL 不是有效 URL。"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($giteaUri.Scheme -notin @("http", "https")) {
|
||||||
|
throw "GITEA_URL 只支持 http 或 https。"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($giteaUri.AbsolutePath.Trim("/") -ne "") {
|
||||||
|
throw "GITEA_URL 必须填写实例根地址,不要包含 /api/v1;gitea-mcp 会自动追加 API 路径。"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($giteaUri.Scheme -eq "http" -and $values["GITEA_ALLOW_INSECURE_HTTP"] -ne "1") {
|
||||||
|
throw "当前使用 HTTP。确认接受 Token 明文传输风险后,在私有配置中设置 GITEA_ALLOW_INSECURE_HTTP=1。"
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($entry in $values.GetEnumerator()) {
|
||||||
|
if ($entry.Key -like "GITEA_*") {
|
||||||
|
Set-Item -Path "Env:$($entry.Key)" -Value $entry.Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$noProxyEntries = @($env:NO_PROXY -split "," | ForEach-Object { $_.Trim() } | Where-Object { $_ })
|
||||||
|
if ($noProxyEntries -notcontains $giteaUri.Host) {
|
||||||
|
$noProxyEntries += $giteaUri.Host
|
||||||
|
}
|
||||||
|
$env:NO_PROXY = $noProxyEntries -join ","
|
||||||
|
$env:no_proxy = $env:NO_PROXY
|
||||||
|
|
||||||
|
if ($values["GITEA_DIRECT"] -eq "1") {
|
||||||
|
foreach ($proxyVariable in @("ALL_PROXY", "all_proxy", "HTTP_PROXY", "http_proxy", "HTTPS_PROXY", "https_proxy")) {
|
||||||
|
Remove-Item -Path "Env:$proxyVariable" -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($CheckConfig) {
|
||||||
|
Write-Output "Gitea MCP 配置有效:URL=$($giteaUri.GetLeftPart([UriPartial]::Authority)),Token 已设置,版本=$Version。"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
$uvx = Get-Command uvx -ErrorAction Stop
|
||||||
|
$stderrLog = Join-Path ([IO.Path]::GetTempPath()) "gitea-mcp-$PID.stderr.log"
|
||||||
|
& $uvx.Source --from "gitea-mcp==$Version" gitea-mcp @ServerArgs 2>> $stderrLog
|
||||||
|
exit $LASTEXITCODE
|
||||||
|
|
||||||
@@ -88,6 +88,17 @@ Get-ChildItem -Recurse -File
|
|||||||
| H-502 | 增加评审评分表 `docs/evaluator-rubric.md` | H-403 | 6 维 0-2 分 + Accept/Revise/Block 结论 + 校准说明 | DONE |
|
| H-502 | 增加评审评分表 `docs/evaluator-rubric.md` | H-403 | 6 维 0-2 分 + Accept/Revise/Block 结论 + 校准说明 | DONE |
|
||||||
| H-503 | 增加质量文档 `docs/quality-document.md` | H-106 | 产品域 × 架构层 A-D 评级 + 变更历史,区别于单次输出评审 | DONE |
|
| H-503 | 增加质量文档 `docs/quality-document.md` | H-106 | 产品域 × 架构层 A-D 评级 + 变更历史,区别于单次输出评审 | DONE |
|
||||||
|
|
||||||
|
## Phase 6 · Gitea 多 Agent 共享上下文
|
||||||
|
|
||||||
|
> 用 Gitea Git 保存版本化事实、Issue / PR 协调实时状态、MCP 按需读取;保持模板通用,不提交实例地址或 Token。
|
||||||
|
|
||||||
|
| ID | 任务 | 依赖 | 验收要点 | 状态 |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| H-601 | Phase 0:建立 Gitea MCP 安全与连接基线 | H-409 | 固定 MCP 版本;私有配置不入库;HTTP 需显式确认风险;读写审批和断连降级规则清楚 | DONE |
|
||||||
|
| H-602 | Phase 1:增加上下文清单与按需读取流程 | H-601 | 有机器可读清单和无第三方依赖验证;agent 按任务类型读取;同一 SHA 不重复加载 | TODO |
|
||||||
|
| H-603 | Phase 2:建立 Issue / 任务文件 / PR 多 Agent 协调协议 | H-602 | 任务映射、领取读回校验、分支 / worktree 和写路径防撞规则完整 | TODO |
|
||||||
|
| H-604 | Phase 3:增加自动化治理与一致性检查 | H-603 | 导航、清单、任务元数据、敏感信息和 Gitea Actions 检查可运行 | TODO |
|
||||||
|
|
||||||
## Backlog
|
## Backlog
|
||||||
|
|
||||||
- 【Tier 3】增加初始化阶段 playbook:第一轮会话产出基线工件 + 第一个 clean commit。
|
- 【Tier 3】增加初始化阶段 playbook:第一轮会话产出基线工件 + 第一个 clean commit。
|
||||||
|
|||||||
Reference in New Issue
Block a user