Skip to content

[Bug] WesightModel 同步逻辑会破坏用户已有的 CLI 配置文件并将 WeSight key 落盘到非 WeSight 文件 #31

@stephenlzc

Description

@stephenlzc

问题描述

WeSight 当前在 WesightModel 模式下保存模型、调用"Apply to Live"、或触发 cowork:config:set 时,会以全文覆写整块替换的方式修改用户本地 CLI 的全局配置文件,并直接把 WeSight provider key 明文写入这些文件。这违反了三条用户可观察的契约:

  1. 用户已有的凭据被静默替换(数据被改);
  2. 用户的非凭据字段(注释、[features] 块、自定义 provider 条目)被擦除(数据丢失);
  3. WeSight 自己的 key 离开 WeSight 的管控范围,泄漏到任何能读这些共享配置文件的工具里(安全)。

说明:以下 path:line 形式的引用在 GitHub 中会自动渲染为可点链接(例如 src/main/libs/externalAgentConfigSync.ts:672 会跳转到对应行)。

一、详细问题点 / 受影响代码

引擎 目标文件 代码位置 现象 严重度
Codex ~/.codex/config.toml src/main/libs/externalAgentConfigSync.ts:672 atomicWrite 全文覆写,不读已有 TOML HIGH(数据丢失)
Codex ~/.codex/auth.json src/main/libs/externalAgentConfigSync.ts:670-676 OPENAI_API_KEY 被 WeSight key 替换 HIGH(凭据被改)
Claude ~/.claude/settings.json(同步路径) src/main/libs/externalAgentConfigSync.ts:319-331, :657 env 整块被 WeSight ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN 替换 HIGH(凭据被改)
Claude ~/.claude/settings.jsonapplyProviderToLive src/main/libs/externalAgentProviderStore.ts:1271-1273 writeJsonFile 整文件写入 HIGH(数据丢失)
Codex ~/.codex/auth.json + config.toml(同上路径) src/main/libs/externalAgentProviderStore.ts:1405-1406 整文件写入 HIGH(数据丢失)
OpenClaw ~/.openclaw/openclaw.json → models.providers src/main/libs/openclawConfigSync.ts:941-1000, :1543-1566 每次 sync 从 resolveAllEnabledProviderConfigs() 整体重建,user 自加 provider 被擦 HIGH(数据丢失)
OpenClaw ~/.openclaw/openclaw.json → gateway / discovery / skills / cron src/main/libs/openclawConfigSync.ts:1090+ 无脑覆写 MEDIUM(数据丢失)
OpenClaw ~/.openclaw/exec-approvals.json src/main/libs/openclawConfigSync.ts:1758-1768 首次 sync 自动把 agents.main 升到 security=full, ask=off MEDIUM(安全策略被改)
cc-switch ~/.cc-switch/cc-switch.db → providers.settings_config src/main/libs/externalAgentConfigSync.ts:540-588 WeSight key 以明文写入 cc-switch DB,任何能读该 DB 的进程都能拿到 HIGH(凭据泄漏)

二、复现步骤

复现 1 — Codex 整文件覆写

  1. ~/.codex/config.toml[features].web_search_request = true 和一行注释
  2. 在 WeSight 保存任意 model
  3. 重开 config.toml → 自定义 [features]、注释消失,只剩 WeSight 写入的 block

复现 2 — Claude env 块被替换

  1. ~/.claude/settings.json → envANTHROPIC_API_KEY=<你自己的真实 key>
  2. 在 WeSight 保存 model
  3. 重开 settings.jsonenv.ANTHROPIC_API_KEY 已被 WeSight key 替换

复现 3 — OpenClaw user provider 被擦

  1. ~/.openclaw/openclaw.json → models.providers 加一个非 WeSight 注册的 provider 条目
  2. 触发任何一次模型保存 / agent CRUD / IM 配置变更
  3. 重开 openclaw.json → 你的 provider 条目消失

复现 4 — cc-switch 明文 key 落盘

  1. 安装 cc-switch + WeSight
  2. WeSight 自动创建 WeSight - Anthropic 行,其 settings_config JSON 字段含明文 WeSight API key
  3. 任何进程只需 sqlite3 ~/.cc-switch/cc-switch.db 'select settings_config from providers' 即可拿到

三、期望行为 vs 实际行为

