From 0e9c77191586e2e830e8429e2e6d2abb7c1dfc81 Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Fri, 26 Jun 2026 18:24:36 +0800 Subject: [PATCH] chore: remove obsolete audit and project docs --- docs/全面代码审计报告-2026-06-21.md | 397 ---------------------------- 1 file changed, 397 deletions(-) delete mode 100644 docs/全面代码审计报告-2026-06-21.md diff --git a/docs/全面代码审计报告-2026-06-21.md b/docs/全面代码审计报告-2026-06-21.md deleted file mode 100644 index 745910518..000000000 --- a/docs/全面代码审计报告-2026-06-21.md +++ /dev/null @@ -1,397 +0,0 @@ -# 全面代码审计报告 - 2026-06-21 - -## 一、审计目标 - -对小虾 SaaS 进行一次系统性代码审计,重点不是局部补丁,而是发现并修复: - -- 架构分层不一致 -- 重复入口和重复实现 -- 安全与认证缺陷 -- 外部依赖导入副作用 -- 配置真源缺失 -- 可验证的 P0/P1 Bug - -## 二、本轮已确认并修复的问题 - -### 1. P0:简化认证使用 SHA256 存储密码 - -**涉及文件**:`apps/api/app/api/routes/auth_simple.py` - -**问题**: -- 注册使用 `hashlib.sha256(password)` 保存密码。 -- 登录用同样 SHA256 比对。 -- 这是不可接受的密码存储方案,缺少 salt 和 cost factor。 - -**根因**: -- `auth_simple.py` 作为临时实现进入真实路由后没有回收。 -- 项目已有 `PasswordHasher(bcrypt)`,但真实 API 没有复用。 - -**修复**: -- 注册改为 `password_hasher.hash_password()`。 -- 登录改为 bcrypt 校验。 -- 对历史 SHA256 用户做透明迁移:首次成功登录后自动升级为 bcrypt。 -- 增加密码强度校验。 - -### 2. P0:登录返回不可验证随机 token - -**涉及文件**:`apps/api/app/api/routes/auth_simple.py`、`apps/api/app/config.py` - -**问题**: -- 登录返回 `secrets.token_urlsafe(32)`。 -- Token 没有签名、没有 payload、没有过期时间、无法被标准认证中间件验证。 - -**根因**: -- 临时认证实现绕过了项目已有 JWT 设计。 -- API settings 缺少 `JWT_SECRET_KEY` 配置项。 - -**修复**: -- 登录返回 HS256 JWT。 -- JWT payload 包含 `sub/email/type/iat/exp`。 -- 响应新增 `expires_in`。 -- `Settings` 增加 `JWT_SECRET_KEY`,由 `.env` 可覆盖。 - -### 3. P1:API 路由包存在重复入口和导入副作用 - -**涉及文件**:`apps/api/app/api/routes/__init__.py` - -**问题**: -- `routes/__init__.py` 维护了一份重复 `api_router`。 -- 正式入口 `app/api/router.py` 也维护一份 `api_router`。 -- 两者内容不一致,旧入口缺少后续生成/成片路由。 -- 导入 `app.api.routes` 会连带导入所有路由和外部依赖。 - -**根因**: -- 迁移过程中保留了旧入口,没有明确唯一真源。 - -**修复**: -- `routes/__init__.py` 改为兼容转发,只导出 canonical `app.api.router` 中的 `api_router` 和 `health_router`。 - -### 4. P1:OSS SDK 为硬导入,导致无 OSS 本地环境无法导入 API Router - -**涉及文件**:`apps/api/app/core/storage.py` - -**问题**: -- `import oss2` 在模块顶层执行。 -- 本地/测试环境未安装 `oss2` 时,导入任何包含 `generated_videos` 的 API router 都会失败。 - -**根因**: -- 外部存储适配器没有做到依赖可选和配置驱动。 -- 无 OSS 配置时,本地 generated 文件已经可以工作,但代码仍强制要求 OSS SDK。 - -**修复**: -- `oss2` 改为可选导入。 -- 只有配置了 OSS AK/SK 并实际创建 bucket 时才要求 `oss2`。 -- 未配置 OSS 时,下载 URL 对本地 `/generated-files/` 直接返回,对 OSS URL 回退为公开 URL。 -- 上传/下载等 OSS 专属操作在未配置时返回明确错误。 - -### 5. P1:API 配置存在双真源 - -**涉及文件**:`apps/api/app/config.py`、`apps/api/app/core/config.py` - -**问题**: -- `app/config.py` 与 `app/core/config.py` 同时定义 Settings。 -- 两套配置字段命名、OSS/MinIO、JWT、CORS 等含义不一致。 -- 新代码主要使用 `app.config`,旧模块可能误导入 `app.core.config`。 - -**根因**: -- 早期目录重构后没有清理旧配置入口。 - -**修复**: -- 明确 `app.config` 为 API 配置唯一真源。 -- `app/core/config.py` 改为兼容转发层,只 re-export canonical Settings/get_settings/settings。 -- 避免未来继续在旧文件增加新字段导致漂移。 - -### 6. P1:旧认证/工作空间路由引用已移除的 DI Container - -**涉及文件**:`apps/api/app/api/routes/auth.py`、`apps/api/app/api/routes/workspaces.py`、`apps/api/app/middleware/auth.py` - -**问题**: -- 旧完整认证和 workspace 路由引用 `get_container()`。 -- `apps/api/app/dependencies.py` 已无 `get_container()`。 -- 当前 canonical router 未挂载它们,但任何误导入都会成为运行时炸弹。 - -**根因**: -- DI 重构后旧路由未删除也未迁移。 -- “完整实现”文件名误导后续开发者可能重新挂载坏代码。 - -**修复**: -- 旧模块顶部显式标记为 legacy disabled。 -- 误导入时立即抛出清晰错误,而不是深层 ImportError。 -- 后续只能在重建 DI composition root 后重新启用。 - -### 7. P1:CI 质量门禁只打印版本,不执行检查 - -**涉及文件**:`.gitea/workflows/ci-cd.yml`、`.github/workflows/ci-cd.yml` - -**问题**: -- CI 安装/验证工具版本后直接成功。 -- `black/isort/flake8/bandit/pytest` 没有实际检查代码。 -- develop/main 可能在红线问题未暴露时继续部署。 - -**根因**: -- 早期为了验证 CI 环境可用,留下了占位命令。 -- 后续没有升级为真实质量门禁。 - -**修复**: -- CI 改为执行 `compileall`、`black --check`、`isort --check-only`、`flake8`、`bandit`。 -- 测试步骤改为 `python -m pytest tests -q`。 -- Gitea 与 GitHub workflow 保持一致,避免 CI 双轨漂移。 - -### 8. P1:Domain 层直接依赖 Redis/SMTP - -**涉及文件**:`packages/domain/auth/session_store.py`、`packages/domain/auth/email_service.py`、`packages/adapters/redis/session_store.py`、`packages/adapters/smtp/email_service.py` - -**问题**: -- Domain 层直接 import Redis、SMTP、email MIME 等基础设施实现。 -- `packages.domain.auth.__init__` 导入时创建 Redis/SMTP 全局单例。 -- Application use case 通过模块全局变量调用外部服务,测试只能 patch 全局变量。 - -**根因**: -- 基础设施实现被放进了 domain 包,破坏 Clean Architecture 依赖方向。 -- 外部服务没有通过构造注入进入 use case。 - -**修复**: -- Redis session 实现迁移到 `packages/adapters/redis/session_store.py`。 -- SMTP email 实现迁移到 `packages/adapters/smtp/email_service.py`。 -- Domain 原路径保留兼容 shim,但不再导出全局外部服务单例。 -- 注册、登录、登出、密码重置、邀请成员 use case 改为构造注入,默认使用 adapter 懒加载工厂。 -- 相关测试从 patch 模块全局变量改为注入 mock 服务。 - -### 9. P1:API 路由层直接依赖具体 SQLAlchemy Adapter - -**涉及文件**:`apps/api/app/api/routes/*.py`、`tests/unit/test_architecture_boundaries.py` - -**问题**: -- 多个 API route 直接 import `SQLAlchemy*Repository`。 -- 路由层本应只依赖 FastAPI dependency provider 和 application use case,不应知道具体仓储实现。 -- 具体 adapter 只能出现在 composition root,例如 `apps/api/app/dependencies.py`。 - -**根因**: -- 为了类型标注方便,把基础设施类型泄漏进 API route。 -- 没有架构守卫测试阻止回归。 - -**修复**: -- 移除 route 层对 `packages.adapters.sqlalchemy_impl` 的直接 import。 -- route 层仓储参数改为 `Any`,具体实现保留在 `dependencies.py` 注入。 -- 新增 `tests/unit/test_architecture_boundaries.py`,防止 route 层再次直接 import SQLAlchemy adapter。 -- 同时守住 `domain.auth` 不再导出 Redis/SMTP 全局单例。 - -### 10. P1:Postgres psycopg Adapter 与 SQLAlchemy Adapter 双轨 - -**涉及文件**:`packages/adapters/postgres/*`、`tests/unit/test_architecture_boundaries.py` - -**问题**: -- `packages/adapters/postgres/*` 与 `packages/adapters/sqlalchemy_impl/*` 同时存在。 -- 主运行链路实际使用 SQLAlchemy,但旧 postgres package 仍可被新代码误导入。 -- 双仓储会造成连接池、事务、模型字段、schema 迁移多头漂移。 - -**根因**: -- 历史 psycopg 实现被 SQLAlchemy 替代后没有明确废弃边界。 -- 缺少测试禁止运行时代码继续引用旧 adapter。 - -**修复**: -- 删除 `packages/adapters/postgres/*` 旧 psycopg adapter 文件。 -- 新增/强化架构守卫:`apps/`、`packages/`、`tests/` 不得 import `packages.adapters.postgres` 或 `Postgres*`。 -- SQLAlchemy 明确为 SaaS 当前唯一主线持久化 adapter。 - -### 11. P1:部署入口双轨与根 Docker 文件漂移 - -**涉及文件**:`Dockerfile`、`docker-compose.yml`、`infra/docker/*`、`docs/DEPLOYMENT.md` - -**问题**: -- 根 `Dockerfile` 与 `infra/docker/api.Dockerfile` 启动路径不同。 -- 根 `docker-compose.yml` 与 `infra/docker/compose.yml` 拓扑不同。 -- 实际 staging/CI 使用 `infra/docker`,但根入口仍可能被误用。 - -**根因**: -- 早期单 API 部署文件没有在多服务架构形成后废弃。 -- 部署文档仍保留旧命令,容易引导错误操作。 - -**修复**: -- 根 `Dockerfile` 改为 deprecated sentinel,误 build 会失败并提示使用 `infra/docker/api.Dockerfile`。 -- 根 `docker-compose.yml` 改为 deprecated sentinel,误 up 会失败并提示使用 `infra/docker/compose.yml`。 -- `docs/DEPLOYMENT.md` 改为声明唯一部署入口和 Gitea Actions 部署约定。 - -### 12. P1:本地 CI 虚拟环境被误跟踪 - -**涉及文件**:`.gitignore`、`.venv-ci-root/` - -**问题**: -- `.venv-ci-root` 中 3000+ 个第三方依赖文件被 Git 跟踪。 -- 搜索、审计、CI 和 diff 都会被虚拟环境噪音污染。 - -**根因**: -- `.gitignore` 没有忽略 `.venv-ci-root/`。 -- 本地 CI 工具环境被误加入索引。 - -**修复**: -- `.gitignore` 增加 `.venv-ci-root/`。 -- 从 Git 索引移除 `.venv-ci-root`,本地文件保留。 - -### 13. P1:数据库 Schema 多真源漂移 - -**涉及文件**:`init-tables.sql`、`migrations/*.sql`、`packages/adapters/sqlalchemy_impl/models.py`、`docs/SCHEMA-MAINLINE.md` - -**问题**: -- `init-tables.sql`、`migrations/*.sql`、SQLAlchemy models 曾同时定义表结构。 -- 字段已经漂移,例如历史 SQL 使用 `assets.library_id/storage_key/mime_type`,当前 SQLAlchemy 使用 `assets.asset_library_id/file_url/file_type`。 -- 旧运行时曾由 SQLAlchemy `Base.metadata.create_all()` 建表,部署缺少正式 migration 主线。 - -**根因**: -- Phase 迁移过程中 SQL 快照没有退役。 -- Alembic 迁移链曾存在但 revision 已过期,和当前 SQLAlchemy model 不一致。 - -**修复**: -- `init-tables.sql` 改为 deprecated sentinel,误执行会直接失败。 -- 新增并更新 `docs/SCHEMA-MAINLINE.md`,明确 SQLAlchemy models 是 schema 定义真源,Alembic 是迁移执行真源。 -- 删除 stale Alembic revisions,新增 `alembic/versions/001_current_schema_baseline.py` 作为当前 schema baseline。 -- `alembic/env.py` 改为从 `DATABASE_URL` 读取连接,并启用 `compare_type=True`。 -- `infra/docker/deploy-staging.sh` 在服务启动前执行 `alembic upgrade head`;已有业务表但无版本表时执行 `alembic stamp head && alembic upgrade head`。 -- API/Worker 默认关闭 `AUTO_CREATE_SCHEMA`,不再默认调用 `Base.metadata.create_all()`。 -- staging 已验证:`alembic_version = 001`,health check healthy,`develop` 部署 job succeeded。 -- 架构守卫禁止运行时代码引用 `init-tables.sql` 或历史 `migrations/001_initial_schema.sql`。 - -## 三、已补充测试 - -### 新增 - -- `tests/unit/test_auth_simple.py` -- `tests/unit/test_login_use_case.py` -- `tests/unit/test_register_user_use_case.py` -- `tests/unit/test_password_reset_use_case.py` -- `tests/unit/test_architecture_boundaries.py` - -覆盖: -- 登录 token 是可验证 JWT。 -- bcrypt 密码可登录。 -- 历史 SHA256 密码登录后自动升级 bcrypt。 -- 错误密码拒绝且不会写库。 - -### 已运行通过 - -```bash -python -m pytest tests/unit/test_architecture_boundaries.py tests/unit/test_auth_simple.py tests/unit/test_password_hasher.py tests/integration/test_generation_pipeline.py tests/integration/test_projects.py -q -python -m pytest tests/unit/test_login_use_case.py tests/unit/test_register_user_use_case.py tests/unit/test_password_reset_use_case.py tests/unit/test_invite_member_use_case.py tests/unit/test_session_store.py tests/unit/test_email_service.py -q -``` - -结果:`34 passed`,`52 passed` - -## 四、仍需继续治理的问题 - -### P1:认证体系双轨(已收敛) - -当前认证入口已收敛到 canonical route: - -- `apps/api/app/api/routes/auth.py` -- `apps/api/app/auth.py` -- `packages/application/auth/*` -- `packages/adapters/sqlalchemy_impl/user_repository.py` - -已完成: -- `auth.py` 不再是 disabled skeleton,也不依赖缺失的 `get_container()`。 -- `api/router.py` 直接挂载 `app.api.routes.auth`。 -- `auth_simple.py` 兼容 shim 已删除,认证入口只保留 `auth.py`。 -- 登录/注册/当前用户均通过 `UserRepository + UseCase + app.auth`,route 层不再承载认证业务逻辑。 - -剩余建议: -1. 持续用 grep/架构测试防止 `auth_simple.py` 路径回流。 -2. 根据产品需要补齐 password reset / verify email 的正式 route。 -3. 接入真实邮件 adapter 前继续保持 no-op email delivery。 - -### P1:认证扩展路由缺失(已补齐) - -已完成: -- `GET /api/v1/auth/verify-email?token=...` -- `POST /api/v1/auth/verify-email` -- `POST /api/v1/auth/password/forgot` -- `POST /api/v1/auth/forgot-password` -- `POST /api/v1/auth/password/reset` -- `POST /api/v1/auth/reset-password` - -说明: -- 同时保留后端历史路径和前端现有路径,避免前后端命名漂移导致功能不可用。 -- 路由仍通过 `UserRepository + UseCase`,不在 route 层写业务逻辑。 -- 邮件发送在当前 compatibility route 中仍使用 no-op email service,接入真实 SMTP adapter 前不会对外发信。 -- 已修复 password reset token 过期判断中的 naive/aware datetime 比较风险。 - -### P1:外部服务 adapter 化收尾(已配置驱动) - -已完成: -- 认证 route 不再持有私有 no-op email/session 类,改为从 `apps/api/app/dependencies.py` 获取配置驱动 adapter。 -- `ENABLE_EMAIL_DELIVERY=false` 时使用 no-op email service,`true` 时按 SMTP 配置创建 `EmailService`。 -- `ENABLE_REDIS_SESSIONS=false` 时使用 no-op session store,`true` 时按 `REDIS_URL` 创建 Redis session store。 -- SMTP/Redis no-op 实现放在 adapter 层,use case 仍通过构造注入使用。 - -剩余建议: -- Production 开启前需要真实 SMTP/Redis 连接验证和密钥审计。 - -### P1:Domain 层存在外部基础设施依赖 - -涉及: -- `packages/domain/auth/session_store.py` 直接依赖 Redis。 -- `packages/domain/auth/email_service.py` 直接依赖 SMTP。 - -问题: -- 违反 Clean Architecture。 -- Domain import 会创建全局外部服务实例。 -- 错误处理使用 `print` 和吞异常。 - -建议: -- 抽象 `SessionStore`、`EmailSender` port。 -- Redis/SMTP 实现迁移到 adapters。 -- UseCase 通过构造函数注入 port。 - -### P1:Repository 体系双轨(已收敛) - -当前持久化主线: - -- `packages/adapters/sqlalchemy_impl/*` - -已完成: -- 旧 `packages/adapters/postgres/*` psycopg adapter 已删除。 -- 架构守卫禁止运行时代码重新引用 `packages.adapters.postgres` 或 `Postgres*`。 -- Workspace/User/Member/Invitation 已走 SQLAlchemy adapter 与 API composition root。 - -剩余建议: -- 继续检查文档和脚本中的旧术语,避免恢复 psycopg adapter。 - -### P1:存储命名漂移(已收敛) - -已完成: -- 运行时代码改用 `OSSStorageService` / `get_storage_service` 命名。 -- `apps/api/app/core/storage.py` 仅保留 `MinIOService` / `get_minio_service` 兼容别名作为边界层。 -- 架构守卫禁止 runtime/test 代码继续引用 MinIO 命名。 -- 当前主线为 OSS;未配置 OSS 时,生成文件通过本地 `/generated-files/` fallback 公开访问。 - -### P2:Bandit 安全告警(已清零) - -已完成: -- B104:API bind 地址改为配置驱动,容器内 `0.0.0.0` 明确标注为 bind address,外部暴露由 Docker/Nginx 控制。 -- B104:登录默认 `ip_address` 从 `0.0.0.0` 改为 `unknown`,避免把未知 IP 伪装成 bind-all 地址。 -- B110:worker 失败状态更新不再吞异常;返回 `state_error` 并执行 rollback。 -- B404/B603/B607:FFmpeg/FFprobe 通过固定参数列表、`shell=False`、`shutil.which` 解析路径,并用 `nosec` 标注剩余可接受风险。 -- 当前 `python -m bandit -r apps packages -q` 通过。 - -### P2:临时代码仍在主线 - -发现: -- `auth_simple.py` shim 已删除。 -- 部分测试和文档仍引用旧 MinIO/OSS 混合术语。 -- `__pycache__` 文件出现在工作树扫描中,需确认 `.gitignore` 和仓库状态。 - -### P1:生产 Alembic 准备(已形成 checklist) - -已完成: -- 新增 `docs/PRODUCTION-RELEASE-CHECKLIST.md`。 -- 明确生产发布前必须执行:staging 验证、CI 验证、生产 env 审计、数据库备份、Alembic 状态判断、发布后 smoke。 -- 明确首次生产接入 Alembic 的判断规则:已有 `alembic_version` 则 upgrade;有业务表但无版本表则 stamp head 后 upgrade;空库直接 upgrade。 -- 明确回滚策略:应用优先回滚 tag;数据库只在验证过 downgrade 时使用 downgrade,否则以备份恢复作为最终兜底。 - -剩余建议: -- 真正生产发布前,基于当时生产容器名和 DB 名写一次性恢复 runbook。 - -## 五、下一步建议修复顺序 - -1. Production 外部服务上线:验证 SMTP/Redis 连接、密钥和告警。 -2. 验证真实 SMTP/Redis/OSS 凭证并完成生产外部服务 smoke。 -3. 全量测试和 CI:后端 unit/integration + 前端 type-check/build + staging smoke。