From 444667e2f0830039a75f30583539902fa2434e7e Mon Sep 17 00:00:00 2001 From: Chiguyong Date: Mon, 6 Jul 2026 10:40:06 +0800 Subject: [PATCH] docs(review): record residual review findings --- ...at-enterprise-agent-platform-p0-3cf29f4.md | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 docs/residual-review-findings/feat-enterprise-agent-platform-p0-3cf29f4.md diff --git a/docs/residual-review-findings/feat-enterprise-agent-platform-p0-3cf29f4.md b/docs/residual-review-findings/feat-enterprise-agent-platform-p0-3cf29f4.md new file mode 100644 index 0000000..581b001 --- /dev/null +++ b/docs/residual-review-findings/feat-enterprise-agent-platform-p0-3cf29f4.md @@ -0,0 +1,182 @@ +# Residual Review Findings — feat/enterprise-agent-platform-p0 + +**Source**: ce-code-review run on commit range `8633f60..3cf29f4` +**Branch**: `feat/enterprise-agent-platform-p0` +**HEAD**: `3cf29f4` +**Tracker availability**: no named tracker documented; `gh` CLI not installed; +`any_sink_available = false` — all findings routed to `no_sink` bucket per +lfg `references/tracker-defer.md` non-interactive mode. + +These findings were surfaced by the multi-agent code review (5 reviewer +subagents: security + adversarial, performance + reliability, +maintainability + standards, api-contract + data-migration, deployment +verification). They are **actionable** (`autofix_class: manual`) but were +**not applied** in Step 5 because they need design input or app+chart +coordinated changes. They are durably recorded here per lfg SKILL.md Step 6. + +## P1 — High-impact defects + +- **P1 / `src/agentkit/llm/pii_filter.py` + `src/agentkit/llm/gateway.py`** — + PERF-1: PII 脱敏模块未接入生产 LLM 调用路径(死代码)。`filter_messages_pii` + 仅被单元测试引用,gateway.py 的 chat()/chat_stream() 直接透传原始 messages。 + 等保合规声称的 PII 保护实际未生效。**Fix**: 在 gateway chat() 接入 + `filter_messages_pii(messages, fail_closed=True)`,从 `agentkit.yaml` 读 + PII 配置开关和 ner_hook 路径。 + +- **P1 / `src/agentkit/server/auth/scim/router.py:233-238`** — + API-1: SCIM GET /Users 的 `totalResults` 返回当前页条数而非全量总数 + (违反 RFC 7644 3.4.2)。当用户总数 > count 时,SCIM 客户端无法得知真实 + 总数,分页逻辑错误。**Fix**: 在列表查询前先执行 `SELECT COUNT(*) FROM + users {where}` 复用同一 where 子句和参数。 + +- **P1 / `src/agentkit/server/auth/scim/router.py:50-64,107-151,154-197,200-238`** — + API-2: SCIM 错误响应使用 FastAPI 默认 `{"detail": "..."}` 而非 RFC 7644 + 3.12 规定的 `{"schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"], + "status": "409", "detail": "..."}`。同文件中定义的 `SCIMErrorResponse` 是 + 死代码。**Fix**: 添加 SCIM 异常处理器转换所有 HTTPException 为 SCIM + ErrorResponse 格式。 + +- **P1 / `src/agentkit/server/routes/auth.py:804-815,823-827`** — + API-4: `_sso_issue_token_pair` 用占位符 `refresh_token="__pending_sso__"` + 创建 session,并发 SSO 登录会触发 `refresh_token_hash` UNIQUE 约束冲突 + 抛 IntegrityError → 500。即使不考虑并发,两步写入也不原子,第二步失败 + 会留下孤儿 session。**Fix**: 先 `secrets.token_urlsafe(32)` 生成真实 + refresh token 再 `SessionCreate` 一步创建。 + +- **P1 / `src/agentkit/server/auth/scim/router.py:107-151`** — + API-5: SCIM `create_user` 不写入 `user_idp_links`,导致 SCIM 预配的用户 + 无法 SSO 登录(OIDC 会 JIT 创建重复账户)。SCIM 的核心价值就是预配 + SSO + 联动,此处断裂使 SCIM 预配失效。**Fix**: SCIM create_user 接受 + `externalId` 作为 idp_subject,INSERT users 后 INSERT user_idp_links。 + +- **P1 / `deploy/offline/scripts/install.sh:58` + docs** — + DEPLOY-1: 部署名引用错误。fullname helper 在 release=agentkit 时渲染 + Deployment 名为 `agentkit`,但 install.sh:58 + 11 处文档引用 + `agentkit-agentkit`(不存在)。**Fix**: 改用标签选择器 + `kubectl rollout restart deployment -l app.kubernetes.io/instance=${RELEASE}`。 + +- **P1 / `deploy/helm/agentkit/templates/deployment.yaml:88-92`** — + DEPLOY-2: Redis 鉴权断裂。`REDIS_PASSWORD` env 注入但应用从不消费 + (`src/agentkit/**/*.py` 零命中),Redis 客户端通过 `redis_url` 构造 + (`redis://redis:6379/0`,无密码)。若 Redis 启用密码,pod 全部 Redis + 操作 NOAUTH 失败。**Fix**: 在 redis 客户端构造处读取 `REDIS_PASSWORD` + 并以 `password=` 参数传入 `from_url`,或将密码拼入 redis_url。 + +## P2 — Moderate issues with meaningful downside + +- **P2 / `src/agentkit/server/auth/scim/router.py:107-151`** — + API-6: SCIM POST /Users 缺少 `Location` 响应头(RFC 7644 3.14 强制要求)。 + **Fix**: 用 `JSONResponse` + `response.headers["Location"] = ...`。 + +- **P2 / `src/agentkit/server/auth/scim/router.py:183-187`** — + API-7: SCIM PATCH userName 更新未处理 UNIQUE 约束冲突,返回 500 而非 + 409。**Fix**: 包在 try/except `aiosqlite.IntegrityError` 中返回 409。 + +- **P2 / `src/agentkit/server/routes/auth.py:812`** — + API-8: `_sso_issue_token_pair` 硬编码 `auth_provider="sso"`,丢失 OIDC/SAML + 区分。admin UI 无法按 IDP 筛选 session。**Fix**: 增加 `provider_name` + + `provider_type` 参数。 + +- **P2 / `src/agentkit/server/auth/scim/router.py:216-225`** — + API-9: SCIM filter 语法不匹配时静默返回全量用户,可能导致数据泄露。 + **Fix**: 不支持的 filter 返回 400 + `scimType=invalidFilter`。 + +- **P2 / `src/agentkit/server/routes/auth.py:1040-1085,1097-1143`** — + API-10: `deprovision_user` / `change_user_role` 返回 untyped `dict[str, Any]`, + OpenAPI schema 无法文档化。**Fix**: 定义 `DeprovisionResponse` / + `RoleChangeResponse` Pydantic 模型。 + +- **P2 / `src/agentkit/server/auth/scim/router.py:94-104`** — + API-11: SCIM `_user_to_scim` 用 `username` 作为 `displayName`,语义错误。 + **Fix**: 改为 `None` 让客户端 fallback。 + +- **P2 / `src/agentkit/server/auth/models.py:612-620`** — + MIG-1: `user_idp_links` 表无 ON DELETE 行为且无 FK 约束到 users。当前无 + 删除路径,但未来引入 GDPR 物理删除时会产生孤儿链接。**Fix**: 引入物理 + 删除时加 `REFERENCES users(id) ON DELETE CASCADE`。 + +- **P2 / `deploy/helm/agentkit/templates/deployment.yaml:58-62,93-97`** — + DEPLOY-4: PG 密码双源不一致。`DATABASE_URL`(被消费) 与 `POSTGRES_PASSWORD` + (死 env)并存。runbook Slot 10 轮换 `postgres-password` 但应用实际用 + `DATABASE_URL`。**Fix**: 明确单一可信源 — 若只用 `DATABASE_URL`,移除 + `POSTGRES_PASSWORD` env 与 `postgresPassword` slot。 + +- **P2 / `deploy/helm/agentkit/templates/deployment.yaml:83-87`** — + DEPLOY-5: OTel exporter token 值格式与 `OTEL_EXPORTER_OTLP_HEADERS` 语义 + 不符。期望 `key=value` 格式,但存储值为 `Bearer `。**Fix**: 改为 + `Authorization=Bearer ` 格式。 + +- **P2 / `src/agentkit/server/auth/scim/router.py` (7 处) + `scim/models.py`** — + STAND-1: SCIM router 使用 `typing.Any` 7 处,违反 AGENTS.md 禁用 any 规则。 + **Fix**: 定义 `SCIMUserResponse` / `SCIMUserListResponse` Pydantic 模型。 + +## P3 — Low-impact / narrow scope + +- **P3 / `src/agentkit/server/auth/providers/oidc.py:185-197`** — + SEC-2: OIDC id_token 未按规范验签(key=None + 关闭 iss/exp)。当前 + authlib 1.6.12 fail-closed,非直接绕过,但规范违反且脆弱。**Fix**: 用 + IDP JWKS 公钥验签,校验 iss/aud/exp。 + +- **P3 / `src/agentkit/server/auth/scim/router.py:170-185`** — + SEC-3: SCIM PATCH deprovisioning(`active=false`)不写审计。admin 同等 + 操作有审计,SCIM 没有。**Fix**: 检测到 active true→false 时调用 + `record_deprovision(source=SOURCE_SCIM)`。 + +- **P3 / `src/agentkit/llm/gateway.py:401-414`** — + PERF-6: 流式路径在无 usage 数据时记录"cache miss",指标误导。**Fix**: + 仅当 `final_usage` 非 None 时才记录 cache metric。 + +- **P3 / `src/agentkit/llm/gateway.py:450-457`** — + PERF-7: 非 Anthropic provider 调用膨胀 `prompt_cache.miss` 计数器。 + **Fix**: 仅对 Anthropic provider 记录 cache metric。 + +- **P3 / `src/agentkit/server/auth/scim/router.py:171-187`** — + API-12: SCIM PATCH 静默忽略不支持的 op/path,无错误反馈。**Fix**: + 收集 skipped ops 并返回 400 + `scimType=noTarget`。 + +- **P3 / `src/agentkit/server/auth/scim/router.py:200-238`** — + API-13: SCIM filter 解析对属性名和操作符大小写敏感,违反 RFC 7644 + 3.4.2.2。**Fix**: 用 `filter.lower()` + 正则 `re.IGNORECASE`。 + +- **P3 / `src/agentkit/server/auth/scim/router.py:154-197`** — + API-14: SCIM PATCH `op.path==None` 时仅更新 active,丢弃 value 中的其他 + 字段(部分实现静默丢数据)。**Fix**: 遍历 value 的 keys 对每个已知属性 + 执行对应 UPDATE。 + +- **P3 / `src/agentkit/server/auth/models.py:609-639`** — + MIG-2: schema 迁移幂等但缺少 V4→V5 的显式迁移记录。**Fix**: 未来新增列 + 时按 schema_version 差值执行 ALTER TABLE。 + +- **P3 / `deploy/helm/agentkit/values.yaml:8-11`** — + DEPLOY-9: 镜像仅 tag 固定(0.1.0),未做 digest pinning。政企生产场景 + 要求 digest pinning 防篡改。**Fix**: 支持可选 `image.digest` 字段。 + +- **P3 / `docs/deployment/key-rotation-runbook.md:254-283`** — + DEPLOY-8: runbook 仅 Slot 1-10,缺独立 Slot 11(PG 密码)段落。**Fix**: + 拆为两节,Slot 11 内注明须同步更新 `database-url`(Slot 3)。 + +- **P3 / `src/agentkit/bitable/service.py:564-568`** — + MAINT-2: `delete_view` 将两种不同失败原因统一抛 `LastViewDeletionError`, + 误导性错误信息。**Fix**: 区分 `DELETED`/`LAST_VIEW`/`NOT_FOUND` 三态。 + +- **P3 / `src/agentkit/server/frontend/src/stores/chatStream.ts:881-887,923-929`** — + MAINT-3: `team_synthesis_chunk` 与 `team_synthesis` 重复同一 `findLastMessage` + 谓词。**Fix**: 提取 `findStreamingSynthesisMilestone(conv, sid)` helper。 + +- **P3 / `src/agentkit/server/frontend/tests/unit/bitable-tokens.test.ts:75,81,85`** — + MAINT-4: token 声明正则转义重复 3 次。**Fix**: 提取 `tokenDeclPattern(token)` + helper。 + +- **P3 / `src/agentkit/server/frontend/tests/unit/bitable-tokens.test.ts:92`** — + STAND-2: test "outside root" 用 replace 仅替换首个 `:root` 块。**Fix**: + 加 `g` 标志 `replace(/:root\s*\{[^}]*\}/g, '')`。 + +- **P3 / `src/agentkit/server/app.py:1401`** — + STAND-3: SCIM router 挂载在 `/api/v1` 前缀下,偏离 RFC 7644 标准 SCIM + 路径 `/scim/v2`。**Fix**: SCIM router 单独挂载不加 `/api/v1` 前缀。 + +--- + +**Total residual findings**: 27 (P1=7, P2=10, P3=10) +**Applied in Step 5**: 9 fixes (commit `3cf29f4`) +**Deferred to follow-up**: 27 (this file)