返回 DeepSeek-Reasonix
webfetch_ssrf_test.go
根目录 / internal / tool / builtin / webfetch_ssrf_test.go
1 package builtin
2
3 import (
4 "context"
5 "encoding/json"
6 "net"
7 "net/http"
8 "net/http/httptest"
9 "strings"
10 "testing"
11 )
12
13 func TestBlockedFetchIP(t *testing.T) {
14 blocked := []string{
15 "169.254.169.254", // cloud metadata (link-local)
16 "10.1.2.3", // RFC1918
17 "172.16.5.6", // RFC1918
18 "192.168.1.1", // RFC1918
19 "0.0.0.0", // unspecified
20 "fe80::1", // IPv6 link-local
21 "fc00::1", // IPv6 unique-local
22 "::ffff:10.0.0.1", // IPv4-mapped private
23 "100.100.100.200", // Alibaba Cloud metadata (CGNAT)
24 "100.64.0.1", // RFC 6598 shared space
25 "::ffff:100.100.100.1", // IPv4-mapped CGNAT
26 }
27 for _, s := range blocked {
28 if !blockedFetchIP(net.ParseIP(s)) {
29 t.Errorf("%s should be blocked", s)
30 }
31 }
32 allowed := []string{"8.8.8.8", "1.1.1.1", "127.0.0.1", "::1", "93.184.216.34"}
33 for _, s := range allowed {
34 if blockedFetchIP(net.ParseIP(s)) {
35 t.Errorf("%s should be allowed", s)
36 }
37 }
38 }
39
40 // TestWebFetchAllowsLoopback proves the guard doesn't break normal fetches: a
41 // loopback dev server (httptest binds 127.0.0.1) stays reachable.
42 func TestWebFetchAllowsLoopback(t *testing.T) {
43 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
44 _, _ = w.Write([]byte("hello from localhost"))
45 }))
46 defer srv.Close()
47
48 args, _ := json.Marshal(map[string]any{"url": srv.URL})
49 out, err := webFetch{}.Execute(context.Background(), args)
50 if err != nil {
51 t.Fatalf("loopback fetch should succeed, got %v", err)
52 }
53 if !strings.Contains(out, "hello from localhost") {
54 t.Fatalf("body missing: %q", out)
55 }
56 }
57
58 // TestWebFetchRefusesLinkLocal proves a fetch aimed at the cloud-metadata
59 // endpoint is refused at dial time (no packet leaves the host).
60 func TestWebFetchRefusesLinkLocal(t *testing.T) {
61 args, _ := json.Marshal(map[string]any{"url": "http://169.254.169.254/latest/meta-data/"})
62 _, err := webFetch{}.Execute(context.Background(), args)
63 if err == nil {
64 t.Fatal("fetch to 169.254.169.254 should be refused")
65 }
66 if !strings.Contains(err.Error(), "169.254.169.254") {
67 t.Fatalf("error should name the refused address, got %v", err)
68 }
69 }
70
70 lines GO