orchard
mirrorEvery site I host, in one repo, along with the Cloudflare Tunnel and Caddy that front them. It's all Go, Vite, and SQLite, and it runs on a desktop at home with nothing listening on an inbound port.
blogbuncaddycloudflare-tunneldockergogolanghomelabhtml-templatemonorepoself-hostedseosqlitestatic-sitetypstuptime-monitoringviteweb-analytics
1package tools
2
3import (
4 "context"
5 "net"
6 "net/http"
7 "net/http/httptest"
8 "strings"
9 "testing"
10 "time"
11)
12
13func TestPublicURLRefusesEverythingButHttp(t *testing.T) {
14 for _, c := range []struct{ in, want string }{
15 {"file:///home/ubuntu/Downloads/070726 WellsFargo.pdf", "already in this conversation"},
16 {"file:///etc/passwd", "no filesystem"},
17 {"ftp://example.com/x", "only http and https"},
18 {"gopher://example.com", "only http and https"},
19 {"example.com/page", "needs to start with https"},
20 {"http://orchard-auth:8000/verify", "not a public address"},
21 {"http://localhost:8000/", "not a public address"},
22 {"http://orchard-llm:8000/v1/models", "orchard tools"},
23 } {
24 got, err := publicURL(c.in)
25 if err == nil {
26 t.Errorf("%q was allowed as %q", c.in, got)
27 continue
28 }
29 if !strings.Contains(err.Error(), c.want) {
30 t.Errorf("%q gave %q, want it to mention %q", c.in, err, c.want)
31 }
32 }
33}
34
35func TestPublicURLAllowsOrdinaryPages(t *testing.T) {
36 for _, ok := range []string{
37 "https://en.wikipedia.org/wiki/Asmongold",
38 "http://example.com/a/b?c=d",
39 "https://www.espn.com/soccer/team/fixtures/_/id/364/liverpool",
40 } {
41 if _, err := publicURL(ok); err != nil {
42 t.Errorf("%q was refused: %v", ok, err)
43 }
44 }
45}
46
47// The address ranges that reach this machine or its network. IsPrivate does not
48// cover carrier grade NAT or the metadata address, and both matter.
49func TestIsPublicIP(t *testing.T) {
50 for _, bad := range []string{
51 "127.0.0.1", "::1", "10.0.0.5", "172.18.0.3", "192.168.1.10",
52 "169.254.169.254", "100.64.0.1", "0.0.0.0", "fe80::1", "fd00::1",
53 } {
54 if isPublicIP(net.ParseIP(bad)) {
55 t.Errorf("%s was treated as public", bad)
56 }
57 }
58 for _, good := range []string{"1.1.1.1", "93.184.216.34", "2606:4700:4700::1111"} {
59 if !isPublicIP(net.ParseIP(good)) {
60 t.Errorf("%s was treated as internal", good)
61 }
62 }
63}
64
65// The string check can be got past by a name that resolves inward, so the real
66// fence is on the dial. This proves it by pointing a real client at a real
67// loopback server, which is exactly the shape of the attack.
68func TestTheClientRefusesToDialLoopback(t *testing.T) {
69 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
70 _, _ = w.Write([]byte("this must never be read"))
71 }))
72 defer srv.Close()
73
74 c := publicClient(5 * time.Second)
75 req, err := http.NewRequestWithContext(context.Background(), "GET", srv.URL, nil)
76 if err != nil {
77 t.Fatal(err)
78 }
79 resp, err := c.Do(req)
80 if err == nil {
81 resp.Body.Close()
82 t.Fatal("a loopback address was fetched")
83 }
84 if !strings.Contains(err.Error(), "refusing") {
85 t.Errorf("err = %v, want the fence to have refused it", err)
86 }
87}
88
89// get() must not put a host in the penalty box for being internal, or one bad
90// suggestion from the model locks out a host that never even answered.
91func TestTheFenceDoesNotTripTheBreaker(t *testing.T) {
92 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
93 defer srv.Close()
94
95 d := &Deps{
96 HTTP: srv.Client(),
97 Public: publicClient(5 * time.Second),
98 Now: time.Now,
99 Guard: NewGuard(time.Minute),
100 }
101 if _, err := get(context.Background(), d, srv.URL, ""); err == nil {
102 t.Fatal("loopback was fetched through get")
103 }
104 host := hostOf(srv.URL)
105 if blocked, _ := d.Guard.Blocked(host); blocked {
106 t.Error("the breaker tripped on a refusal that was ours, not the host's")
107 }
108}