Files
summercms/modules/fetchguard/fetch_coverage_test.go
Jakub Zych 5e50b166ef refactor(10.2-01): nest framework packages under modules
- Move remaining beach packages and embedded admin assets\n- Rewrite framework, example, build, and gate paths
2026-09-28 02:21:02 +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")
}
}