package core import ( "encoding/json" "os" "strings" "testing" ) // CodeArts 的华为云 SDK-HMAC-SHA256 签名必须与参考**逐字节一致**,否则服务端 // 返回 `verify ak sk signature fail`,而这个平台的领取就永远失败 —— 且失败原因 // 会被误读成"账号问题"。此前插件里的实现是凭印象编的另一套形状 // (Credential={ak}/{ts}/{nonce}/sdk_request),必然验签失败。 // // 期望值由参考实现(sign.ts:signRequestHuawei,Node + node:crypto)生成: // // node tools/codearts_sig_expected.mjs > internal/core/codearts_sig_expected.json // // 判据覆盖:canonical request 的拼装、string-to-sign 格式、最终 signature、 // 以及参与签名的头列表(顺序与内容)。 func TestCodeArtsSignatureMatchesReference(t *testing.T) { raw, err := os.ReadFile("codearts_sig_expected.json") if err != nil { t.Skipf("缺差分数据(跳过): %v", err) } var cases []struct { Method string `json:"method"` URL string `json:"url"` Body string `json:"body"` AK string `json:"ak"` SK string `json:"sk"` ST string `json:"st"` Canonical string `json:"canonical"` StringToSign string `json:"string_to_sign"` Signature string `json:"signature"` SignedHeaders string `json:"signed_headers"` } if err := json.Unmarshal(raw, &cases); err != nil { t.Fatal(err) } if len(cases) == 0 { t.Fatal("差分数据为空") } c := kernelTestCore(t) ps := c.Plugins() src, err := readBundledChannels(t) if err != nil { t.Fatal(err) } if err := ps.LoadSource("channels", src); err != nil { t.Fatal(err) } for i, tc := range cases { got, err := ps.Call("channels", "__codearts_sign_probe", map[string]interface{}{ "method": tc.Method, "url": tc.URL, "body": tc.Body, "ak": tc.AK, "sk": tc.SK, "st": tc.ST, "date": "20261013T150405Z", }) if err != nil { t.Fatalf("case %d probe: %v", i, err) } w, _ := got.(map[string]interface{}) m, _ := w["json"].(map[string]interface{}) if m == nil { t.Fatalf("case %d 无 json: %#v", i, got) } if m["error"] != nil { t.Fatalf("case %d 探针报错: %v", i, m["error"]) } if s, _ := m["canonical"].(string); s != tc.Canonical { t.Errorf("case %d canonical 不一致\n 实得: %q\n 参考: %q", i, s, tc.Canonical) } if s, _ := m["string_to_sign"].(string); s != tc.StringToSign { t.Errorf("case %d string-to-sign 不一致\n 实得: %q\n 参考: %q", i, s, tc.StringToSign) } if s, _ := m["signature"].(string); s != tc.Signature { t.Errorf("case %d signature 不一致: 实得 %s 参考 %s", i, s, tc.Signature) } if s, _ := m["signed_headers"].(string); s != tc.SignedHeaders { t.Errorf("case %d SignedHeaders 不一致: 实得 %q 参考 %q", i, s, tc.SignedHeaders) } // Authorization 头必须是 `SDK-HMAC-SHA256 Access=...`(不是 Credential=) if s, _ := m["authorization"].(string); len(s) == 0 || s[:len("SDK-HMAC-SHA256 Access=")] != "SDK-HMAC-SHA256 Access=" { t.Errorf("case %d Authorization 形状应为 SDK-HMAC-SHA256 Access=…,实得 %q", i, s) } // 签名后追加的头绝不能进 canonical if s, _ := m["canonical"].(string); len(s) > 0 { for _, bad := range []string{"Agent-Type", "X-Language", "agent-type", "x-language"} { if strings.Contains(s, bad) { t.Errorf("case %d canonical 里出现了签名后追加的头 %s(会导致验签失败)", i, bad) } } } } }