| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- package fetch
- import (
- "context"
- "errors"
- "net/http"
- "net/netip"
- "strings"
- "sync/atomic"
- "testing"
- )
- func TestIsPublic(t *testing.T) {
- tests := map[string]bool{
- "93.184.215.14": true,
- "2606:4700:4700::1111": true,
- "127.0.0.1": false, // loopback
- "::1": false,
- "10.1.2.3": false, // private networks
- "172.16.0.1": false,
- "192.168.1.1": false,
- "fd12:3456::1": false,
- "169.254.169.254": false, // link-local: cloud metadata services
- "fe80::1": false,
- "100.100.1.1": false, // carrier-grade NAT, Tailscale
- "0.0.0.0": false,
- "::": false,
- "224.0.0.1": false, // multicast
- "255.255.255.255": false,
- "::ffff:192.168.1.1": false, // IPv4-mapped
- }
- for addr, want := range tests {
- if got := isPublic(netip.MustParseAddr(addr)); got != want {
- t.Errorf("isPublic(%s) = %v, want %v", addr, got, want)
- }
- }
- }
- func TestIsLocal(t *testing.T) {
- ctx := context.Background()
- for _, host := range []string{"localhost", "127.0.0.1", "::1", "192.168.1.20", "169.254.169.254"} {
- if !IsLocal(ctx, host) {
- t.Errorf("IsLocal(%q) = false, want true", host)
- }
- }
- for _, host := range []string{"93.184.215.14", "2606:4700:4700::1111", "name.invalid"} {
- if IsLocal(ctx, host) {
- t.Errorf("IsLocal(%q) = true, want false", host)
- }
- }
- }
- func TestGuardBlocksLocalPage(t *testing.T) {
- var hits atomic.Int32
- srv := serve(t, func(w http.ResponseWriter, r *http.Request) { hits.Add(1) })
- f := New(false)
- f.Retries = 0
- _, err := f.Fetch(context.Background(), mustParse(t, srv.URL+"/page"))
- var blocked *BlockedError
- if !errors.As(err, &blocked) {
- t.Fatalf("error = %v, want a BlockedError", err)
- }
- if !strings.Contains(err.Error(), "is a local or private address (127.0.0.1)") {
- t.Errorf("error = %q, want an explanation", err)
- }
- if hits.Load() != 0 {
- t.Errorf("server received %d requests, want none", hits.Load())
- }
- }
- func TestGuardBlocksRedirectToLocal(t *testing.T) {
- var localHits atomic.Int32
- local := serve(t, func(w http.ResponseWriter, r *http.Request) { localHits.Add(1) })
- public := serve(t, func(w http.ResponseWriter, r *http.Request) {
- http.Redirect(w, r, local.URL+"/admin", http.StatusFound)
- })
- // Treat the "public" server as being on the internet.
- publicAddr := netip.MustParseAddrPort(strings.TrimPrefix(public.URL, "http://"))
- defer func(old func(netip.AddrPort) bool) { permitted = old }(permitted)
- permitted = func(ap netip.AddrPort) bool { return ap == publicAddr }
- f := New(false)
- f.Retries = 0
- _, err := f.Fetch(context.Background(), mustParse(t, public.URL+"/story"))
- var blocked *BlockedError
- if !errors.As(err, &blocked) {
- t.Fatalf("error = %v, want a BlockedError", err)
- }
- if localHits.Load() != 0 {
- t.Errorf("local server received %d requests, want none", localHits.Load())
- }
- }
- func TestFetchRefusesFileRedirect(t *testing.T) {
- srv := serve(t, func(w http.ResponseWriter, r *http.Request) {
- http.Redirect(w, r, "file:///etc/hosts", http.StatusFound)
- })
- f := testFetcher()
- f.Retries = 0
- page, err := f.Fetch(context.Background(), mustParse(t, srv.URL+"/page"))
- if err == nil || page != nil {
- t.Fatalf("Fetch followed a redirect to a local file: page = %+v, err = %v", page, err)
- }
- if !strings.Contains(err.Error(), "unsupported protocol scheme") {
- t.Errorf("error = %v", err)
- }
- }
- func TestProxyAddrs(t *testing.T) {
- // Set both spellings: which one wins depends on the x/net version.
- for _, name := range []string{"HTTP_PROXY", "http_proxy"} {
- t.Setenv(name, "http://10.0.0.2")
- }
- for _, name := range []string{"HTTPS_PROXY", "https_proxy"} {
- t.Setenv(name, "proxy.lan:3128")
- }
- got := proxyAddrs()
- for _, want := range []string{"10.0.0.2:80", "proxy.lan:3128"} {
- if !got[want] {
- t.Errorf("proxyAddrs() = %v, missing %s", got, want)
- }
- }
- }
|