从 Simple-FR 拆出 Platform.Server 并重命名为 MiGu.Server;frontends 源码与构建脚本迁入本仓库。地图监控在 projection/cars 失败或为空时回退 reflection 车辆列表;Simple 仓库已移除旧 Platform.Server。 Co-authored-by: Cursor <cursoragent@cursor.com>
134 lines
12 KiB
Markdown
134 lines
12 KiB
Markdown
# 本周代码复审报告(2026-05-23 ~ 2026-05-29)
|
||
|
||
> **用途**:对本周全部改动(含已提交)的一次复审,聚焦后端 C# 安全性与正确性。
|
||
> **仓库**:`E:\Work\Core\Simple-FR\Simple` **分支**:`dev_simpleLite`
|
||
> **基线**:`d8d4d3c`(2026-05-21)→ **HEAD**:`1412bf0`(含一次合并远程的 merge)
|
||
> **本文档为本地工作记录,默认不纳入版本库。**
|
||
|
||
---
|
||
|
||
## 1. 范围与方法
|
||
|
||
- 本周累计:**37 commit,277 文件,+32461 / −5740 行**;其中后端 C# **96 文件 / +15302 行**为审核核心。
|
||
- `MiGu.Server/wwwroot/assets`(占 dirstat 28%)为前端构建产物,忽略。
|
||
- 说明:本轮原计划用 5 个并行子代理分模块审核,但因账户账单(unpaid invoice)子代理不可用,改为**人工聚焦后端安全关键面**深度审核;功能性大文件(录制/流/UI/Workspace)与前端做轻量或留待后续。
|
||
|
||
### 已深度审阅
|
||
| 文件 | 关注点 | 结论 |
|
||
|------|--------|------|
|
||
| `MiGu.Server/Launcher/SimpleLiteLauncher.cs` | 命令注入/进程生命周期/JobObject | 良好 |
|
||
| `SimpleLite/Web/AiCompletionService.cs` | SSRF/密钥/Prompt 注入/并发 | 见 RV-01~03 |
|
||
| `SimpleLite/PluginManager.cs` | 任意 DLL 加载/RCE/并发 | 良好(入口受限) |
|
||
| `SimpleLite/Web/AiConfigController.cs` | 密钥脱敏 | 良好 |
|
||
| `SimpleLite/Web/InternalAuthModule.cs` | 8222 鉴权 | 见 RV-04 |
|
||
| `SimpleLite/Web/ReflectionApiController.cs`(本会话已改部分) | writableViaApi/只读/Envelope | 已修 ISSUE-05/06/08 |
|
||
| `SimpleLite/Persistence/Repositories/CarRepository.cs` | 反序列化白名单 | 已修 ISSUE-10 |
|
||
|
||
### 鉴权链确认(关键前提)
|
||
- `InternalAuthModule`(AR-1/AR-2)统一前置守护所有 `/projection/*`(reflection / map-edit / persistence / ai-config / stream)。
|
||
- MiGu.Server 侧 Controller 已加 `[Authorize]`(AR-4)。
|
||
- **结论**:AI 生图、插件 reload、字段写入等敏感入口**不可被未认证外部直接触达**——这把多处原本可能的高危项降级为 P2 加固建议。
|
||
|
||
---
|
||
|
||
## 2. 发现清单(按严重度)
|
||
|
||
### P1 — 建议处理(视部署环境)
|
||
|
||
#### RV-04 — InternalAuthModule loopback 默认无条件放行
|
||
- **文件**:`SimpleLite/Web/InternalAuthModule.cs:79-80`(`AllowLoopback` 默认 `true`)
|
||
- **现象**:本机回环(127.0.0.1/::1/IPv4-mapped)请求**不校验 token 直接放行**。
|
||
- **风险**:单机可信环境无碍;但在**多租户/共享主机**或存在可打到 `127.0.0.1:8222` 的 SSRF 时,本机任意进程即可接管调度内核(execute [MethodMember] / patch 字段 / reload plugin)。
|
||
- **建议**:生产(尤其共享主机)通过配置将 `AllowLoopback=false`,强制依赖 `X-Platform-Internal-Token`;并在部署文档标注。代码已预留开关,无需改逻辑,仅需配置 + 文档。
|
||
|
||
### P2 — 加固建议
|
||
|
||
#### RV-01 — AiCompletion endpoint 无 SSRF 白名单
|
||
- **文件**:`SimpleLite/Web/AiCompletionService.cs:191-197`
|
||
- **现象**:`cfg.Endpoint`(管理员经 ai-config 表单可配任意 URL)被服务端 `SharedClient.SendAsync` 直接请求,并携带 `Authorization: Bearer <apiKey>`。
|
||
- **风险**:已认证管理员可让服务器向任意(含内网)URL 发带密钥请求 → SSRF / 密钥外泄到非预期端点。
|
||
- **建议**:对 `Endpoint` 做 scheme 限制(仅 https)+ host 白名单/黑名单(禁内网段),或显式告警「endpoint 指向内网」。
|
||
|
||
#### RV-02 — apiKey 明文存盘且 config/ 未被 gitignore
|
||
- **文件**:`SimpleLite/Web/AiCompletionService.cs:71-99`(`CWD/config/ai-completion.json` 明文读写)
|
||
- **现象**:`apiKey` 明文落盘;`.gitignore` 未覆盖 `config/` 或该文件(已核查无匹配规则)。
|
||
- **风险**:误 `git add config/` 或随包分发会泄露 LLM 密钥。
|
||
- **建议**:`.gitignore` 增加 `**/config/ai-completion.json`(或运行时 `config/`);考虑落盘前对 apiKey 做 DPAPI/对称加密。注:GET 已脱敏(`AiConfigController.MaskApiKey`),此项仅针对磁盘文件。
|
||
|
||
#### RV-03 — AiCompletion 把 LLM 原始响应回抛前端
|
||
- **文件**:`SimpleLite/Web/AiCompletionService.cs:199-200`(`throw ... LLM HTTP {status}: {raw}`)
|
||
- **现象**:上游非 2xx 时把 `raw` 响应体原样抛出,经 `MapEditApiController` 返回前端。
|
||
- **风险**:信息泄露(上游错误细节/内部 endpoint),低概率含敏感片段。
|
||
- **建议**:仅记日志保留 `raw`,对前端返回精简错误码与通用提示。
|
||
|
||
#### RV-05 — 登录阻塞至 Readiness 超时(同 ISSUE-01)
|
||
- **文件**:`MiGu.Server/Controllers/AuthController.cs:111` + `SimpleLiteLauncher.WaitForProjectionReady`
|
||
- **现状**:已通过 `SimpleLiteOptions.ReadinessTimeoutMs`(可配,0=立即返回)缓解;真正的「fire-and-forget + 前端轮询」留待迭代。
|
||
|
||
---
|
||
|
||
## 3. 已确认良好(无需改动)
|
||
|
||
- **SimpleLiteLauncher**:`displayMode` 由 `NormalizeDisplayMode` 归一为固定枚举(web / web+local),命令行无注入面;`_opts.Arguments` 来自受信 appsettings;进程 `Exited` 事件用 `ReferenceEquals(_proc, proc)` 防误清;JobObject 资源用 `AllocHGlobal/FreeHGlobal` 配对释放。
|
||
- **PluginManager**:`reload` 入口(`/reflection/plugins/reload`)只**重扫固定 `./plugins` 目录**,不接受任意路径参数 → 无「任意路径 DLL 加载」漏洞;`Load`/`Unload` 全程加锁(修复历史并发覆盖 bug);collectible ALC + WeakReference + GC best-effort 卸载。
|
||
- **AiConfigController**:GET 脱敏 `apiKey`(`***`+后4位);POST 对脱敏占位保留旧值,避免被覆盖清空。
|
||
- **InternalAuthModule**:token 校验用 `CryptographicOperations.FixedTimeEquals` 防 timing;未配置 token 时一律拒绝远程(仅 loopback 可入)。
|
||
- **AiCompletionService 并发**:静态 `SharedClient`(避免 socket exhaustion)+ `Timeout.Infinite` + `CancellationTokenSource` 控制单次超时;`BoundsContext` 用 `AsyncLocal` 跨 await 保持并 `try/finally` 清理。
|
||
- **本会话修复**:ISSUE-10(反序列化白名单)/ ISSUE-05(writableViaApi 403)/ ISSUE-06(GET 只读)/ ISSUE-08(Envelope 统一),均经 `dotnet build SimpleLite` 验证 0 错误。
|
||
|
||
---
|
||
|
||
## 4. 未深审(建议后续专项)
|
||
|
||
| 区域 | 文件(本周新增/大改) | 建议关注 |
|
||
|------|----------------------|----------|
|
||
| 录制/回放 | `SimulationRecorder` `PlaybackController` `RecordingFileIO` `SimulationPlayer` `SchedulerProbe` | 文件句柄/内存映射释放、帧索引边界 |
|
||
| 实时流 | `ProjectionStreamModule` `AlarmStreamService` | SSE 连接泄漏/重连/背压 |
|
||
| 持久化 | `SceneRepository` `ScriptRepository` `MissionRepository` `PersistenceWorker` `PersistenceBootstrap` | EF DbContext 生命周期、并发、事务 |
|
||
| 交互/渲染 | `WorkspaceSceneInput` `CurveControlPointDragService` `SimpleSceneRenderer` `ComposerDockPanel` `SimpleUI` | 线程竞态、事件/定时器泄漏 |
|
||
| 前端 | `simple-platform-vue/**` | 鉴权守卫、契约一致、XSS、mock 开关(子代理不可用,本轮未审) |
|
||
|
||
---
|
||
|
||
## 5. 结论
|
||
|
||
本周后端改动建立在会话 53 的安全基线(AR-1~AR-6)之上,**鉴权链完整、未见新增高危漏洞**。建议优先处理 **RV-04(生产关 loopback)** 与 **RV-02(config/ai-completion.json 入 gitignore)** 两项低成本加固;RV-01/RV-03 视风险排期。功能性模块与前端建议在子代理可用后做一轮专项复审。
|
||
|
||
---
|
||
|
||
## 6. 本轮处理与补充发现(会话 65 续)
|
||
|
||
### 6.1 已修复(按「修复 RV-04 + P2 加固项」)
|
||
- **RV-01(SSRF)**:`AiCompletionService.GenerateMapInternalAsync` 发请求前校验 endpoint —— 仅允许 http/https(挡 `file://` 等异常 scheme),指向内网/回环时记审计日志但不阻断(兼容自建 LLM)。新增 `IsPrivateOrLoopbackHost`。
|
||
- **RV-02(密钥入库)**:`.gitignore` 增 `**/config/ai-completion.json`。
|
||
- **RV-03(信息泄露)**:上游 LLM 非 2xx 时原始响应仅进日志(截断 500 字符),前端只返回状态码 + “详见日志”。
|
||
- **RV-04(loopback)**:经核实**已可配置**(`PlatformOptions.allowLoopbackBypass`,`ProjectionWebHost.cs:51` 已接线),无需改代码;已在 `MiGu.Server/README.md` 安全须知补充「生产建议 `allowLoopbackBypass=false`」。
|
||
|
||
### 6.2 补充深审结论(质量良好)
|
||
- **实时流** `ProjectionStreamModule` / `AlarmStreamService`:每订阅者一把 `SemaphoreSlim` + `finally` 清理 CTS/锁 + 断连移除;删除车辆时清 `LastAlarmByCar` 防膨胀;反射 `FieldInfo` 缓存。仅 P2(SSE `Dispose` 与 fire-and-forget `Broadcast` 的竞态已被 `catch (ObjectDisposedException)` 吸收;`AlarmStream` Timer Tick 极端慢时可能重入,`ConcurrentDictionary` 保证安全)。
|
||
- **录制文件 IO** `RecordingFileIO`:`compressedLen` ≤ 64MB 校验、`ReadExact` 截断检测、GZip 流 `using` 释放、`ScanIndex` 损坏帧停止。仅 P2(解压后大小无上限,GZip 炸弹理论风险;录像为本地受信文件,实际影响小)。
|
||
|
||
### 6.3 补充发现并修复
|
||
- **RV-06(已预防性修复,2026-05-29)** `PersistenceWorker.FlushUpsert`/`FlushDelete` 原在 `ctx.SaveChanges()` / `tx.Commit()` **之前**就调用 `SetHash`/`RemoveHash`,若事务随后回滚,`_lastWrittenHash` 已记录该 hash → 后续 `ShouldSkipWrite` 跳过重写 → 该变更(尤其低频状态如 Mission/Script)可能永久不落库。`AUDIT.md` 的 PB-1~PB-5 未覆盖此项。虽 `Startup.cs:72-74` 已整体下线持久化(worker 不运行),仍按要求预防性修复:
|
||
- `FlushUpsert`/`FlushDelete` 改为把 hash 变更累积到局部 `hashOps`(`hash==null` 表示移除),不再直接改字典;
|
||
- 仅当 `SaveChanges()` + `tx.Commit()` 成功后才由新增的 `ApplyHashOps` 在 `_hashLock` 内原子应用;回滚 / 异常路径不应用,保留「下次仍需写入」语义;
|
||
- 删除不再使用的 `SetHash`/`RemoveHash`。`ReadLints` 验证 0 错误(完整 `dotnet build` 仍受 §6.4 weaver 环境阻塞)。
|
||
|
||
### 6.4 编译验证说明(受阻于织入工具链环境问题)
|
||
- 修复仅触及 `SimpleLite/Web/AiCompletionService.cs`(标准 C#:`Uri.TryCreate` / `IPAddress` / 字符串操作)。
|
||
- **首次** `dotnet build SimpleLite/SimpleLite.csproj`(本会话早段)**0 错误**成功,`LessokajiWeaver` 在线织入完整跑过(约 51s,日志 `Online compilation finished`)。
|
||
- **本次复验**:SimpleCore 报 20 个 `CS0246/CS0616/CS0641`,全部是 `LessokajiWeaver` 在线织入临时项目(`obj/MaxKeeper_tmp`)应提供的类型缺失(`I18N`/`MethodMember`/`MaxValueKeeper`/`KeepMaxMod`/`ReplaceWithCompileInfo`);`--no-incremental` 全量重建仍 7s 即失败,未进入在线织入。**错误列表无任何 SimpleLite 文件**。
|
||
- 判定:**织入工具链在线服务/状态不可用的环境问题,非本次代码问题**。建议在 VS2022 正常环境、或 `tools\setenv.ps1` 恢复 deps 且 weaver 在线服务可用后复验本次 AI 服务改动。
|
||
|
||
### 6.5 未完成(建议后续专项)
|
||
- `Workspace` / `UI` / `Rendering`、前端全量:本轮因并行子代理不可用(账户账单)未深审,建议子代理恢复后补一轮。
|
||
|
||
---
|
||
|
||
## 修订记录
|
||
| 日期 | 说明 |
|
||
|------|------|
|
||
| 2026-05-29 | 初版:会话 65 本周复审(聚焦后端安全关键面;子代理因账单不可用,人工聚焦审核) |
|
||
| 2026-05-29 | 续:修复 RV-01/02/03 + RV-04 文档;补深审(流/文件良好、PersistenceWorker 发现 RV-06);编译复验受阻于 LessokajiWeaver 在线织入环境问题(与改动无关)。 |
|
||
| 2026-05-29 | 续:按要求预防性修复 RV-06(hash 延迟到事务 Commit 后由 ApplyHashOps 原子应用);本会话全部 5 个改动文件 ReadLints 0 错误。 |
|