# 安全审查与修复报告(0.10.1 修订) 审查范围:`internal/*`(29k 行 Go)、`migrations/`、`deploy/`、`api/openapi/`。 审查方式:4 路并行深度代码审查 + 关键发现人工复核 + 全量单元测试/`go vet`。 日期:2026-08-12。以下条目均已在代码中修复并验证;未修复项作为已知限制列出。 ## 高危(已修复) ### 1. 数据平面 SSRF:DNS rebinding 可把代理流量引向内网 `internal/gateway/proxy.go` 的 `http.Transport` 原先使用普通 `net.Dialer`, Provider 主机名被 DNS 重绑到 169.254.169.254 / 10.x 后,网关会把带完整 Prompt 的请求转发到内网并回传响应。控制面有 `safeDialContext`,数据面没有。 **修复**:`provider.SafeDialContext`(拨号前对所有解析地址做公网校验、直连 已校验 IP 防止二次解析)统一供控制面与数据面使用;`Proxy` 新增 `SetAllowPrivateProviderURLs`,由 `ALLOW_PRIVATE_PROVIDER_URLS` 控制。 ### 2. 网关 API Key 泄露给上游 `internal/provider/openai/adapter.go` 的 `Prepare` 只在配置了上游凭据时 `Set Authorization`,从不剥离客户端头——Provider 无 API Key 时,客户端 用于认证网关的完整 Bearer Key 被原样转发给上游。 **修复**:无条件 `Header.Del("Authorization")` 后再按需注入。 ### 3. SSRF 判定网段不完整(CGNAT/6to4/NAT64 可绕过) `isPublicAddress` 只用了 Go 内建分类,漏掉 100.64.0.0/10、198.18.0.0/15、 192.0.2.0/24、192.88.99.0/24、240.0.0.0/4、2002::/16(6to4 内嵌 IPv4)、 64:ff9b::/96(NAT64)等;且存在两份重复实现(provider 与 controlplane)。 **修复**:导出 `provider.IsPublicAddress` 单点实现,补齐全部特殊用途网段, v4-mapped v6 先 `Unmap()` 再判定;两处调用统一。 ### 4. 应用/数字员工运行把所有网关错误伪装成 200 `internal/workbench/runtime_http.go` 的 `boundedRecorder` 初始 `code=200`, `WriteHeader(4xx/5xx)` 变成 no-op:鉴权 401、限流 429、上游 502 全部被 记录为成功,`callGateway` 的错误分支是死代码,审计/trace/事实核验数据被 污染。 **修复**:`code` 初始化为 0,`Write` 兜底 200;错误分支恢复生效。 ### 5. 登录枚举 + 锁定状态泄露(响应差异 + 计时侧信道) `internal/identity`:未知账号 401 / 停用 403 / 锁定 429(精确倒计时)三态 可枚举全部管理员/门户账号及其安全状态;停用/锁定账号不执行 dummy 哈希, 响应时间还泄露"账号是否存在"。 **修复**:登录路径四种失败(未知/停用/锁定/口令错)统一返回 401 "账号或口令错误";停用/锁定账号同样执行 600k 次 dummy PBKDF2,计时一致。 ### 6. 登录限流可被伪造 X-Forwarded-For 绕过 `ClientIP` 无条件采信 XFF 首值;dev compose 把网关端口暴露在 0.0.0.0, 直连时每请求换一个 XFF 值即可旋转 `gateway:login-limit:` 键。 **修复**:新增 `TRUSTED_PROXIES`(默认环回+RFC1918+ULA,覆盖 compose 的 nginx 同网段场景),仅可信对端携带的 XFF 被采信;dev compose 网关端口 改绑 `127.0.0.1:8080`。 ### 7. 凭据变更后旧会话继续有效 会话是独立 Redis 键,无账号级版本:改密/停用 2FA 后,被盗会话在 TTL (12h)内一直有效,强制改密形同虚设。 **修复**:`Principal.AuthVersion` + Redis 计数键(TTL 7 天 > 会话 12h, 保证版本键过期时旧会话已自然消亡)。改密、TOTP 启用/停用、备用码重生成 均 `BumpAuthVersion`;会话与 TOTP 挑战校验版本。 ### 8. Webhook 投递失败被吞掉(无重试、无死信) `notifications.go` `deliver` 失败只记日志,事件照常 ACK——与注释声称的 "reclaim 重试"相悖,通知实际是 at-most-once。 **修复**:投递失败让事件留在 pending 由 reclaim 重试;单条投递记录 `attempts >= 10` 后放弃自动重试(保留 failed 状态供管理端人工重试), 防止毒 Webhook 永久卡死事件队列。 ## 中危(已修复) ### 9. API Key 认证热路径全表扫描 `api_keys` 只有 `(key_prefix, key_hash)` 复合索引,`WHERE key_hash=$1` 无法使用 → 每个代理请求全表扫描。 **修复**:迁移 `000029` 新增 `UNIQUE (key_hash)` 索引。 ### 10. 撤销/改限流的缓存传播窗口与误报 缓存 TTL 30s + `Invalidate` 失败时 DB 已生效却向管理员报错(重试会造成 混乱)。 **修复**:`Invalidate` 失败降级为 Warn 日志(DB 是权威源);缓存 TTL 降至 10s 作纵深防御。 ### 11. Token 配额提交脚本可重建无 TTL 残留键 `SET ... KEEPTTL` 在键已过期(跨月)时创建永不过期的新键。 **修复**:`EXISTS` 为 0 时放弃回写。 ### 12. 熔断器忽略 HTTP 500 只有 502/503/504 计入熔断失败,持续 500 的上游永远不熔断且 `success()` 不断清零失败计数。 **修复**:`failureResult` 将所有 5xx 计入熔断;重试仍只针对 502/503/504 与可重放请求。 ### 13. Outbox 退避在 attempt≥35 溢出为负延迟 `2^(attempt-1)` 秒转 `time.Duration` 溢出 → `MarkFailed` 把 available_at 设到过去 → 事件被立即重新认领,形成热循环烧掉重试预算(`MAX_ATTEMPTS` 允许到 100)。 **修复**:`attempt > 30` 直接返回上限。 ### 14. 调度器停机期间漏掉所有中间执行 `scheduleDue` 只补最新一次,`* * * * *` 任务宕 30 分钟丢 29 次执行。 **修复**:从旧 `next_run_at` 起逐个 occurrence 补跑(单任务单轮上限 `catchUpLimit=100`,防长期停机瞬间灌入海量记录)。 ### 15. 多字节截断产生无效 UTF-8,事件永久卡死 scheduler/trace/notifications 按字节截断 `[:1000]`,切半多字节 rune 后 PostgreSQL `text` 列拒绝写入 → 重试循环永远失败;inbox 的 Go `len()` (字节)与 DB `length()`(字符)口径不一致,4000 字节中文正文超 4000 字符 CHECK。 **修复**:全部改为 rune 边界安全截断;inbox 落库前按 rune 数截断 (title 256 / body 4000 / link 512)。 ### 16. 管理端部分更新静默重置角色/状态 只改显示名的 PUT 把 superadmin 降为 operator、把停用部门重新激活(绕过 停用保护)。 **修复**:`role` 留空 = 不修改(仅创建时默认 operator/member);`active` 省略时保留当前值(部门与账号一致)。 ### 17. Agent 节点心跳可枚举 + 令牌非恒定时间比较 `WHERE code AND token_hash` 单条 UPDATE:404 vs 401 区分有效 code。 **修复**:先按 code 取哈希、`subtle.ConstantTimeCompare`,未知 code 与 错误令牌统一 `ErrInvalidToken`;`Endpoint` 增加 http(s) 绝对 URL 格式校验。 ### 18. MCP 服务器内部端点泄露给门户用户 `marketplace.Detail` 原样返回 `endpoint_url`/`has_secret_headers`,任何 登录门户用户可读取内网服务拓扑。 **修复**:门户详情脱敏这两个字段(管理端与运行时列表不受影响)。 ### 19. usage_daily 多币种成本混算 价格支持任意货币,但日聚合无币种维度,USD 与其它币种相加成单一数字。 **修复**:迁移 `000030` 将 `currency char(3)` 并入 PK;recorder 按币种 聚合,查询视图返回 `currency`。 ### 20. Content policy 脱敏整包重编码 `json.Marshal` 重编码改变键序/数字格式/HTML 转义,破坏上游签名与字节 级契约。 **修复**:`redactBytes` 状态机在原始字节上仅对 textual 字段值字符串做 替换,其余字节零改动(语义与 walkText 一致,含嵌套/数组上下文传播)。 ### 21. 事实核验不作用于数字员工 `runApplication` 有 `applyFactCheck`,`runDigitalEmployee` 没有——`block` 策略可被数字员工入口绕过。 **修复**:数字员工响应同样执行事实核验管线。 ### 22. 其它(批量) - `shadow` 中间件:handler panic(如 `http.ErrAbortHandler`)泄漏并发槽位 → 新增 completed 标志在 panic 路径归还。 - 审计关闭 drain 只试一次 → 3 次有限重试 + 失败明确日志。 - Trace 表无保留 → `TRACE_RETENTION`(默认 90 天)由 maintenance worker 清理(spans 靠 ON DELETE CASCADE)。 - 广播(inbox)非事务逐行插入 → 单事务包裹,中途失败不留半套消息。 - `visible()` 空 APIKeyID fail-open → fail-closed。 - 文件下载缺 `X-Content-Type-Options: nosniff`(用户可控 Content-Type)。 - Provider 管理错误原样返回内网解析地址 → `ErrBlockedAddress` 哨兵, 客户端只收通用提示,细节进服务端日志。 - 备用码生成 `%31` 取模偏差 → 拒绝采样(丢弃 ≥248)。 - OIDC 每次登录拉 discovery+JWKS → JWKS 按 provider 缓存 5 分钟。 - MCP 客户端缓存不随服务器编辑失效 → 缓存键含 `revision`。 - 凭据轮换单条损坏阻塞全部 → 跳过并计数。 - `auditSpan.capture` 无锁写 → 纳入锁。 - `proxyFor` 并发 cache-miss 重复构建 → `LoadOrStore` 单飞。 - `max_tool_rounds=0`(未配置)导致应用首个工具调用即失败 → 默认 5。 ## 部署配置(已修复) - **`CREDENTIAL_MASTER_KEY` 全零默认值**(compose):任何拿到仓库的人可 解密全部 Provider 凭据/TOTP 密钥/Webhook 签名。compose 改为 `${CREDENTIAL_MASTER_KEY:?...}` 强制设置;`ValidateRuntime` 拒绝已知弱 密钥(所有环境)。 - 网关端口 8080 暴露 0.0.0.0 → 绑定 `127.0.0.1`(nginx 是唯一外部入口)。 - nginx:`server_tokens off` + `X-Content-Type-Options`/`X-Frame-Options`/ `Referrer-Policy` 安全头。 - worker 服务加内存限制(`deploy.resources.limits`)。 - 本地 compose 的 PostgreSQL/MinIO 弱口令为本地开发默认,README 已注明 生产必须覆盖(生产 compose 已强制要求)。 ## 已知限制(未修复,建议后续处理) 1. **审计维护大锁**(`audit/maintenance.go`):分区 drop/move 全程持 `ACCESS EXCLUSIVE`,flush 会阻塞。建议把空分区创建移出锁区间、仅 detach/move/attach 留在锁内,并分批删除。 2. **内容策略单条规则失败冻结全部策略**:`Reload` 时任一规则编译失败会 中止整个快照刷新(保存时 `Validate` 已缓解)。建议 per-policy skip。 3. **无定价模型静默按 0 计费**:`pricing.Calculate` 未命中返回零成本且无 信号。建议未定价模型加日志/指标或可配置默认价。 4. **outbox 事件丢失预算**:Redis Stream `MAXLEN ~100k` 裁剪 + worker 宕机 超预算时事件永久丢失(无回放)。建议 worker 启动时按 `published_stream_id` 从 outbox 表补发。 5. **audit 幂等**:`audit_events` 无唯一键,响应丢失重试会重复落审计行。 可用 `request_id + recorded_at` 建唯一约束。 6. **Outbox marker 30 天 Redis 内存税**:高吞吐下 `published:*` 键累积。 7. **多实例 JWKS/会话版本键无 TTL 清理**:auth-version 键 7 天 TTL 已缓解。 8. **`UPSTREAM_FALLBACK_ENABLED` 是空转**:fallback adapter 与主 adapter 相同,没有真正的第二个上游。建议支持第二上游 URL/凭据或移除该开关。 9. **OpenAPI 漂移**:`scripts/check_openapi_routes.py` 报 52 条已实现路由 未收录 OpenAPI(admin files/inbox/marketplace/skills、portal marketplace、 `/v1/mcp-servers` 等)。建议把检查脚本接入 CI 并补齐文档。 10. **`/metrics` 未认证**:仅靠网络位置保护(dev compose 已回环绑定), 生产建议放内网或加访问控制。 11. **scheduled_task_runs.response 无保留**:每次执行持久化最多 2 MiB 响应。建议只存截断预览或加清理任务。 12. **agent_traces 会话聚合无预聚合表**:大表上 GROUP BY 查询慢。 ## 验证 - `go build ./...`、`go vet ./...` 通过。 - `go test ./...` 全量单元测试通过(含 gateway/identity/scheduler/ workbench/contentpolicy/audit/outbox/provider/shadow)。 - 新增测试:XFF 可信代理/不可信对端、无可信代理时的回退行为。 - 迁移 `000029`(key_hash 索引)、`000030`(usage_daily currency)在 真实 PostgreSQL 上执行通过。 - Docker Compose 全栈(API + 5 workers + admin/portal 前端 + 依赖)启动 健康,`/healthz` `/readyz` 正常。 --- # 追加:业务逻辑正确性专项审查(第二轮,2026-08-13) 以功能正确性视角(算法边界/算术/并发语义/协议语义/事务一致性)对全模块复核, 在安全审查基础上新增修复: ## 已修复 1. **[高] 裸 body 上传 nil reader panic**(`files_admin_http.go`/`portal_files_http.go`): 非 multipart Content-Type 上传时 body 保持 nil,`io.LimitReader(nil,...)` 在 `Upload` 中 panic(net/http 恢复后每个此类请求 500 + panic 日志)。 修复:非 multipart 时回退 `r.Body`。 2. **[高] Bootstrap key 流量永久卡死审计管线**(`audit/recorder.go`): bootstrap 身份 `APIKeyID="bootstrap"` 非 UUID,写入 `usage_daily.api_key_id uuid NOT NULL REFERENCES api_keys` 使整批事务失败、 pending 永不清理、审计队列填满后所有事件被丢弃。 修复:`uuidPointer` 对非 UUID 返回 NULL;日聚合跳过非 UUID 身份(审计 事件本身仍落 audit_events)。 3. **[高] 定价通配符优先级错误**(`pricing/service.go`):通配符只按生效日期 排序,`gpt-4o-mini` 可能命中更宽泛的 `gpt-4*` 价格。 修复:通配符按前缀长度降序(更具体优先)再按生效日期。 4. **[高] 全局工具对所有人生效不可见**(`runtime_http.go visible()`): `len(departments)==0` 对 secure 资源返回 false——无部门限定的工具/MCP 全部消失,绑定它们的应用运行时失败且无配置期报错。 修复:无部门限定=全局资源,所有已认证主体可见;部门匹配只约束有部门 限定的资源。 5. **[高] 调度任务可配置永远无法执行的目标**(`scheduler/service.go`): 管理员创建的 API Key 无 tenant,对部门限定应用/员工运行时一律 404/403, 但保存时无任何校验。 修复:`validateTarget` 增加 `requireKeyTenant` 预检——目标部门限定时, 任务 API Key 必须存在、启用且 tenant 匹配,否则保存即报错。 6. **[中] 路由权重合计 0 → 整数除零 panic**(`runtime/resolver.go`):weight≤0 的脏数据行(绕过管理端校验)使 `Sum64()%0` panic。 修复:权重池过滤非正权重;全非正时视为无匹配。 7. **[中] 重试路径 nil `GetBody()` panic**(`gateway/resilience.go`):GET/HEAD 携带未设置 GetBody 的 body 时重试调用 nil 方法。 修复:GetBody 缺失时放弃重试(避免空体重发)。 8. **[中] TOTP 挑战令牌双重使用竞态**(`identity/session.go`):两个并发请求 用同一 pending 令牌 + 各自有效的不同因子可铸出两个会话。 修复:`AuthenticatePending` 改 `GetDel` 原子消费。 9. **[中] 事实核验策略作用域失效**(`factcheck/engine.go` + workbench): `ORDER BY scope LIMIT 1` 只执行字典序第一条策略,其余部门策略被静默忽略 或张冠李戴。 修复:`Check` 增加 scope 参数,`enabledPolicy` 优先精确匹配 `department:`、回退 global;workbench 从应用/员工部门推导 scope。 10. **[低] 熔断器陈旧成功关闭刚打开的电路**(`gateway/resilience.go`): 打开前通过 allow() 的在途请求成功会清零失败计数并清除 openUntil。 修复:仅半开探针成功可关闭电路;关闭态成功只清零计数。 11. **[低] MCP 工具缓存读竞态**(`mcp_client.go`):tools/toolsAt 无锁读。 修复:读写统一持锁。 12. **[低] 心跳 UPDATE 不复查 token_hash**(`agentnode/store.go`):轮换瞬间 旧令牌仍可更新一次节点元数据。 修复:UPDATE 条件恢复 `token_hash=$2`。 13. **[低] 卸载已停用/归档资源失败**(`marketplace.go`):publishedResourceID 要求 enabled+published,管理员停用后用户无法卸载,安装行永久卡死。 修复:Uninstall 改用不限制状态的 `resourceIDByCode`。 ## 复核为正确的关键逻辑(节选) - 限流 Lua:无 off-by-one(限额 N 精确放行 N 次);月配额键含月份自动翻转。 - Token 配额 reserve/commit/refund 对账数学正确、并发安全;跨月提交安全跳过。 - `usageSession.finish` sync.Once 保证恰好执行一次;SSE/JSON usage 提取路径 无双重计数。 - 审计 recorder 队列/pending 无丢失无重复(事务内 CopyFrom+聚合原子提交)。 - 分区维护 SQL 边界([start,end)、drop 判定)正确。 - PBKDF2/TOTP/备用码密码学实现正确(±1 窗口、原子防重放、拒绝采样)。 - 调度器补跑循环无 off-by-one;outbox claim/发布/标记与去重正确。 - 会话 AuthVersion 语义正确:bump 后新登录签发新版本、旧会话 fail-closed。 --- # 追加:网关接入报错修复与部署密钥持久化(2026-08-13) ## 用户报告的问题 管理端「网关接入」页面提示"服务器内部错误,请稍后重试"。 ## 根因 `GET /api/v1/admin/providers` 对每条记录解密凭据,**任何一条解密失败 (AES-GCM tag 校验失败)都会让整个列表返回 400**。本环境每次重建部署都 重新生成随机 `CREDENTIAL_MASTER_KEY`,旧 key 加密的 Provider 记录全部 无法解密 → 列表接口 400 → 前端显示"服务器内部错误"。 ## 修复 1. **[bug] Provider 列表降级展示**(`provider/admin_http.go view()`): 单条凭据解密失败不再使整个列表报错,改为 `credential_error` 警示字段 返回 200,管理端仍可编辑/删除该记录恢复。 2. **[功能缺口] Provider 缺少删除接口**:新增 `DELETE /api/v1/admin/providers/{id}`(`repository.Delete` 事务 + outbox `provider.deleted` 事件;provider_models/model_routes 由 FK 级联清理)。 3. **[部署] master key 持久化**:本地 compose 首次启动生成 `deploy/.env`(compose 自动读取)并复用,重启/重建不再换 key; `.gitignore` 排除 `deploy/.env`;`deploy/PRODUCTION.md` 增加持久化与 轮换说明。 ## 验证 - 列表接口对损坏记录返回 200 + `credential_error` 警示(旧代码返回 400)。 - DELETE 接口删除成功,级联清理正常。 - 用持久 key 重建 Provider 后**容器重启凭据仍可解密** (`key_configured: true, credential_error: ''`),resolver 快照刷新无错误。 - 全量单测 24 包通过。 --- # 追加:第三轮审查(前端 + 回归,2026-08-13) ## 前端审查发现并修复(web/apps/admin + portal + api-client) 1. **[高] 凭据异常不可见**:后端新增的 `credential_error` 字段前端未展示, 运维无法发现 KEK 失配/损坏的 Provider。修复:`ProviderRecord` 增加字段, 凭据列对异常记录显示红色警示 Tag(tooltip 展示原因)。 2. **[高] localStorage 持久化敏感数据**:Pinia persist 全量落盘 (accessToken/refreshToken/账户信息/锁屏密码密文),任何 XSS 直接窃取。 修复:改为 sessionStorage + `pick` 白名单(仅 isLogin/token/language), 账户信息与锁屏密码不再持久化。 3. **[中] 后端错误消息被吞**:`handleError` 优先用状态码通用文案,400/429 显示"服务器内部错误",校验失败原因不可见。修复:后端 `msg` 优先; 补齐 400/429 文案与枚举。 4. **[中] 门户用量页显示原始微单位**:`cost_microunits` 原样输出。修复: 按 /1e6 转换显示 USD 金额(与管理端一致)。 5. **[功能] Provider 删除按钮**:前端补齐删除入口(确认框 + 级联提示)。 6. 记录未修项:refreshToken 双端死代码、锁屏密码加密占位 key(已不落盘, 建议后续改服务端校验)、注册/找回密码页为模板 mock、B7 约 21 处 ElMessageBox 取消未捕获等。 ## Go 回归审查发现并修复 1. **[高] >4MB 非流式响应 token 计量丢失**:`consumeUsageObject` 提取的 usage 对象以 `inUsage=false` 解析,顶层 *_tokens 永不计数;压缩触发后 计量静默回退为预留值。修复:`consumeJSONAsUsage`(inUsage=true), 并加固 "usage" 键位置判定(要求前一非空白字符为 { 或 ,)。 2. **[中] 脱敏检测与改写不一致**(转义内容):`\u002d` 等 JSON 转义使 解码后匹配成功但字节改写失败,请求原样上送却标记已脱敏。修复: `applyRules` 增加解码文本回退路径(替换后重新 JSON 转义);替换文本 本身做 JSON 转义防破坏结构;`Redacted` 以字节改写结果为准,不谎报。 3. **[中] 脱敏规则跨策略泄漏**:字节改写使用所有 redact 策略的规则(含 不适用于本请求的端点/模型作用域)。修复:仅收集本请求命中的 redact 策略规则。 4. **[中] 熔断探针语义**:陈旧成功/取消探针会错误关闭电路。修复: `allow` 返回探针标记,`success(probe)` 仅探针成功可关闭;取消探针 调用 `abortProbe` 保持打开。 5. **[中] TOTP 挑战令牌先消费后验证**:验证码输错即烧令牌、强制重新登录 且叠加锁定计数。修复:`AuthenticatePending` 改只读,验证通过后 `ConsumePending`(GetDel)原子消费——重试友好且防双会话。 6. **[中] 管理员重置密码不作废会话**:management update 补 `BumpAuthVersion`,与自助改密语义一致。 7. **[低] 会话 TTL 校验**:`AUTH_SESSION_TTL` 上限 7 天,`authVersionTTL` 提高至 14 天,杜绝版本键先过期导致旧会话复活。 8. **[低] provider delete 补充 `tenant_id IS NULL` 过滤**(与其它查询一致)。 ## 新 logo(LLMGuardX语枢) - 设计:AI 网关路由主题——深蓝圆角方块 + 青色枢纽节点 + 三向分支连接, 中心"语枢"字母 A 标记;沿用品牌色(#071F4D/#00E4E5/#006EFF)。 - 替换:侧边栏/顶栏 logo(SVG,Vite 内联)、登录页图标、favicon(16-256 多尺寸 ICO)。程序化像素验证渲染正确。 --- # 追加:旗舰版 Ultra 功能实现(2026-08-13) 按旗舰版功能矩阵补齐的模块(均含后端+前端+迁移+端到端验证): ## 新功能模块 1. **License 授权**(`internal/platform/license` + 迁移 000031 前置账号管控): HMAC-SHA256 签名 License 文件(edition/max_accounts/有效期/特性),启动校验 + 热更新上传,账号创建按上限管控(Free=3)。管理端「系统管理→License 授权」。 2. **登录记录**(迁移 000031):admin/portal 每次登录尝试(成功/失败/原因/IP/ UA)落库;门户「登录记录」+ 管理端「系统管理→登录记录」(audit 权限)。 3. **会话管理**:门户会话列表/重命名/删除(PATCH/DELETE)。 4. **角色管理 CRUD**(迁移 000032 + `internal/identity/roles.go`):自定义 角色 + 权限字符串,内置角色合并展示;分配账号时权限展开。 5. **门户定时任务**(`internal/scheduler/portal_http.go`):门户工作台 定时任务创建/启停/立即执行/历史(仅本人任务)。 6. **按模型 Token 配额**(迁移 000033 + `internal/modelquota`):企业级 模型总配额(Provider+模型模式),所有 Key 共享自然月计数,与 Key 级配额 叠加;管理端「模型配额」。 7. **输出侧脱敏**(`internal/contentpolicy/output.go` + gateway wrapper): 模型回答(content/delta)应用 redact 规则拦截替换,SSE 逐行/JSON 缓冲; 响应头 X-Gateway-Output-Redacted。 8. **供应链安全扫描**(`internal/workbench/scan.go`):skill/mcp 定义静态 检测(硬编码密钥/内网端点/危险命令/提示词注入/base64 混淆),管理端扫描 按钮+结果展示。 9. **记忆管理**(迁移 000034 + `internal/memory`):用户/部门/全局三层记忆, Ollama 向量化语义召回(pgvector HNSW),关键帧衰减清理,向用户授权; 门户「记忆管理」。 10. **AI 助手**(`internal/assistant`):管理平台自然语言问答,注入实时平台 统计(供应商/模型/账号/用量/待办),走默认模型供应商。 11. **概览真实数据**:dashboard 由模板 demo 改为 system-info + monitoring/overview + license 实时数据。 ## 遗留(记录,建议后续) - 渠道接入(企微/钉钉/飞书)与扫码:依赖外部 IM 开放平台,需企业凭据, 建议独立迭代。 - 多租户管理:现有 tenant 为单租户模型,平台管理员多租户管理需数据隔离 重构。 - ARM64 安装包:构建已可交叉编译(CGO_ENABLED=0),发布流程待配置 buildx。 - 个人环境变量注入、资源权限等级(查看/仅使用/管理)、收藏:前端增强。 --- # 追加:旗舰版功能完善第二轮(2026-08-13) 1. **资源权限等级**(迁移 000035):marketplace 安装支持 view/use/manage 三级权限,门户"我的资源"展示。 2. **个人环境变量**(迁移 000036 + `internal/workbench/envvars.go`): 门户配置 key-value(加密存储),对话/应用运行时自动合并进请求变量。 3. **管理平台收藏**(`art-favorites` 组件):顶栏星标收藏当前页, 弹窗快捷跳转(localStorage)。 4. **企业报表**(`gateway/reports`):按供应商/模型/日三个维度汇总 用量与成本。 5. **租户概览**(`/api/v1/admin/tenants/overview`):以部门为租户维度, 统计账号/Key/今日用量,支持多租户管理视角。 6. **ARM64 发布流程**:`scripts/publish-images.sh`(buildx 多架构推送) + PRODUCTION.md 说明。 7. **渠道接入**(迁移 000037 + `internal/channel`):统一渠道抽象, 支持通用 Webhook/企业微信/钉钉/飞书;入站 `POST /v1/channels/{code}/inbound`(企微签名校验/钉钉加签工具), 绑定模型应答后按平台协议回复(企微应用消息/钉钉机器人/飞书应用), 管理端渠道 CRUD + 连通性测试。 # 追加:旗舰版功能完善第三轮(0.11.2,2026-08-13) 1. **门户通用聊天**(迁移 000038 + `internal/portal/chat.go`): 门户「通用聊天」页选择已批准模型直接对话。模型权限审批通过后按需自动 开通用户级运行时 API Key(`portal_user_runtime_credentials` 加密落库, 限额取批准申请值),聊天请求经 `SetGateway` 注入的受管网关执行,与外部 Key 同权受审计/配额约束。会话复用哈希链消息完整性与 busy 租约防并发, 失败不落库(无孤儿/重复消息)。 2. **企微/钉钉/飞书扫码登录**(迁移 000039 + `internal/identity/social*.go`): `identity_providers` 扩展三种 kind;管理端「账号与权限 → 扫码登录」配置; 登录页自动展示;`start/callback` 复用 SSO 路由按 kind 分发。安全设计: - state 为 128-bit 一次性令牌(Redis one-time,5 分钟),回调原子消费; - 平台 AppSecret 用 IdP 专用 KEK 加密存储,列表/视图只回 `secret_configured`; - `provider_uid` 全局唯一 → 平台账号不可同时绑定多个本系统账号; - 未绑定且未开自动开户时重定向回登录页提示,不静默开户; - 平台端点全部为固定公网 URL,复用 public-only 拨号客户端(无新增出站面)。 3. **个人安全策略**(迁移 000040/000041 + 会话索引): - 「账号安全」页:登录设备列表(IP/UA/时间,索引只存令牌 SHA-256 摘要, 惰性清理过期项)、吊销任意非当前会话、扫码绑定/解绑; - 新设备登录提醒:登录成功发布 `security.login_detected` outbox 事件, notification worker 依据 `portal_security_prefs.login_notify`(默认开) 落站内信(新增 security 类别),可单独关闭; - 当前会话吊销返回 400(`ErrRevokeCurrentSession`)。 4. 修复:`request.patch` → `put`(前端无 patch 方法);社交 provider 更新 SQL `$14`→`$13` 参数越界;update 返回视图未回读凭据导致 secret_configured 显示失真;`/api/v1/portal/chat/` 与 `/api/v1/admin/social-providers` 挂载缺失; start/callback 用 provider code 而非 kind 查询。 # 追加:旗舰版功能完善第四轮(0.11.3,2026-08-13) 1. **统一审批中心**(迁移 000042):新增 `resource_access_requests`(资源/渠道申请) 与 `tool_approval_requests`(工具审批);管理端审批中心聚合模型/资源/工具三类 申请;通过后自动开通(marketplace 安装 / 渠道授权记录);outbox → 站内信 通知申请人与管理员。 2. **工具治理**:`tool_definitions` 增加 `rate_limit_rpm` 与 `approval_required`; 限流用 `tool_rate_usage` 固定窗口原子 upsert(多实例共享额度,无竞态放大); 审批标记工具在批准前执行一律拒绝并自动发起申请(部分唯一索引防重复), 批准后立即可用。已端到端验证:拒绝→申请→审批→放行→RPM 超限。 3. **平台环境变量**(迁移 000043):`platform_env_vars` 加密存储,运行时合并顺序 平台→个人,个人可覆盖;管理端仅 `system:manage` 可写。 4. **数字员工会话入口**:门户员工列表(部门可见或已安装)+ 对话 + 调用记录 (经用户运行时 Key 归属)。 5. **个人渠道**(迁移 000044):webhook 入站令牌 SHA-256 摘要落库、constant-time 校验、仅创建/轮换时显示;入站经用户运行时凭据调受管网关,用量/审计归属 用户 Key。已验证正确令牌放行、错误令牌拒绝。 6. **报表多维**:工具调用/审批授权/安全事件三组端点 (`/api/v1/admin/reports/{tools,approvals,security}`)。 7. **租户配额**(迁移 000045):部门 Key/月 Token 上限,运行时凭据开通时强制 校验;租户概览展示配额与当月用量。 8. 修复:渠道 Save 空 API Key 时 `encrypted_api_key` NOT NULL 违约(空 bytea); 安全报表 inet 列二进制扫描失败(`ip::text`)。 # 追加:旗舰版功能完善第五轮(0.11.4,2026-08-13) 1. **智能体节点任务下发**(迁移 000046 + `internal/agentnode/tasks.go`): 任务表 `agent_tasks`(类型/载荷/池路由/重试/认领令牌/结果);认领用 `FOR UPDATE SKIP LOCKED` 保证多节点并发互斥,15 分钟租约;上报凭认领令牌 校验(伪造/重放拒绝),失败按 30s×attempts 退避重入队,达 max_attempts 标记 failed;取消仅限排队中,失败任务可重试;事件经 outbox → 站内信。 2. **个人智能体安全策略**(`portal_agent_policies`):auto_approve_tools 跳过 个人调用的审批门;rate_limit_multiplier 按 (tool,user) 独立窗口放宽个人 限流;`tool_rate_usage` 主键扩展为 (tool_id, portal_user_id, window_start), 全零 UUID 为全局窗口哨兵。 3. **修复存量缺陷**:`/v1/agent/nodes/` 未挂 publicMux,节点心跳/认领端点 在部署拓扑下全部不可达;已挂载并端到端验证。 4. 验证:任务全链路集成测试 `TestAgentTaskPostgreSQLLifecycle` 连真实库通过 (下发→认领→伪造令牌拒绝→成功/失败上报→退避重试→达上限→取消冲突→重试); HTTP 端到端:下发→认领(返回认领令牌)→上报→succeeded,管理端列表不泄露 认领令牌;任务完成站内信已验证。 # 追加:旗舰版功能完善第六轮(0.11.5,2026-08-13) 1. **节点 Agent**(`cmd/agent-node`):裸机执行器,心跳/认领/执行/上报闭环。 - 安全边界:http 任务仅 http(s) 绝对地址 + 禁重定向 + 1 MiB 响应上限; prompt 任务可走本地 LLM 或经网关(凭 AGENT_GATEWAY_KEY);不执行 任意 shell 命令;节点令牌只经环境变量注入,不进入任务载荷; - claim 响应按网关 apiresponse 信封解包(修复根级解析 bug)。 2. **裸机安装**(`scripts/install-agent-node.sh`):systemd 服务 + 0600 配置, NoNewPrivileges + ProtectSystem 加固。 3. **集群/冷备方案**(`deploy/CLUSTER.md`):无状态控制面多副本依据 (outbox SKIP LOCKED/Redis 会话/审计分区 advisory lock),入口 least_conn 负载均衡,冷备含 master key 同步与恢复演练要点。 4. 实测:真实 Agent 进程注册→心跳上线→custom/http 任务认领执行上报→ 管理端成功可见。 # 追加:第七轮代码审查与修复(2026-08-13) 三轮并行审查(安全/并发一致性/前端)后修复清单: **安全(H)** - 渠道入站无鉴权:webhook 渠道强制 X-Inbound-Token 恒定时间校验,未配置令牌 拒绝入站;渠道不存在与令牌无效统一 401 文案防枚举;body 读取改 io.ReadAll+LimitReader。企微签名按官方算法补 echostr 参与签名。 - 运维报表/概览/systemInfo 端点由"仅认证"改为按权限 (usage:read/audit:read/system:manage);reload 错误不再回显内部细节。 - 扫码回调 sso_error 只回传固定码(unbound/disabled/expired/login_failed), 内部细节写服务端日志。 - 个人渠道令牌只接受请求头(query 会进日志/历史),渠道存在性不再可枚举。 - 工具出站 SSRF:Dialer.Control 在系统 connect 阶段校验目标 IP,消除 DNS rebinding 的校验-拨号 TOCTOU 窗口。 - 新增 channel:read/channel:manage 权限并映射进 superadmin/auditor, 渠道端点不再复用 notification 权限;个人限流倍数上限 100→10。 **并发/一致性(H/M)** - 任务上报改为单条条件 UPDATE(claim_token+status 双守卫 + RowsAffected 校验),并发/重放只有一个生效;认领支持回收过期 claimed 任务 (available_at 即租约到期),节点崩溃不再永久卡死队列。 - 资源申请审批改"先开通后落审批"两步式:Install/Grant 幂等且独立提交, 不再事务内嵌套事务;渠道审批校验 enabled。 - 聊天消息一轮(user+assistant)单事务落库,重试不产生孤儿/重复消息。 - 会话列表校验 AuthVersion(改密/2FA 后旧会话不再展示);吊销先 Del 后 SRem,Redis 抖动不再出现"列表已吊销但令牌仍有效"。 - 删除工具前检查调用历史,有记录禁止删除(级联会清空报表数据)。 - 工具审批 rejected 24h 冷却防通知轰炸;限流被拒调用补偿递减不占额度。 - maintenance worker 每日清理 tool_rate_usage 过期窗口(保留 2 天)。 **前端/菜单(H/M)** - 网关菜单 late-append bug:Reports/Tenants/Channels 因 gatewayChildren cap 扩容指向旧数组而在菜单中不可见——已移到 push 前并加注释防复发。 - 聊天会话改名 405:后端 PATCH 改 PUT 与前端 request.put 对齐。 - 渠道编辑弹窗:openEdit 显式清空平台凭据字段(防跨渠道串写); 新增启用开关,编辑不再静默重新启用停用渠道。 - 聊天发送中切模型/会话:响应到达校验会话 id,丢弃迟到响应防串扰。 - 报表默认日期改本地时区计算。