Files
sundynix-agentix/sundynix-mcp-go/internal/mcp/external_test.go
T
Blizzard aa3139da68 feat(mcp-go): external_api 通用出站 HTTP 工具(带 SSRF 防护)
新增 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>
2026-06-18 11:58:45 +08:00

56 lines
1.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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("非白名单主机不应放行")
}
}