fix(multimodal): 视觉工具的空/畸形 data: URI 明确拒绝 + 入参嗅探与可行动报错
根因(从 checkpoints 取到的那一轮 tool_call 实际 ARGS 逐字节核实):
模型交给工具的 image 参数是 "data:image/png;base64," —— 只有前缀、
载荷为空(全长 22 字符)。两个加载器早就会剥 data: 前缀,但把空载荷
解码成零字节后照样挂上合法 MIME 放行:
image-crop → 零字节进 image.Decode ⇒ image: unknown format
vision → DataURI("image/png", nil) 发给上游 ⇒ 400 unsupported image
(模型复述不出入参里那条 58583 字符的 data: URI,只抄下了前缀。)
- 新增 checkInlinePayload:data: URI 解码后做内容嗅探(复用既有裁决)
- 空载荷 / 缺 ;base64 段 / 缺逗号 / 非法 base64 ⇒ 明确拒绝
- 报错含「不是图片」+ 嗅到的类型 + 支持格式 + 十六进制物证 + 可行动提示,
且不回显正文或 base64
- 裸 base64、http(s) URL、工具 schema、安全策略一律未动
- 测试:新增 16 个用例;全树 61 包 ok;4 个变异(含停用嗅探 ⇒ 5 用例红)
This commit is contained in:
1 parent
da1d23dd23
commit
789e3af74e
8 files changed
+636
-24
No files matched your search
@@ -1,6 +1,7 @@
|
||||
package vision
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -68,9 +69,14 @@ func TestParseImageRef(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestResolve_Base64 钉住 data: URI 那条路:载荷被解码成原始字节,MIME 取前缀
|
||||
// 里声明的那个。**载荷必须是真的图片字节** —— data: URI 带声明,解码后要过内容
|
||||
// 嗅探(image.go 的 checkInlinePayload),拿非图片字节(如 "hello")当载荷
|
||||
// 现在会被明确否决,而不是挂着 image/png 的声明送进模型。
|
||||
func TestResolve_Base64(t *testing.T) {
|
||||
t.Parallel()
|
||||
ref, err := ParseImageRef("data:image/png;base64,aGVsbG8=")
|
||||
png := pngBytes(t)
|
||||
ref, err := ParseImageRef(DataURI("image/png", png))
|
||||
if err != nil {
|
||||
t.Fatalf("ParseImageRef error: %v", err)
|
||||
}
|
||||
@@ -81,8 +87,8 @@ func TestResolve_Base64(t *testing.T) {
|
||||
if mime != "image/png" {
|
||||
t.Errorf("mime = %q, want image/png", mime)
|
||||
}
|
||||
if string(data) != "hello" {
|
||||
t.Errorf("data = %q, want hello", data)
|
||||
if !bytes.Equal(data, png) {
|
||||
t.Errorf("data = %d bytes, want the decoded payload (%d bytes)", len(data), len(png))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user