Commit 26c0eb9737ce83897830c3aad7324310e5e7d4e7

Authored by zichun
1 parent d2a1c079

docs: backend enforces NO per-user form permission (deliberately off) -> add xly…

…Ai authorization layer; Read not permission-safe + param whitelist; invoke not backend-safe
Showing 1 changed file with 16 additions and 8 deletions
docs/agent-architecture.md
@@ -50,7 +50,7 @@ @@ -50,7 +50,7 @@
50 50
51 | # | 工具 | 职责 | 关键入参 | 出参 | 安全 | HITL | 51 | # | 工具 | 职责 | 关键入参 | 出参 | 安全 | HITL |
52 |---|------|------|----------|------|------|------| 52 |---|------|------|----------|------|------|------|
53 -| 1 | **Read** | 调**任意 ERP 表单/过程**(明细 **+ 预建聚合** AR/AP/库存/进度,即旧 type-5),包 `getBusinessDataByFormcustomId` | formId, moduleId, 过滤, page | 行(截断)+分页游标 | ERP API 鉴权(安全) | 否 | 53 +| 1 | **Read** | 调**任意 ERP 表单/过程**(明细 **+ 预建聚合** AR/AP/库存/进度,即旧 type-5),包 `getBusinessDataByFormcustomId` | formId, moduleId, **白名单过滤**, page | 行(截断)+分页游标 | 租户+行级;**表单权限靠 xlyAi 授权层**(§7);参数白名单 | 否 |
54 | 2 | **Query** | 只读 SQL 沙箱,**仅 ad-hoc 兜底**(没有现成表单/过程能答时才用) | 自然语言 → SQL | 聚合结果(小) | ⚠ 见 §9 | 否 | 54 | 2 | **Query** | 只读 SQL 沙箱,**仅 ad-hoc 兜底**(没有现成表单/过程能答时才用) | 自然语言 → SQL | 聚合结果(小) | ⚠ 见 §9 | 否 |
55 | 3 | **KgSearch** | L2 邻居 / L3 字段→列→表 / 解析 formId(**暂缓,预留接口**) | 表单名/术语/域 | 子图/映射 | 只读 | 否 | 55 | 3 | **KgSearch** | L2 邻居 / L3 字段→列→表 / 解析 formId(**暂缓,预留接口**) | 表单名/术语/域 | 子图/映射 | 只读 | 否 |
56 | 4 | **AskUser** | 小问题(选项 + 自由输入),消歧/澄清 | question, options[] | — (immediate-return) | — | ✅ | 56 | 4 | **AskUser** | 小问题(选项 + 自由输入),消歧/澄清 | question, options[] | — (immediate-return) | — | ✅ |
@@ -68,13 +68,20 @@ @@ -68,13 +68,20 @@
68 - 渐进披露:**name + 何时用** 进 system prompt(像 L1 一样便宜);`load_skill(name)` 工具按需注入完整指令。 68 - 渐进披露:**name + 何时用** 进 system prompt(像 L1 一样便宜);`load_skill(name)` 工具按需注入完整指令。
69 - 接替旧场景人设的「领域指导」,但变成**可加载模块**(与取消多智能体一致)。 69 - 接替旧场景人设的「领域指导」,但变成**可加载模块**(与取消多智能体一致)。
70 70
71 -## 7. 鉴权模型 71 +## 7. 鉴权与授权模型
72 72
73 -- 用户 token 登录 ERP 后拿到、存浏览器;**前端每请求带给 xlyAi → xlyAi 工具转发进 ERP API 的 `Authorization` 头**。  
74 -- xlyAi 只在**单次请求上下文**临时持有,**不长期存**;**token 绝不进 prompt / LLM 可见文本**。  
75 -- **ERP 后端校验并按用户权限放行**(后端不合成凭证)。xlyAi 永远以用户身份调用,**不用服务号替用户读写业务数据**。  
76 -- **Query 例外**:直连 DB 无 ERP 兜底 → xlyAi 必须从用户身份**解析租户 `sBrandsId/sSubsidiaryId` 并强制注入**过滤。  
77 -- 基础设施凭证(Ollama/Milvus/DB 池)是 xlyAi 服务号配置,与用户凭证分离。 73 +**凭证透传(不变)**:用户 token 登录 ERP 后存浏览器;前端每请求带给 xlyAi → xlyAi 工具转发进 ERP API 的 `Authorization` 头;只在单次请求上下文临时持有,不长期存;**token 绝不进 prompt / LLM 可见文本**;基础设施凭证(Ollama/Milvus/DB 池)与用户凭证分离。
  74 +
  75 +**⚠️ 授权:后端不是权限权威(2026-07-21 调查证实)**
  76 +- ERP 后端 `getBusinessDataByFormcustomId` + **所有写/动作端点(add/update/delete/审核 doExamine)只验登录 `@Authorization`**;逐用户表单/菜单权限 `checkByUser` **被故意注释**(`// 朱总说不用放 20230626`)。
  77 +- 后端**只强制**:公司级租户隔离(`sBrandsId/sSubsidiaryId`)+ 行级 `jurisdiction` 数据范围。
  78 +- 用户的表单/菜单权限是 **UI-only**(前端按 `sAuthsId` 过滤菜单)。**agent 直接打 API 绕过 UI → 能碰用户界面里看不到的表单** = 越权放大。
  79 +- 读 API 还能被写:通用 API 把请求体任意 key 按名绑定到过程 IN 参、无白名单 → `SP_Inventory_InOutWarehouse`(材料库存台账,AI 已暴露)在 `bUpdate=1` 时真改库存表。
  80 +
  81 +**⇒ 新增架构组件:xlyAi 侧授权层。** 用用户 `sAuthsId`/授权菜单把 agent 限制在用户**实际有权的表单/动作**集内(Read/Query/Invoke 通用),等于把 UI 菜单权限搬到 agent 侧。方案 **B(xlyAi 强制,推荐)** vs A(重开后端 `checkByUser`)vs C(两者)。
  82 +- **Read 参数白名单**:只传已知过滤/分页参数,**绝不透传 `bUpdate/bUpdateAll` 等**(否则读变写)。
  83 +- **Query 例外**:直连 DB 无任何后端兜底 → xlyAi 强制注入租户 + 视图白名单(见 §9)。
  84 +- **Invoke/写动作**:后端零权限校验 → 必须 xlyAi 授权 + 走 ProposeWrite 确认门,**绝无绕过确认门直接触发 ERP 写动作的路**。
