check / check (push) Waiting to run
The image request now holds only its source and its transform, exactly what the variant key covers. The /v1/image/ handler checks the allowlist and verifies the signature itself with Signer.Verify(req, sig, expires). Both image handlers keep the URL's expiry as a local value for Cache-Control. The image service holds no signer, allowlist or AllowHTTP, and ValidateRequest is gone. The fetcher takes the source and builds its URL with Source.URL from its own AllowHTTP, so no URL string is built and parsed again. chooseAutoFormat returns the format it chooses instead of writing into the request. The signed URL format and every route's answers are unchanged. Model: opus-5-5
77 lines
2.1 KiB
Go
77 lines
2.1 KiB
Go
package httpfetcher
|
|
|
|
import (
|
|
"errors"
|
|
"net"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// TestNewUsesCheckedDialerWithoutDialContext checks that a fetcher built
|
|
// without DialContext, as pixa builds it, refuses to connect to a local
|
|
// server.
|
|
func TestNewUsesCheckedDialerWithoutDialContext(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
srv := startUpstream(t)
|
|
transport := transportOf(t, New(&Config{
|
|
Timeout: 30 * time.Second,
|
|
MaxResponseSize: 50 << 20,
|
|
MaxConnectionsPerHost: 20,
|
|
MaxConnections: 64,
|
|
}))
|
|
|
|
addr := srv.Listener.Addr().String()
|
|
|
|
_, err := transport.DialContext(testContext(t), "tcp", addr)
|
|
if !errors.Is(err, ErrSSRFBlocked) {
|
|
t.Fatalf("DialContext(%s) error = %v, want ErrSSRFBlocked", addr, err)
|
|
}
|
|
}
|
|
|
|
// TestDialContextReplacesOnlyTheDialer checks that a fetcher built with
|
|
// DialContext connects through it, while the URL check still refuses a
|
|
// loopback URL and the redirect check a redirect to a link-local address.
|
|
func TestDialContextReplacesOnlyTheDialer(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
srv := startUpstream(t)
|
|
dialer := &recordingDialer{target: srv.Listener.Addr().String()}
|
|
|
|
f := New(&Config{
|
|
Timeout: 30 * time.Second,
|
|
MaxResponseSize: 50 << 20,
|
|
AllowHTTP: true,
|
|
MaxConnectionsPerHost: 20,
|
|
MaxConnections: 64,
|
|
DialContext: dialer.dialContext,
|
|
})
|
|
|
|
if body := fetchBody(t, f, "/image"); body != imagePayload {
|
|
t.Errorf("body = %q, want %q", body, imagePayload)
|
|
}
|
|
|
|
_, err := f.Fetch(testContext(t), imageSourceOn("127.0.0.1"))
|
|
if !errors.Is(err, ErrSSRFBlocked) {
|
|
t.Errorf("Fetch(loopback URL) error = %v, want ErrSSRFBlocked", err)
|
|
}
|
|
|
|
_, err = f.Fetch(testContext(t), upstreamSource("/redirect/private"))
|
|
if !errors.Is(err, ErrSSRFBlocked) {
|
|
t.Errorf("Fetch(/redirect/private) error = %v, want ErrSSRFBlocked", err)
|
|
}
|
|
|
|
// The upstream server is reached through DialContext, and nothing else
|
|
// is asked of it.
|
|
dialed := dialer.dialedAddrs()
|
|
if len(dialed) == 0 {
|
|
t.Error("DialContext was never called")
|
|
}
|
|
|
|
for _, addr := range dialed {
|
|
if addr != net.JoinHostPort(testPublicHost, "80") {
|
|
t.Errorf("DialContext was asked to connect to %s", addr)
|
|
}
|
|
}
|
|
}
|