X2 C1-3(自排工具面与提示词): - request_think(thinktool.go,照 transcode/tool.go 范式)+ reason 必填/urgency 闭集 + 双守门 (安全闸 + owner 判定:群聊/陌生人一律拒) - **成对**进两张唤醒态允许集(quiet + awake;CAPACITY_TOOL_SETS 是整体替换) - 提示词抽共用段落 + 自排前缀优先(不枚举工具清单);面板加 self_wake 标签与筛选轴 - 4 个变异自检(拿掉 owner 判定/只加一张表/放宽 urgency/前端标签恒等) X3 收口(与 X2 共用 wire_services.go ⇒ 一并落): - 删掉第三份 SSRF 实现(guard.go),改由 io.ValidateRequestURL 唯一判定 + 薄门面(保两个错误身份) - 浏览器会话挂进 a.close(真机:真 Chrome 被关、无残留、生产未扰动) - a.wireBrowser() 归位到 wire_runtime.go,删掉 ioToolOptions 的临时挂点 - 5 个变异自检 顺带补提交 C1-2 漏掉的 internal/config/runtimeconfig/backthink_test.go(ed50053 漏 add)。
81 lines
3.6 KiB
Go
81 lines
3.6 KiB
Go
package io
|
||
|
||
import (
|
||
"context"
|
||
"errors"
|
||
"testing"
|
||
)
|
||
|
||
// TestValidateRequestURL_DeniedByDefault 钉住**共享判定本体**的默认拒绝面(收口 X3):
|
||
// 收口之前浏览器侧另有一份同策实现,两份各自被自己的用例覆盖;收口之后浏览器侧
|
||
// (`internal/capability/browser` 的守卫用例)与本用例打的是**同一张表、同一份代码**。
|
||
func TestValidateRequestURL_DeniedByDefault(t *testing.T) {
|
||
t.Parallel()
|
||
denied := []string{
|
||
"http://127.0.0.1/",
|
||
"http://[::1]/",
|
||
"http://192.168.1.10/admin",
|
||
"http://10.0.0.5/",
|
||
"http://172.16.3.4/",
|
||
"http://169.254.169.254/latest/meta-data/",
|
||
"http://0.0.0.0/",
|
||
"file:///etc/passwd",
|
||
"ftp://example.com/",
|
||
"http:///no-host",
|
||
"",
|
||
}
|
||
for _, raw := range denied {
|
||
if _, err := ValidateRequestURL(context.Background(), raw, nil); err == nil {
|
||
t.Errorf("ValidateRequestURL(%q) = nil,应当拒绝", raw)
|
||
}
|
||
}
|
||
// 网段拒绝必须**保住 ErrSSRF 身份**:调用方(含浏览器侧的门面)按 errors.Is 分类降级。
|
||
if _, err := ValidateRequestURL(context.Background(), "http://127.0.0.1/", nil); !errors.Is(err, ErrSSRF) {
|
||
t.Errorf("回环目标的错误丢了 ErrSSRF 身份: %v", err)
|
||
}
|
||
}
|
||
|
||
// TestValidateRequestURL_AllowlistEntryForms 钉住白名单条目的**全部写法**都按主机名匹配。
|
||
// 这是收口 X3 的回归网:完整 URL 写法(以及裸 IPv6 字面量)原先只有被删掉的那份
|
||
// 浏览器侧实现认;合并成一份时若丢掉它,面板里写 `http://host:port` 的白名单会**静默
|
||
// 失效**(改前生效、改后不生效,没有一条日志会说出这件事)。
|
||
func TestValidateRequestURL_AllowlistEntryForms(t *testing.T) {
|
||
t.Parallel()
|
||
cases := []struct{ entry, raw string }{
|
||
{"127.0.0.1", "http://127.0.0.1:7777/x"},
|
||
{"127.0.0.1:9", "http://127.0.0.1:7777/x"},
|
||
{"http://127.0.0.1:9999/", "http://127.0.0.1:7777/x"},
|
||
{"localhost", "http://localhost:8080/x"},
|
||
{"LocalHost:8080", "http://LOCALHOST/x"},
|
||
{"http://localhost:8080", "http://localhost/x"},
|
||
{"[::1]", "http://[::1]:9999/x"},
|
||
{"::1", "http://[::1]:9999/x"},
|
||
}
|
||
for _, tc := range cases {
|
||
if _, err := ValidateRequestURL(context.Background(), tc.raw, []string{tc.entry}); err != nil {
|
||
t.Errorf("条目 %q 未放行 %q: %v", tc.entry, tc.raw, err)
|
||
}
|
||
}
|
||
}
|
||
|
||
// TestValidateRequestURL_AllowlistDoesNotOverreach 钉住**放行的边界**:白名单只放行它
|
||
// 自己那条主机名,且不依赖 DNS 的放行面照旧(公网字面 IP)。
|
||
func TestValidateRequestURL_AllowlistDoesNotOverreach(t *testing.T) {
|
||
t.Parallel()
|
||
if _, err := ValidateRequestURL(context.Background(), "http://93.184.216.34/", nil); err != nil {
|
||
t.Fatalf("公网字面 IP 被拒: %v", err)
|
||
}
|
||
// 白名单按主机名字符串比较:写 `localhost` 不等于放行 `127.0.0.1`(是两个名字)。
|
||
if _, err := ValidateRequestURL(context.Background(), "http://127.0.0.1/", []string{"localhost"}); !errors.Is(err, ErrSSRF) {
|
||
t.Errorf("条目 localhost 不该放行 127.0.0.1(实得 %v)", err)
|
||
}
|
||
// 无关的 URL 条目(主机是别的名字)不能被「看谁像 URL」放过。
|
||
if _, err := ValidateRequestURL(context.Background(), "http://127.0.0.1/", []string{"http://other:1/"}); !errors.Is(err, ErrSSRF) {
|
||
t.Errorf("无关的 URL 条目不该放行 127.0.0.1(实得 %v)", err)
|
||
}
|
||
// 空条目不能让任何主机命中(面板里删空一行 = 不放行任何东西)。
|
||
if _, err := ValidateRequestURL(context.Background(), "http://127.0.0.1/", []string{"", " "}); !errors.Is(err, ErrSSRF) {
|
||
t.Errorf("空白条目不该放行任何主机(实得 %v)", err)
|
||
}
|
||
}
|