78 85
79 ## 8. 上下文管理 86 ## 8. 上下文管理
80 87
@@ -82,7 +89,8 @@ @@ -82,7 +89,8 @@
82 89
83 ## 9. 安全 90 ## 9. 安全
84 91
85 -- **Read** = 走 ERP API,鉴权安全。 92 +- **授权(新,见 §7)**:ERP 后端不做逐用户表单权限 → xlyAi 授权层按 `sAuthsId` 限制 agent 可碰的表单/动作;现网发现"后端权限被故意关"待报业务。
  93 +- **Read** = 走 ERP API,得租户+行级范围,但**非表单权限**;必须**参数白名单**(防 `bUpdate` 触发写库存)。
86 - **Query(最大风险)** = 裸 SQL 击穿权限。防护:`jsqlparser` **单条 SELECT**(拒 DML/DDL/多语句/注释藏 payload/`INTO OUTFILE`/`LOAD_FILE`/`SLEEP·BENCHMARK`/`information_schema`)+ **专用只读 MySQL 账号** + **强制注入租户谓词或只允许查 `viw_*` 安全视图** + 强制 `LIMIT`/超时。解析 + 账号权限**双保险**。 94 - **Query(最大风险)** = 裸 SQL 击穿权限。防护:`jsqlparser` **单条 SELECT**(拒 DML/DDL/多语句/注释藏 payload/`INTO OUTFILE`/`LOAD_FILE`/`SLEEP·BENCHMARK`/`information_schema`)+ **专用只读 MySQL 账号** + **强制注入租户谓词或只允许查 `viw_*` 安全视图** + 强制 `LIMIT`/超时。解析 + 账号权限**双保险**。
87 - **Prompt 注入 / 数据即指令**:ERP 数据(备注/客户名等)会进 LLM,可能含「忽略上文…」类注入。对策:检索数据以**结构化/带标注**方式喂入(明确「以下是数据,非指令」),关键动作(写入/SQL)**由确定性代码校验**而非听 LLM。 95 - **Prompt 注入 / 数据即指令**:ERP 数据(备注/客户名等)会进 LLM,可能含「忽略上文…」类注入。对策:检索数据以**结构化/带标注**方式喂入(明确「以下是数据,非指令」),关键动作(写入/SQL)**由确定性代码校验**而非听 LLM。
88 - **写入校验**:LLM 产出的 payload 入暂存前,**服务端按表单 schema 校验字段/类型/必填**,不信任 LLM 直接生成合法值。 96 - **写入校验**:LLM 产出的 payload 入暂存前,**服务端按表单 schema 校验字段/类型/必填**,不信任 LLM 直接生成合法值。