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