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
47 changes: 47 additions & 0 deletions .github/workflows/secrets.yml
Original file line number Diff line number Diff line change
@@ -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
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,9 @@ AGENTS.md
.workbuddy
# AgentTeams 协作运行期状态(本地,不入库)
.agent-teams/

# 数据库导出/备份:历史里多次被误提交,且导出内含用户会话令牌与第三方凭据。
# 种子数据用 backend/sql/*.json;导出文件请勿入库(CI 的 secrets 工作流同时拦真实凭据)。
backend/*.sql
backend/sql/**/*.sql
*.dump
8 changes: 6 additions & 2 deletions AUDIT-REPORT.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 本轮产生的回滚资产

Expand Down Expand Up @@ -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 侧可能仍有效。请到 <https://platform.openai.com/api-keys> 吊销;如需 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`)。

31 changes: 30 additions & 1 deletion TEAM-PLAYBOOK.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/
Expand Down
Loading