mirror of
https://github.com/MengMengCode/CLICD.git
synced 2026-08-04 21:31:23 +08:00
146 lines
4.0 KiB
Go
146 lines
4.0 KiB
Go
package safehttp
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"net"
|
|
"net/http"
|
|
"net/netip"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestValidateURLRejectsUnsafeDestinations(t *testing.T) {
|
|
t.Parallel()
|
|
for _, rawURL := range []string{
|
|
"file:///etc/passwd",
|
|
"http://user:pass@example.com/image",
|
|
"http://127.0.0.1/image",
|
|
"http://[::1]/image",
|
|
"http://169.254.169.254/latest/meta-data",
|
|
"http://100.100.100.200/latest/meta-data",
|
|
"http://[fd00:ec2::254]/latest/meta-data",
|
|
"http://example.com:99999/image",
|
|
} {
|
|
if _, err := ValidateURL(rawURL); err == nil {
|
|
t.Fatalf("ValidateURL(%q) succeeded, want rejection", rawURL)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestValidateURLAcceptsPrivateNetworkMirror(t *testing.T) {
|
|
t.Parallel()
|
|
for _, rawURL := range []string{
|
|
"http://10.0.0.10/images/rootfs.tar.xz",
|
|
"http://172.16.20.30:8080/images/vm.qcow2",
|
|
"https://192.168.1.10/image.iso",
|
|
"http://100.64.0.10/image.qcow2",
|
|
"http://[fd00::10]/rootfs.tar.xz",
|
|
} {
|
|
if _, err := ValidateURL(rawURL); err != nil {
|
|
t.Errorf("ValidateURL(%q) returned error: %v", rawURL, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestValidateURLAcceptsPublicHTTPURL(t *testing.T) {
|
|
t.Parallel()
|
|
parsed, err := ValidateURL("https://example.com/images/rootfs.tar.xz?variant=default")
|
|
if err != nil {
|
|
t.Fatalf("ValidateURL returned error: %v", err)
|
|
}
|
|
if parsed.Hostname() != "example.com" {
|
|
t.Fatalf("hostname = %q, want example.com", parsed.Hostname())
|
|
}
|
|
}
|
|
|
|
func TestIsAllowedDownloadAddress(t *testing.T) {
|
|
t.Parallel()
|
|
tests := map[string]bool{
|
|
"8.8.8.8": true,
|
|
"1.1.1.1": true,
|
|
"2606:4700:4700::1111": true,
|
|
"127.0.0.1": false,
|
|
"10.0.0.1": true,
|
|
"100.64.0.1": true,
|
|
"172.16.0.1": true,
|
|
"192.168.1.1": true,
|
|
"169.254.169.254": false,
|
|
"100.100.100.200": false,
|
|
"192.0.2.1": false,
|
|
"198.18.0.1": false,
|
|
"::1": false,
|
|
"64:ff9b::127.0.0.1": false,
|
|
"2002:7f00:1::1": false,
|
|
"fc00::1": true,
|
|
"fd00:ec2::254": false,
|
|
"fec0::1": false,
|
|
"fe80::1": false,
|
|
"2001:db8::1": false,
|
|
}
|
|
for raw, expected := range tests {
|
|
if actual := isAllowedDownloadAddress(netip.MustParseAddr(raw)); actual != expected {
|
|
t.Errorf("isAllowedDownloadAddress(%s) = %v, want %v", raw, actual, expected)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGetRejectsLoopbackBeforeRequest(t *testing.T) {
|
|
t.Parallel()
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
defer cancel()
|
|
if _, err := Get(ctx, "http://127.0.0.1:1/image", "test", time.Second); err == nil {
|
|
t.Fatal("Get accepted a loopback destination")
|
|
}
|
|
}
|
|
|
|
func TestGetAllowsPrivateNetworkMirror(t *testing.T) {
|
|
privateIP := privateInterfaceIPv4(t)
|
|
listener, err := net.Listen("tcp4", net.JoinHostPort(privateIP.String(), "0"))
|
|
if err != nil {
|
|
t.Fatalf("failed to listen on private interface: %v", err)
|
|
}
|
|
defer listener.Close()
|
|
|
|
server := &http.Server{Handler: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
_, _ = io.WriteString(w, "private mirror ok")
|
|
})}
|
|
go func() { _ = server.Serve(listener) }()
|
|
defer server.Close()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
response, err := Get(ctx, "http://"+listener.Addr().String()+"/image", "test", 5*time.Second)
|
|
if err != nil {
|
|
t.Fatalf("Get rejected private mirror: %v", err)
|
|
}
|
|
defer response.Body.Close()
|
|
body, err := io.ReadAll(response.Body)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if string(body) != "private mirror ok" {
|
|
t.Fatalf("body = %q, want private mirror response", body)
|
|
}
|
|
}
|
|
|
|
func privateInterfaceIPv4(t *testing.T) netip.Addr {
|
|
t.Helper()
|
|
addresses, err := net.InterfaceAddrs()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, rawAddress := range addresses {
|
|
prefix, err := netip.ParsePrefix(rawAddress.String())
|
|
if err != nil {
|
|
continue
|
|
}
|
|
address := prefix.Addr().Unmap()
|
|
if address.Is4() && address.IsPrivate() && isAllowedDownloadAddress(address) {
|
|
return address
|
|
}
|
|
}
|
|
t.Skip("no private IPv4 interface is available")
|
|
return netip.Addr{}
|
|
}
|