package lua import ( "encoding/json" "strings" "testing" ) // ★★ zcode 的 Messages 端点鉴权是 **Bearer**,不是 x-api-key。 // // 背景(本轮**最重要的一处证据冲突**,必须写清): // // 任务前提是「anthropic 协议 ⇒ x-api-key,所以 channels-zcode 继承基座就 401」。 // 但逆向参考(vendor/channel-pack,全部结论来自真实流量)明确写着相反的事: // // src/zcode-transport.ts:121-125 buildChannelRequest() // headers = buildZcodeHeaders(credential, { json: true }) // headers.Authorization = `Bearer ${token}` // token = zcode_jwt // src/zcode-upstream.ts:83-108 buildZcodeHeaders() // 只发 User-Agent / HTTP-Referer / X-Device-Mid / anthropic-version … // Authorization 是**可选注入项**,且全仓 `grep -rn "x-api-key" src/` = **0 次** // // 也就是说 zcode 的这条路径是「**Anthropic 请求体形状 + Bearer 鉴权**」—— // 协议与鉴权是两个独立维度,被同一家上游拆开了。 // // 为什么修复前它其实是**对的**:zcode 未声明 credential_shape ⇒ api_key = 凭据里的 // access_token = zcode_jwt;而基座当时没有 build_headers ⇒ 内核兜底发了 // `Authorization: Bearer `,与参考逐字一致。 // // ⇒ 真正的风险来自**本轮的正向修复**:给 anthropic 基座补 x-api-key 之后, // 若 zcode 只是「继承」而不覆盖,它就会从 Bearer 变成 x-api-key ⇒ 立刻 401。 // 所以 zcode 必须**显式覆盖** build_headers,把 Bearer 钉住。 // // 判据咬真实产出(vm.BuildHeaders 是内核发请求前的那条路径),不造 HTTP 假上游。 func TestZcodeBuildHeadersUsesBearerNotXAPIKey(t *testing.T) { vm, _ := zcodeHarness(t) if !vm.HasFn("channels-zcode", "build_headers") { t.Fatal("★ channels-zcode 必须有**自己的** build_headers(覆盖基座)," + "否则会继承基座的 x-api-key 而丢掉参考要求的 Bearer") } hdrs, err := vm.BuildHeaders("channels-zcode", map[string]interface{}{ "api_key": "ZCODE-JWT-ABC", "url": "https://zcode.z.ai/api/v1/zcode-plan/anthropic/v1/messages", "method": "POST", "body": `{"model":"GLM-5.3"}`, }) if err != nil { t.Fatalf("BuildHeaders: %v", err) } // ① 鉴权必须是 Bearer (参考 zcode-transport.ts:125) if got := hdrs["Authorization"]; got != "Bearer ZCODE-JWT-ABC" { t.Fatalf("★ zcode 的 Authorization 必须是 `Bearer `,实得 %q(头: %v)\n"+ " 参考 zcode-transport.ts:125 逐字如此;全仓没有一处发 x-api-key", got, keysOf(hdrs)) } // ② 不得同时发 x-api-key(那就是双鉴权;且参考从不发它) if v, has := hdrs["x-api-key"]; has { t.Fatalf("★ zcode 不得发 x-api-key(实得 %q)—— 参考全程没有这个头,"+ "发了就是两套鉴权方案", v) } // ③ 协议头 anthropic-version 必须保留(它与鉴权无关,参考也发) if got := hdrs["anthropic-version"]; got != "2023-06-01" { t.Fatalf("anthropic-version 应保留为 2023-06-01,实得 %q", got) } // ④ 键集合恰好 2 个,不多不少(防止顺手塞进别的身份头) if len(hdrs) != 2 { t.Fatalf("头集合应恰好 2 个(Authorization + anthropic-version),实得 %d: %v", len(hdrs), keysOf(hdrs)) } t.Logf("✓ zcode 头: %v", keysOf(hdrs)) } // 配对判据:**基座**必须发 x-api-key(这是 P1-1 的正向要求,也是与 zcode 的差异点)。 // // 两条判据放在一起才有意义:它们证明「同一份基座 + 显式覆盖」能同时满足 // 两种鉴权,而不是把整个 anthropic 生态压成一种。 func TestAnthropicBaseAndZcodeDifferOnAuthHeader(t *testing.T) { vm, _ := zcodeHarness(t) base, err := vm.BuildHeaders("anthropic", map[string]interface{}{"api_key": "SK-ANT"}) if err != nil { t.Fatalf("anthropic BuildHeaders: %v", err) } zc, err := vm.BuildHeaders("channels-zcode", map[string]interface{}{"api_key": "JWT-Z"}) if err != nil { t.Fatalf("zcode BuildHeaders: %v", err) } // 基座:x-api-key,无 Authorization if base["x-api-key"] != "SK-ANT" { t.Fatalf("★ anthropic 基座必须发 x-api-key,实得 %v", base) } if _, has := base["Authorization"]; has { t.Fatalf("★ anthropic 基座不得发 Authorization(x-api-key 才是它的鉴权),实得 %v", base) } // zcode:Authorization,无 x-api-key if zc["Authorization"] == "" { t.Fatalf("★ zcode 必须发 Authorization: Bearer,实得 %v", zc) } if _, has := zc["x-api-key"]; has { t.Fatalf("★ zcode 不得发 x-api-key,实得 %v", zc) } // 两者必须**真的不同**——若某天有人删掉 zcode 的覆盖,这条会立刻红 if base["x-api-key"] == "" || zc["Authorization"] == "" { t.Fatal("两者鉴权头方案必须各自成立") } b, _ := json.Marshal(base) z, _ := json.Marshal(zc) if string(b) == string(z) { t.Fatalf("★ 基座与 zcode 的鉴权方案不能相同:%s", b) } // 协议头必须相同(anthropic-version 与鉴权无关) if base["anthropic-version"] != zc["anthropic-version"] { t.Fatalf("两者都必须发 anthropic-version:base=%v zcode=%v", base, zc) } if strings.Contains(string(b), "Bearer") { t.Fatalf("基座不该出现 Bearer:%s", b) } t.Logf("✓ 基座=%s | zcode=%s", b, z) } // 无凭据时:两边都只发协议头,都不发空鉴权(空值会让上游回认证失败, // 而真因是「没配置凭据」,会把人带偏)。 func TestAuthHeadersOmitEmptyCredential(t *testing.T) { vm, _ := zcodeHarness(t) for _, name := range []string{"anthropic", "channels-zcode"} { h, err := vm.BuildHeaders(name, map[string]interface{}{}) if err != nil { t.Fatalf("%s: %v", name, err) } if h["anthropic-version"] != "2023-06-01" { t.Errorf("%s 仍须发 anthropic-version,实得 %v", name, h) } if _, has := h["x-api-key"]; has { t.Errorf("%s 无凭据时不得发空 x-api-key,实得 %v", name, h) } if _, has := h["Authorization"]; has { t.Errorf("%s 无凭据时不得发空 Authorization,实得 %v", name, h) } } }