Commit 38fc1e055f8879e9b03c789cc67a7c39b214e9df

Authored by zichun
1 parent cd0b937b

docs: §19.5 security hardening after external audit

Showing 1 changed file with 30 additions and 1 deletions
docs/agent-architecture.md
@@ -378,7 +378,36 @@ ai_op_queue( @@ -378,7 +378,36 @@ ai_op_queue(
378 - `chat.html` 整页重写:原生 JS(去 jQuery)、全视口、可折叠会话侧栏、单滚动区、markdown 表格、提议卡片、 378 - `chat.html` 整页重写:原生 JS(去 jQuery)、全视口、可折叠会话侧栏、单滚动区、markdown 表格、提议卡片、
379 collectForm 网格布局 + 类型化控件 + FK 选择器模态。 379 collectForm 网格布局 + 类型化控件 + FK 选择器模态。
380 380
381 -### 19.5 验收(2026-07-28) 381 +### 19.5 安全加固(2026-07-28,外部审计后)
  382 +
  383 +审计(21 agent / 641 工具调用)报出 5 个 Critical,**逐条复核全部成立**,已修复并回归。两个根因:
  384 +**身份由客户端自报**、**所有失败路径 fail-open**。
  385 +
  386 +- **身份唯一来源 = ERP 内省**:新增 ERP 端点 `GET /ai/whoami`(`@Authorization` + `@CurrentUser`),
  387 + xlyAi `AuthzService.resolveIdentity(token)` 换取真实 userId/租户/用户类型;请求体与前端里的
  388 + `userid/brandsid/usertype` 全部删除(自报即可伪造)。
  389 +- **fail-closed**:`erp.dev-login.enabled` 变成真开关(默认 false,此前无任何消费者),
  390 + dev 账号 `admin/666666` 之类的内置默认值清空;生产下空 token / 无效 token → 401,
  391 + 绝不回落 dev(管理员) 身份。意图门失败也不再顺带关闭反编造护栏。
  392 +- **越权**:会话 id 按 userId 命名空间化 + 归属校验(他人会话 403);
  393 + `/op/{id}/confirm|cancel` 需登录且必须是提议发起人;`/op/pending` 不再吐 `sPayload`。
  394 +- **并发**:confirm 用 `UPDATE … WHERE sStatus='draft'` 做 CAS 抢占,重复/并发确认只会执行一次;
  395 + 单号在**执行前重新生成**,不再重放 propose 时的 MAX+1 快照。
  396 +- **租户**:FK 选项端点的租户改为取自内省身份(不再接受 `brandsid` 查询参数),
  397 + `fkOptionPage` 拿不到租户时返回空而不是丢掉 `sBrandsId` 谓词。
  398 +- **凭据**:DB/Redis/LLM/ERP 连接信息全部改为环境变量(`DB_URL`/`DB_USERNAME`/`DB_PASSWORD`/`REDIS_*`/`LLM_*`/`ERP_BASEURL`),
  399 + `allowMultiQueries=false`。⚠️ **历史提交里的生产库口令必须轮换**——删掉不等于失效。
  400 +- **数据正确性**:update 路径拒绝系统列、解析 FK、按列类型强转;数值强转对无法解析的输入**报错**
  401 + (此前「一千」→0、「1,000」→1 静默写入),确认卡片展示的是**将要写入的值**(与 payload 同源)。
  402 +- **其它**:拼进 ERP URL 的 id 走 `safeId` 校验(阻断 query/path 注入);`ResponseStatusException`
  403 + 保留 401/403 状态码,不再被全局兜底包成 200。
  404 +- **回归保护**:新增首批单测(10 个,`src/test`)钉住上述不变量;意图门 bench 复跑 50/50=100%。
  405 +
  406 +**仍未做(需要环境/业务决策)**:nginx `/xlyAi` 前置的 ERP 会话校验(决定这些端点是公网可达还是内网可达)、
  407 +生产库 3318 是否对公网开放、xlyEntry 侧 `getBusinessDataByFormcustomId` 是否把 query 参数绑进存储过程 IN 参数、
  408 +以及审计其余 30 条中低危项。
  409 +
  410 +### 19.6 验收(2026-07-28)
382 411
383 - 意图门 bench50:**50/50 = 100%**(终跑 49/50,唯一错例为 Ollama 502 网络抖动,单测复核正确)。 412 - 意图门 bench50:**50/50 = 100%**(终跑 49/50,唯一错例为 Ollama 502 网络抖动,单测复核正确)。
384 - agent 单步 bench50:严格 38/50 = **76%**(达标 ≥76%),宽松 45/50 = 90%。 413 - agent 单步 bench50:严格 38/50 = **76%**(达标 ≥76%),宽松 45/50 = 90%。