Skip to content

[code-review] the .ggcode/a2a.yaml override never expands ${VAR} references while the main config, vendors.yaml and im.yaml all do - writing api_key: ${A2A_KEY} there merges the literal string over the real value and auth fails silently; onboarding with the A2A checkbox replaces the whole cfg.A2A struct with a zero-value (wiping host/port/auth on re-run) instead of just clearing Disabled; anthropic_bootstrap's fallback ignores ANTHROPIC_BASE_URL entirely and matches hosts by 'bigmodel' substring, sending third-party proxy tokens to the built-in official endpoint while silently dropping the user's real URL; and keys.env loading treats an empty-but-set shell env var as precedence, so 'export OPENAI_API_KEY=' placeholders shadow the real persisted key every startup #1515

Description

@topcheer

文件行号

internal/config/a2a_override.go L43-46(yaml.Unmarshal(migrated, &override) 全文件无 Expand)+ 唯一非测试调用链 instance.go:665-667(LoadA2AOverride → MergeA2AConfig 仅非零覆盖无展开)对照主配置 config.go L1145-1146(runtimeEnvLookup(raw) 展开——vendors.yaml/im.yaml 同样展开 external_files.go L333/L356);cmd/ggcode/onboard.go L93-95(if result.A2A { cfg.A2A = config.A2AConfig{Disabled: false} } 整体零值替换)+ L114-115 cfg.Save() 持久化;internal/config/anthropic_bootstrap.go L69(strings.Contains(strings.ToLower(rawURL), p.substring))+ L167(模式表仅 {"bigmodel","zai","cn-coding-anthropic"})+ L151(fallback matchKnownAnthropicEndpoint(cfg, apiKey, model) 不接收 baseURL)+ L182-191/L58-60(token 写入内置端点并选中)+ 触发门 L1554-1564(首启判定);api_keys.go L494-497(if _, exists := os.LookupEnv(name); exists { continue }——空值也 exists=true)+ L526-527(writeKeysEnvTo 无条件合并写入)+ load 两路 L469/L478;a2a_override.go L21-23/L44-45(YAML 解析错误静默 return nil 对照主 Load L1090-1092 返回 error)+ L93-101(OAuth2/OIDC/MTLS 指针非 nil 即整对象替换,违背文件头 L12 "Only non-zero fields" 声明——同层 api_key 却逐字段合并)

问题

案 A(Med):a2a.yaml 覆盖不展开 ${VAR}——同一配置体系两种语法语义

  • .ggcode/a2a.yamlauth: {api_key: ${A2A_KEY}} → 合并后覆盖全局真值为字面量 "${A2A_KEY}" → 鉴权必失败且零告警——主配置/vendors/im 全展开唯 a2a 覆盖不展开
    :LoadA2AOverride 内套用 ExpandEnvRecursiveWithLookup。

案 B(Low-Med):onboard 勾选 A2A 整清既有配置

  • 重跑 onboard(首跑失败重试场景)勾选 A2A → Host/Port/MaxTasks/TaskTimeout/Auth(APIKey/OAuth2/OIDC/MTLS)全灭并持久化——意图只是启用(应 cfg.A2A.Disabled=false)
    :改单字段赋值。

案 C(Low-Med):anthropic_bootstrap 子串匹配+无视 URL 的 fallback——第三方 token 错发官方端点

  • 首启 + BASE_URL 含 "bigmodel" 子串(第三方中转 xxx-bigmodel-proxy.example.com)+ AUTH_TOKEN → upsert 精确匹配不中 → fallback 不接收 baseURL 直接把 token 写入内置 zai/cn-coding-anthropic 端点并选中——第三方 token 发往官方主机(鉴权失败+token 错发)且用户真实 base_url 被静默丢弃
    :fallback 传入 baseURL 比对;子串匹配收紧为 host 后缀。

案 D(Low-Med):空 shell 环境变量屏蔽 keys.env 真 key

  • shell rc 有 export OPENAI_API_KEY=(空占位)→ onboard 落盘真 key → 每次启动 LookupEnv exists=true 跳过 → 报缺 key 用户误以为 onboard 丢 key
    :存在且非空才跳过。

案 E(Low×2)

  • a2a.yaml 解析错误静默吞(写坏文件覆盖整体失效零提示);OAuth2/OIDC/MTLS 指针整对象替换与"只覆盖非零字段"声明颗粒度不一致
    :解析失败至少 debug.Log+提示;指针下钻逐字段合并或修正声明。

翻案记录(复核推翻初审)

初审小案 a(单引号不转义往返损坏)不成立——解析端只剥恰好一对首尾引号,a'b/ab'/'ab' 各构造均正确还原;仅含换行值破坏(env 值本身非法)。

干净项

onboard:#1435 修复区在位(Windows 降级双保险/keys.env 持久化)/自定义 provider 分支/预设同步无回归。api_keys:读写原子性(AtomicWriteFile 0600+lockConfigFile)/注释行/双引号 Unquote/MigratePlaintextAPIKeys/instance 前缀防碰撞/写失败中止顺序全对。config.go:加载优先级链方向正确/首启分支/skipAutoConfig 逃生门/Validate 守卫/#591/#284/#610/#1422-B2/#959/#736 各已修项在位——空 switch+重复 debug.Log 为无功能影响残留(信息级);Load 每次重写为有意设计(diskStrSnap 防误删)。

排除

#1435(16bc1971)已修项;#1513 commands 族;config 包早轮各案无回归。

严重程度

Med + Low-Med×3 + Low×2


独立复核确认:四主案原文与调用链穷尽(含小写变体 grep)、主配置 expand 对照、字面量覆盖推演、字段丢失清单、案 3 全文独立重建(模式表/fallback 签名/首启门)、LookupEnv 空值语义、小案 a 构造还原反证。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions