Files
summercms/fetchguard/fetch_coverage_test.go
Jakub Zych 98dd09847a test(06-05): close framework coverage gaps in bouncer, surf, wire, fetchguard
- Registry neither-interface, nil-registry, and authenticate default
- MemoryStore sweep actually drops expired entries
- RegisterHouseMiddlewareFactory duplicate-name failure
- pathScopedCORS unmatched path plus empty-raw-group introspection
- Time UnmarshalJSON +00:00/Z and PublicOnlyMode host/IP cases
2026-09-19 21:08:04 +02:00

99 lines
2.8 KiB
Go

package fetchguard
import (
"errors"
"io"
"net"
"net/http"
"net/http/httptest"
"testing"
"time"
)
// Gap (f): PublicOnlyMode private-IP rejection is already asserted by
// TestFetchPrivateIPBlockedInBothModes/PublicOnlyMode. This file adds
// PublicOnlyMode accepting any host when the dial-time IP check is
// satisfied (loopback httptest with skipReservedCheck — a live public
// IP dial would require outbound network and is not asserted here).
func TestFetchPublicOnlyModeAcceptsAnyHostWhenPublic(t *testing.T) {
srv := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
io.WriteString(w, "ok")
}))
t.Cleanup(srv.Close)
res, err := Fetch(t.Context(), srv.URL, withTestLoopback(srv, Policy{
Mode: PublicOnlyMode,
MaxBytes: 1024,
Timeout: 2 * time.Second,
}), nil)
if err != nil {
t.Fatalf("PublicOnlyMode must accept the httptest host: %v", err)
}
if string(res.Body) != "ok" {
t.Fatalf("body = %q", res.Body)
}
}
func TestFetchPublicOnlyModePrivateIPRejected(t *testing.T) {
// Explicit restatement of the private-IP case under PublicOnlyMode so
// this coverage file names both halves of gap (f).
srv := httptest.NewTLSServer(http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
t.Error("handler must not run for a private dial")
}))
t.Cleanup(srv.Close)
_, err := Fetch(t.Context(), srv.URL, Policy{
Mode: PublicOnlyMode,
Timeout: 2 * time.Second,
MaxBytes: 1024,
}, nil)
if reasonFrom(t, err) != ReasonPrivateIP {
t.Fatalf("reason = %q, want %s", reasonFrom(t, err), ReasonPrivateIP)
}
}
func TestHostAllowedExactAndDottedSuffix(t *testing.T) {
allowed := []string{"discogs.com", ""}
if !hostAllowed("discogs.com", allowed) {
t.Fatal("exact match")
}
if !hostAllowed("api.discogs.com", allowed) {
t.Fatal("dotted-suffix match")
}
if hostAllowed("evil-discogs.com", allowed) {
t.Fatal("raw suffix must not match")
}
if hostAllowed("example.test", allowed) {
t.Fatal("unrelated host")
}
}
func TestMapTransportErrorReasons(t *testing.T) {
if got := mapTransportError(errPrivateIP); got.Reason != ReasonPrivateIP {
t.Fatalf("private_ip = %s", got.Reason)
}
dns := &net.DNSError{Err: "no such host", Name: "nope.test", IsNotFound: true}
if got := mapTransportError(dns); got.Reason != ReasonUnresolvable {
t.Fatalf("dns = %s", got.Reason)
}
if got := mapTransportError(errors.New("connection reset")); got.Reason != ReasonNetworkError {
t.Fatalf("other = %s", got.Reason)
}
}
func TestErrorStringWithoutInner(t *testing.T) {
e := &Error{Reason: ReasonScheme}
if e.Error() != "fetchguard: scheme" {
t.Fatalf("Error() = %q", e.Error())
}
var nilE *Error
if nilE.Error() != "fetchguard: error" {
t.Fatalf("nil Error() = %q", nilE.Error())
}
if nilE.Unwrap() != nil {
t.Fatal("nil Unwrap")
}
}