维度 期望 实际
Codex config.toml user 字段保留;WeSight 只追加 [model_providers.wesight] block 整文件覆写(externalAgentConfigSync.ts:672
Claude settings.json → env user 的 ANTHROPIC_API_KEY / 其它 FOO_TOKEN 全部保留;WeSight 用 marker 圈出自己的 block env 整块替换(externalAgentConfigSync.ts:319-331, 657
OpenClaw models.providers spread existing.providers 在前,只 override WeSight 命中的 key 整体重建(openclawConfigSync.ts:941-1000
WeSight key 落点 只在 WeSight sqlite app_config.providers.<n>.apiKey 明文出现在 4 个共享全局文件 + 1 个 DB
applyProviderToLive 走 merge 路径 writeJsonFile 整文件路径(externalAgentProviderStore.ts:1272, 1405-1406
exec-approvals.json 已有 agents.main 配置时尊重之 无脑升 security=full, ask=offopenclawConfigSync.ts:1758

四、预计实现路径

按风险从低到高分六步,每步独立可发版:

Step 1 — OpenClaw models.providers spread 顺序修复

  • 文件:src/main/libs/openclawConfigSync.ts:1543-1566
  • 改法:把 resolveAllEnabledProviderConfigs() 生成的 managed.providers 改成 {...existing.providers, ...managed.providers},并对每条 managed provider 内部只覆盖匹配 id 的字段。
  • 验证:在 ~/.openclaw/openclaw.json 手加一个 models.providers.<custom>,触发 sync,重启检查 <custom> 仍在。
  • 风险:低;改动局限在 OpenClaw 子树。

Step 2 — Codex config.toml 增量写入

  • 文件:src/main/libs/externalAgentConfigSync.ts:672
  • 改法:调用 atomicWrite 前先 readFile 解析现有 TOML;如果已有 model_provider / model 或任何 [[model_providers]] 表,则只 merge/追加 [model_providers.wesight] block 而不覆写。参照 Hermes 的 mergeHermesConfigForWesightModelsrc/main/libs/hermesConfig.ts:348-401)的「spread per section + writeIfChanged」结构。
  • 验证:见复现步骤 1。
  • 风险:中;需要小心 TOML 解析的边界(注释、字符串中的 [ 等),可借助现有的 toml parser。

Step 3 — Claude settings.json → env 标记符化

  • 文件:src/main/libs/externalAgentConfigSync.ts:319-360
  • 改法:把 buildClaudeEnvForConfig 改为只输出 WeSight 自己的 env 子集(ANTHROPIC_AUTH_TOKEN / ANTHROPIC_API_KEY / ANTHROPIC_BASE_URL / ANTHROPIC_MODEL);写入时用 marker 标记(同 Hermes .env# >>> WeSight managed: <id> 模式,但因为是 JSON,封装为 env.__wesight_managed = { auth: true, model: true })。下次写入先读旧值,把非 marker 字段保留。
  • 风险:中;需要把 marker 当作「WeSight own」与「user own」的分界。

Step 4 — applyProviderToLive 改走 merge 路径

  • 文件:src/main/libs/externalAgentProviderStore.ts:1271-1273, :1405-1406
  • 改法:把 writeJsonFile(getClaudeSettingsPath(), settingsConfig) 改为调用 mergeClaudeSettingsWithProvidersrc/main/libs/externalAgentConfigSync.ts:333-360)的等价 merge;Codex 同理。
  • 风险:低;纯重构,无外部行为变化(merge 之后行为更对)。

Step 5 — cc-switch 明文 key 改为占位符

  • 文件:src/main/libs/externalAgentConfigSync.ts:540-588
  • 改法:upsertCcSwitchClaudeProvider 写入的 settings_config 中,ANTHROPIC_API_KEY 改为 ${WESIGHT_APIKEY_ANTHROPIC} 占位符;cc-switch 启动时由 WeSight 注入的 env 解析(仿 OpenClaw 的 WESIGHT_APIKEY_* 机制,src/main/libs/openclawConfigSync.ts:1606-1633)。
  • 风险:中;cc-switch 自身可能不支持占位符语法,需先确认。

Step 6 — exec-approvals.json 加"不升级"守卫

  • 文件:src/main/libs/openclawConfigSync.ts:1758-1768
  • 改法:仅在 ~/.openclaw/exec-approvals.json 不存在没有 agents.main 时写入默认值;否则保留。
  • 风险:低;可立刻发版。

五、实施后可改进

  • 数据安全:用户已有的 CLI 配置不再被 WeSight 静默改写;cc-switch DB 不再含明文 WeSight key。
  • 多工具共存:用户在 Claude Code、Codex、cc-switch、OpenClaw 之间切换时,每个工具的本地配置互不污染。
  • 可审计性:所有 WeSight 写入都有 marker 标记,用户可一眼看出"哪部分是 WeSight 管的",可以随时删 WeSight 段而不影响其他字段。
  • 降低支持成本:减少"我升级 WeSight 之后 Claude Code 报 invalid key"类工单。
  • 后续 feature 解锁:mark 化后,可以支持"暂时禁用 WeSight 对该 CLI 的管理"等用户可控制开关。

六、验证清单

  • 复现 1 完成后,user 的 [features] + 注释仍在 config.toml
  • 复现 2 完成后,user 的 ANTHROPIC_API_KEY / FOO_TOKEN 仍在 settings.json
  • 复现 3 完成后,user 的 models.providers.<custom> 仍在 openclaw.json
  • 复现 4 完成后,cc-switch DB 中 settings_config 不含明文 key(只有 ${WESIGHT_APIKEY_*}
  • Step 6 后,已存在的 exec-approvals.json 不被自动覆写
  • 新增单元测试:mergeClaudeSettingsWithProvider 保留 __wesight_managed 之外的字段;buildCodexConfig 在已有 user 字段时走 merge 路径

七、补充:相关 issue

  • 详细的产品级设计动机和原语复用方案见姊妹 design proposal。
  • 配套的 renderer 侧增强(认证状态探测、UI 徽章)见姊妹 feature request。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    Status
    In Progress

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions