From 1b597d0e3f8ee2a464ca6151bda9379074944fdb Mon Sep 17 00:00:00 2001 From: zhangtao <9480807882@qq.com> Date: Mon, 5 Oct 2026 20:37:06 +0800 Subject: [PATCH] =?UTF-8?q?chore(security+team):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=AF=86=E9=92=A5=E6=89=AB=E6=8F=8F=20CI=E3=80=81=E5=BF=BD?= =?UTF-8?q?=E7=95=A5=E6=95=B0=E6=8D=AE=E5=BA=93=E5=AF=BC=E5=87=BA=E3=80=81?= =?UTF-8?q?=E5=AE=A1=E8=AE=A1=E6=B3=A2=E6=A8=A1=E6=9D=BF=E4=B8=8E=E6=8A=A5?= =?UTF-8?q?=E5=91=8A=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 按团队体检结论落地四项优化: 1) .github/workflows/secrets.yml —— gitleaks 8.30.1(固定版本 + sha256 校验),只扫描 本次变更范围(push: before..sha;PR: base..head;新分支退化为 -1),职责是拦住**新增**密钥。 因历史未清洗(全历史 138 处命中),不做全历史扫描。 2) .gitignore —— 增加 backend/*.sql、backend/sql/**/*.sql、*.dump(历史里多次误提交数据库导出, 导出内含用户会话令牌与第三方凭据;种子数据仍用 backend/sql/*.json)。 3) TEAM-PLAYBOOK.md —— 新增 §7「审计波模板」:7 角色 + 7 只读报告任务的可复制计划, 并注明 maxMembers=8 的余量与调大方式。 4) AUDIT-REPORT.md —— 记录密钥扫描 CI 与全历史密钥清单(138 处 / 15 提交,集中在仅存于历史的 sql 导出与旧 .env、旧私钥;当前 HEAD 无真实密钥,audit-security.md:242 为误报), 新增「曾存入存储源/接口密钥的第三方凭据」与 TEAM-PLAYBOOK 索引。 本地验证:gitleaks 干净提交 exit 0;插入 ghp_ 令牌 exit 1;before 全 0 退化分支正常;YAML 解析通过。 --- .github/workflows/secrets.yml | 47 +++++++++++++++++++++++++++++++++++ .gitignore | 6 +++++ AUDIT-REPORT.md | 8 ++++-- TEAM-PLAYBOOK.md | 31 ++++++++++++++++++++++- 4 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/secrets.yml diff --git a/.github/workflows/secrets.yml b/.github/workflows/secrets.yml new file mode 100644 index 000000000..9c28e28cd --- /dev/null +++ b/.github/workflows/secrets.yml @@ -0,0 +1,47 @@ +name: secrets + +# 密钥扫描:防止再次把真实凭据提交进 public 仓库(本仓库历史里出现过 OpenAI Key、 +# 旧数据库口令与自签名私钥)。只扫描「本次变更的提交范围」,不做全历史扫描—— +# 全历史命中 138 处(主要在仅存于历史的 sql 导出文件与旧 .env 中),需要先清洗历史 +# 才可能全绿;当前的职责是拦住**新增**密钥。 + +on: + push: + branches: [dev, master] + pull_request: + workflow_dispatch: + +jobs: + gitleaks: + name: 密钥扫描(本次变更范围) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # gitleaks 需要完整历史才能按提交范围扫描 + + - name: 安装 gitleaks(固定版本 + 校验和) + run: | + set -euo pipefail + VER=8.30.1 + SHA256=551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb + curl -sSfL -o /tmp/gitleaks.tgz \ + "https://github.com/gitleaks/gitleaks/releases/download/v${VER}/gitleaks_${VER}_linux_x64.tar.gz" + echo "${SHA256} /tmp/gitleaks.tgz" | sha256sum -c - + sudo tar -xzf /tmp/gitleaks.tgz -C /usr/local/bin gitleaks + gitleaks version + + - name: 扫描本次变更范围 + run: | + set -euo pipefail + if [ "${{ github.event_name }}" = "pull_request" ]; then + RANGE="${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }}" + else + RANGE="${{ github.event.before }}..${{ github.sha }}" + fi + # 新分支首次推送时 before 是全 0,退化为只扫最新一个提交 + case "$RANGE" in + *0000000000000000000000000000000000000000*) RANGE="-1" ;; + esac + echo "扫描范围: $RANGE" + gitleaks detect --source . --redact --verbose --log-opts="$RANGE" --exit-code 1 diff --git a/.gitignore b/.gitignore index a0b352956..4418a5128 100755 --- a/.gitignore +++ b/.gitignore @@ -76,3 +76,9 @@ AGENTS.md .workbuddy # AgentTeams 协作运行期状态(本地,不入库) .agent-teams/ + +# 数据库导出/备份:历史里多次被误提交,且导出内含用户会话令牌与第三方凭据。 +# 种子数据用 backend/sql/*.json;导出文件请勿入库(CI 的 secrets 工作流同时拦真实凭据)。 +backend/*.sql +backend/sql/**/*.sql +*.dump diff --git a/AUDIT-REPORT.md b/AUDIT-REPORT.md index 38fa2dc01..67cfcbabe 100644 --- a/AUDIT-REPORT.md +++ b/AUDIT-REPORT.md @@ -195,6 +195,8 @@ | 对外口径(异常统一后的取舍) | 存储「测试连接」密钥环损坏、上传/下载失败、OAuth 渠道未配置、Redis 同步失败等已由具体原因变为通用 5xx 文案(细节只在日志)。若希望管理员在界面看到可操作原因,正确做法是为这些场景定义**业务码**,而不是回到「包装意外异常」 | | 仍可能外泄的出口 | `ValueError` 全局处理器返回 `msg=str(exc)`;建议改为通用文案 + 日志,或让我们的校验统一抛 `CustomException` | | 静默吞异常(既有) | 48 处「记录日志但不 raise」的 best-effort 处理器(redis_crud、ap_scheduler、middlewares、discover 等),属审计「静默吞异常」主线,建议单独排一轮 | +| 仓库密钥防回归(已落地) | 新增 `.github/workflows/secrets.yml`:gitleaks **8.30.1**(固定版本 + sha256 校验)只扫描**本次变更范围**(push 用 `before..sha`,PR 用 base..head,新分支退化为 `-1`),拦住**新增**密钥;`.gitignore` 增加数据库导出规则 `backend/*.sql`、`backend/sql/**/*.sql`、`*.dump` | 本地红绿验证:干净提交 `no leaks found`(exit 0);临时插入 `ghp_` 令牌 → 命中 `github-pat`(exit 1);`before` 全 0 的退化分支同样正常;YAML 解析通过 | +| 历史密钥清单(gitleaks 全历史扫描) | 命中 **138 处 / 15 个提交**:`jwt` 97、`generic-api-key` 38、`private-key` 2、`curl-auth-header` 1;集中在本轮之前就已从 HEAD 删除的 `backend/sql/**/*.sql`(76+7+36) 与 `fastapi_vue3_admin.json`、旧 `backend/env/.env.dev/.env.prod`、旧 `docker/nginx/ssl/server.key` | **当前 HEAD 无真实密钥**:这些文件均已不在 HEAD(仅存在于历史)。`backend/audit-security.md:242` 的命中经核实为**误报**(长标识符,非密钥)。因历史未清洗,CI 不做全历史扫描(否则必然红) | ### 9.3 本轮产生的回滚资产 @@ -229,10 +231,12 @@ **团队** 审计团队(7 名成员,t1–t19)已归档;本报告与 7 份分报告为全部交付物,残余项见 §9.2。 +多智能体协作的**项目专属纪律**(先探测工具再选档位、审计波/交付波两种队形、审计波可复制模板、本项目踩过的版本坑、验证命令、不可逆动作规则)见 [TEAM-PLAYBOOK.md](TEAM-PLAYBOOK.md)——通用协作策略由 DSH 运行时注入,不在此重复。 **收尾后仍需人工处理(不阻塞上线)** 1. **吊销旧 OpenAI API Key**:仓库是 public,`sk-…`(35 字符)确实出现在 `backend/env/.env.prod` 的历史提交中。线上环境从未使用它(容器 env 与该键均为空、新镜像已不含任何密钥),所以风险只在于该 Key 在 OpenAI 侧可能仍有效。请到 吊销;如需 AI 功能再签发新 Key 并只写入服务器 `.env`。 -2. **是否清洗 git 历史**:`git-filter-repo` 可把历史中的 `.env*` 与旧证书整体抹除,但会重写所有 SHA(需 force-push `dev` 与 `master`,影响既有克隆与 PR 引用,且 gitee 镜像需同步)。由于相关口令已轮换、Key 将吊销,**建议以「吊销 + 轮换」为准,清洗作为可选项**;若仓库要转为对外开源,则建议执行清洗。 -3. §9.2 中的产品/架构决策项(会话失效、数据权限注入、多租户规格、i18n、移动端 `ContentType`)。 +2. **曾存入存储源/接口密钥配置的第三方凭据**:历史里的数据库导出文件含 38 处 `generic-api-key` 形态命中,而旧主密钥(`DATA_ENCRYPTION_OLD_KEYS`)同样在历史的 `.env.prod` 中——若这些导出里含加密态的第三方凭据,持有历史的第三方可解密。建议按「已暴露」处理并轮换真实凭据;同时可在数据库层面清空 `DATA_ENCRYPTION_OLD_KEYS`(确认无历史密文依赖后)。 +3. **是否清洗 git 历史**:`git-filter-repo` 可把历史中的 `.env*`、`.sql` 导出与旧证书整体抹除,但会重写所有 SHA(需 force-push `dev` 与 `master`,影响既有克隆与 PR 引用,且 gitee 镜像需同步)。由于相关口令已轮换、Key 将吊销,**建议以「吊销 + 轮换」为准,清洗作为可选项**;若仓库要转为对外开源,则建议执行清洗(清洗后还可把 `secrets` 工作流从「仅扫描变更范围」升级为全历史扫描)。 +4. §9.2 中的产品/架构决策项(会话失效、数据权限注入、多租户规格、i18n、移动端 `ContentType`)。 diff --git a/TEAM-PLAYBOOK.md b/TEAM-PLAYBOOK.md index 84caff695..b0958d760 100644 --- a/TEAM-PLAYBOOK.md +++ b/TEAM-PLAYBOOK.md @@ -76,7 +76,36 @@ cd frontend/app && pnpm run type-check 3. 交付物落入仓库并提交(走 dev → master PR) 4. 归档团队,并向用户说明覆盖缺口与残余项 -## 7. 参考:官方文档在本地 +## 7. 审计波模板(可直接复制) + +插件不提供预置队形,因此把审计波的 roster 与任务写成模板,省掉每次手工列角色: + +```text +create({ approval: "required", description: "<本次目标>", plan: { + members: [ + { name: "后端架构师", role: "backend architect" }, + { name: "前端工程师", role: "frontend engineer" }, + { name: "测试工程师", role: "QA engineer" }, + { name: "安全审计员", role: "security auditor" }, + { name: "运维工程师", role: "devops / deployment" }, + { name: "产品经理", role: "requirements coverage" }, + { name: "UX/UI 工程师", role: "UX + UI reviewer" } + ], + tasks: [ // 七个 kind=work 只读任务,各自写一份报告文件;均不可改代码/启服务/提交 + { id: "a1", subject: "后端架构与代码质量审计", assignee: "后端架构师", description: "只读;产出 backend/audit-backend.md(文件:行号 + 证据 + 优先级)" }, + { id: "a2", subject: "前端与移动端审计", assignee: "前端工程师", description: "只读;产出 frontend/audit-frontend.md" }, + { id: "a3", subject: "测试与可验证性审计", assignee: "测试工程师", description: "只读;产出 backend/audit-testing.md(含实跑结果)" }, + { id: "a4", subject: "安全审计", assignee: "安全审计员", description: "只读;产出 backend/audit-security.md" }, + { id: "a5", subject: "Docker 与部署审计", assignee: "运维工程师", description: "只读;产出 docker/audit-deploy.md" }, + { id: "a6", subject: "需求覆盖审计", assignee: "产品经理", description: "只读;产出 backend/audit-requirements.md" }, + { id: "a7", subject: "UX/UI 审计", assignee: "UX/UI 工程师", description: "只读;产出 frontend/audit-uxui.md" } + ] +}}) +``` +要点:7 名成员 = 插件的 `maxMembers: 8` 上限内留 1 个余量;报告文件名先约定好,避免成员互相覆盖同一文件。 +若要更多角色(如移动端、性能单列),需先把 profile 的 `cordis.patch.yml` 里 `maxMembers` 调大(改后需重载 GUI)。 + +## 8. 参考:官方文档在本地 ``` …/app.asar.unpacked/node_modules/@deepseek-ai/