aa3139da68
新增 external_api 工具(GET/POST):agent 图可调外部 API。安全为先: - SSRF 防护 validateExternalURL/isBlockedIP:scheme 限 http/https;拒环回/内网 /链路本地(含 169.254.169.254 云元数据)/未指定 IP;重定向同样校验、限 3 跳。 - 可选 EXTERNAL_API_ALLOWLIST(逗号分隔主机,支持子域)收窄到白名单。 - 超时 10s + 响应体限 256KB。 - 校验逻辑纯函数,单测覆盖(内网/元数据/scheme/白名单,字面量 IP 离线判定)。 注册进 mcp-go dispatch(external_api → externalAPI)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
56 lines
1.7 KiB
Go
56 lines
1.7 KiB
Go
package mcp
|
||
|
||
import (
|
||
"net"
|
||
"testing"
|
||
)
|
||
|
||
func TestIsBlockedIP(t *testing.T) {
|
||
blocked := []string{"127.0.0.1", "10.1.2.3", "172.16.0.5", "192.168.1.1", "169.254.169.254", "0.0.0.0", "::1"}
|
||
for _, s := range blocked {
|
||
if !isBlockedIP(net.ParseIP(s)) {
|
||
t.Errorf("%s 应被拦截(内网/环回/元数据)", s)
|
||
}
|
||
}
|
||
allowed := []string{"8.8.8.8", "1.1.1.1", "93.184.216.34"}
|
||
for _, s := range allowed {
|
||
if isBlockedIP(net.ParseIP(s)) {
|
||
t.Errorf("%s 是公网,不应被拦截", s)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestValidateExternalURL_Scheme(t *testing.T) {
|
||
for _, raw := range []string{"ftp://x/y", "file:///etc/passwd", "not a url", "ws://h"} {
|
||
if reason, ok := validateExternalURL(raw, nil); ok {
|
||
t.Errorf("%q 应被拒(非 http/https), got ok reason=%q", raw, reason)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestValidateExternalURL_SSRF(t *testing.T) {
|
||
// 字面量 IP 不走真实 DNS,可离线判定。
|
||
for _, raw := range []string{"http://127.0.0.1/admin", "http://169.254.169.254/latest/meta-data/", "http://10.0.0.1/"} {
|
||
if _, ok := validateExternalURL(raw, nil); ok {
|
||
t.Errorf("%q 应被 SSRF 防护拦截", raw)
|
||
}
|
||
}
|
||
// 公网字面量放行。
|
||
if reason, ok := validateExternalURL("http://8.8.8.8/", nil); !ok {
|
||
t.Errorf("公网 IP 应放行, got %q", reason)
|
||
}
|
||
}
|
||
|
||
func TestValidateExternalURL_Allowlist(t *testing.T) {
|
||
allow := []string{"api.github.com"}
|
||
if _, ok := validateExternalURL("http://8.8.8.8/", allow); ok {
|
||
t.Error("白名单生效时,非白名单主机应被拒")
|
||
}
|
||
if !hostAllowed("api.github.com", allow) || !hostAllowed("sub.api.github.com", allow) {
|
||
t.Error("白名单主机及其子域应放行")
|
||
}
|
||
if hostAllowed("evil.com", allow) {
|
||
t.Error("非白名单主机不应放行")
|
||
}
|
||
}
|