check / check (push) Waiting to run
Adding or editing an http or slack target whose address is private or reserved was refused with no hint that the refusal is deliberate or that it can be lifted. The refusal now adds that such addresses are refused by default and that the server's ALLOWED_EGRESS_CIDRS setting allows named networks, naming the README section "Allowing egress to your own network". Metadata refusals do not get it. The default blocklist's public addresses move to a list of their own, still checked after the allowlist, and are refused as cloud metadata addresses. The private-and-reserved error is exported as ErrBlockedPrivateOrReservedIP so the handler can tell them apart. Model: opus-5-5
175 lines
3.5 KiB
Go
175 lines
3.5 KiB
Go
package delivery_test
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"sneak.berlin/go/webhooker/internal/delivery"
|
|
)
|
|
|
|
func TestGuardCheckIP_PrivateRanges(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
ip string
|
|
blocked bool
|
|
}{
|
|
{"loopback 127.0.0.1", "127.0.0.1", true},
|
|
{"loopback 127.0.0.2", "127.0.0.2", true},
|
|
{"loopback 127.255.255.255", "127.255.255.255", true},
|
|
{"10.0.0.0", "10.0.0.0", true},
|
|
{"10.0.0.1", "10.0.0.1", true},
|
|
{"10.255.255.255", "10.255.255.255", true},
|
|
{"172.16.0.1", "172.16.0.1", true},
|
|
{"172.31.255.255", "172.31.255.255", true},
|
|
{"172.15.255.255", "172.15.255.255", false},
|
|
{"172.32.0.0", "172.32.0.0", false},
|
|
{"192.168.0.1", "192.168.0.1", true},
|
|
{"192.168.255.255", "192.168.255.255", true},
|
|
{"169.254.0.1", "169.254.0.1", true},
|
|
{metadataIP, metadataIP, true},
|
|
{"8.8.8.8", "8.8.8.8", false},
|
|
{"1.1.1.1", "1.1.1.1", false},
|
|
{publicIP, publicIP, false},
|
|
{"::1", "::1", true},
|
|
{"fd00::1", "fd00::1", true},
|
|
{"fc00::1", "fc00::1", true},
|
|
{"fe80::1", "fe80::1", true},
|
|
{
|
|
"2607:f8b0:4004:800::200e",
|
|
"2607:f8b0:4004:800::200e",
|
|
false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ip := net.ParseIP(tt.ip)
|
|
|
|
require.NotNil(t, ip,
|
|
"failed to parse IP %s", tt.ip,
|
|
)
|
|
|
|
refused := delivery.NewTestGuard().ExportCheckIP(ip) != nil
|
|
|
|
assert.Equal(t,
|
|
tt.blocked,
|
|
refused,
|
|
"default guard refuses %s = %v, want %v",
|
|
tt.ip,
|
|
refused,
|
|
tt.blocked,
|
|
)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidateTargetURL_Blocked(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
blockedURLs := []string{
|
|
loopbackHookURL,
|
|
"http://127.0.0.1:8080/hook",
|
|
"https://10.0.0.1/hook",
|
|
"http://192.168.1.1/webhook",
|
|
"http://172.16.0.1/api",
|
|
metadataURL,
|
|
"http://[::1]/hook",
|
|
"http://[fc00::1]/hook",
|
|
"http://[fe80::1]/hook",
|
|
"http://0.0.0.0/hook",
|
|
}
|
|
|
|
for _, u := range blockedURLs {
|
|
t.Run(u, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
err := delivery.NewTestGuard().ValidateTargetURL(
|
|
context.Background(), u,
|
|
)
|
|
|
|
assert.Error(t, err,
|
|
"URL %s should be blocked", u,
|
|
)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidateTargetURL_Allowed(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
allowedURLs := []string{
|
|
"https://example.com/hook",
|
|
"http://93.184.216.34/webhook",
|
|
"https://hooks.slack.com/services/T00/B00/xxx",
|
|
}
|
|
|
|
for _, u := range allowedURLs {
|
|
t.Run(u, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
err := delivery.NewTestGuard().ValidateTargetURL(
|
|
context.Background(), u,
|
|
)
|
|
|
|
assert.NoError(t, err,
|
|
"URL %s should be allowed", u,
|
|
)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidateTargetURL_InvalidScheme(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
err := delivery.NewTestGuard().ValidateTargetURL(
|
|
context.Background(), "ftp://example.com/hook",
|
|
)
|
|
|
|
require.Error(t, err)
|
|
|
|
assert.Contains(t, err.Error(),
|
|
"unsupported URL scheme",
|
|
)
|
|
}
|
|
|
|
func TestValidateTargetURL_EmptyHost(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
err := delivery.NewTestGuard().ValidateTargetURL(
|
|
context.Background(), "http:///path",
|
|
)
|
|
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestValidateTargetURL_InvalidURL(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
err := delivery.NewTestGuard().ValidateTargetURL(
|
|
context.Background(), "://invalid",
|
|
)
|
|
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestBlockedNetworks_Initialized(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
nets := delivery.ExportBlockedNetworks()
|
|
|
|
assert.NotEmpty(t, nets,
|
|
"blockedNetworks should be initialized",
|
|
)
|
|
|
|
assert.GreaterOrEqual(t, len(nets), 8,
|
|
"should have at least 8 blocked network ranges",
|
|
)
|
|
}
|