mirror of
https://github.com/pocket-id/pocket-id.git
synced 2025-12-11 15:53:04 +03:00
344 lines
7.8 KiB
Go
344 lines
7.8 KiB
Go
package utils
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"net/url"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/pocket-id/pocket-id/backend/internal/common"
|
|
)
|
|
|
|
func TestIsLocalhostIP(t *testing.T) {
|
|
tests := []struct {
|
|
ip string
|
|
expected bool
|
|
}{
|
|
{"127.0.0.1", true},
|
|
{"127.255.255.255", true},
|
|
{"::1", true},
|
|
{"192.168.1.1", false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
ip := net.ParseIP(tt.ip)
|
|
got := IsLocalhostIP(ip)
|
|
assert.Equal(t, tt.expected, got)
|
|
}
|
|
}
|
|
|
|
func TestIsPrivateLanIP(t *testing.T) {
|
|
tests := []struct {
|
|
ip string
|
|
expected bool
|
|
}{
|
|
{"10.0.0.1", true},
|
|
{"172.16.5.4", true},
|
|
{"192.168.100.200", true},
|
|
{"8.8.8.8", false},
|
|
{"::1", false}, // IPv6 should return false
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
ip := net.ParseIP(tt.ip)
|
|
got := IsPrivateLanIP(ip)
|
|
assert.Equal(t, tt.expected, got)
|
|
}
|
|
}
|
|
|
|
func TestIsTailscaleIP(t *testing.T) {
|
|
tests := []struct {
|
|
ip string
|
|
expected bool
|
|
}{
|
|
{"100.64.0.1", true},
|
|
{"100.127.255.254", true},
|
|
{"8.8.8.8", false},
|
|
{"::1", false}, // IPv6 should return false
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
ip := net.ParseIP(tt.ip)
|
|
|
|
got := IsTailscaleIP(ip)
|
|
assert.Equal(t, tt.expected, got)
|
|
}
|
|
}
|
|
|
|
func TestIsLocalIPv6(t *testing.T) {
|
|
// Save and restore env config
|
|
origRanges := common.EnvConfig.LocalIPv6Ranges
|
|
defer func() { common.EnvConfig.LocalIPv6Ranges = origRanges }()
|
|
|
|
common.EnvConfig.LocalIPv6Ranges = "fd00::/8,fc00::/7"
|
|
localIPv6Ranges = nil // reset
|
|
loadLocalIPv6Ranges()
|
|
|
|
tests := []struct {
|
|
ip string
|
|
expected bool
|
|
}{
|
|
{"fd00::1", true},
|
|
{"fc00::abcd", true},
|
|
{"::1", false}, // loopback handled separately
|
|
{"192.168.1.1", false}, // IPv4 should return false
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
ip := net.ParseIP(tt.ip)
|
|
got := IsLocalIPv6(ip)
|
|
assert.Equal(t, tt.expected, got)
|
|
}
|
|
}
|
|
|
|
func TestIsPrivateIP(t *testing.T) {
|
|
// Save and restore env config
|
|
origRanges := common.EnvConfig.LocalIPv6Ranges
|
|
t.Cleanup(func() {
|
|
common.EnvConfig.LocalIPv6Ranges = origRanges
|
|
})
|
|
|
|
common.EnvConfig.LocalIPv6Ranges = "fd00::/8"
|
|
localIPv6Ranges = nil // reset
|
|
loadLocalIPv6Ranges()
|
|
|
|
tests := []struct {
|
|
ip string
|
|
expected bool
|
|
}{
|
|
{"127.0.0.1", true}, // localhost
|
|
{"192.168.1.1", true}, // private LAN
|
|
{"100.64.0.1", true}, // Tailscale
|
|
{"fd00::1", true}, // local IPv6
|
|
{"8.8.8.8", false}, // public IPv4
|
|
{"2001:4860:4860::8888", false}, // public IPv6
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
ip := net.ParseIP(tt.ip)
|
|
got := IsPrivateIP(ip)
|
|
assert.Equal(t, tt.expected, got)
|
|
}
|
|
}
|
|
|
|
func TestListContainsIP(t *testing.T) {
|
|
_, ipNet1, _ := net.ParseCIDR("10.0.0.0/8")
|
|
_, ipNet2, _ := net.ParseCIDR("192.168.0.0/16")
|
|
|
|
list := []*net.IPNet{ipNet1, ipNet2}
|
|
|
|
tests := []struct {
|
|
ip string
|
|
expected bool
|
|
}{
|
|
{"10.1.1.1", true},
|
|
{"192.168.5.5", true},
|
|
{"172.16.0.1", false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
ip := net.ParseIP(tt.ip)
|
|
got := listContainsIP(list, ip)
|
|
assert.Equal(t, tt.expected, got)
|
|
}
|
|
}
|
|
|
|
func TestInit_LocalIPv6Ranges(t *testing.T) {
|
|
// Save and restore env config
|
|
origRanges := common.EnvConfig.LocalIPv6Ranges
|
|
t.Cleanup(func() {
|
|
common.EnvConfig.LocalIPv6Ranges = origRanges
|
|
})
|
|
|
|
common.EnvConfig.LocalIPv6Ranges = "fd00::/8, invalidCIDR ,fc00::/7"
|
|
localIPv6Ranges = nil
|
|
loadLocalIPv6Ranges()
|
|
|
|
assert.Len(t, localIPv6Ranges, 2)
|
|
}
|
|
|
|
func TestIsURLPrivate(t *testing.T) {
|
|
ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
urlStr string
|
|
expectPriv bool
|
|
expectError bool
|
|
}{
|
|
{
|
|
name: "localhost by name",
|
|
urlStr: "http://localhost",
|
|
expectPriv: true,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "localhost with port",
|
|
urlStr: "http://localhost:8080",
|
|
expectPriv: true,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "127.0.0.1 IP",
|
|
urlStr: "http://127.0.0.1",
|
|
expectPriv: true,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "127.0.0.1 with port",
|
|
urlStr: "http://127.0.0.1:3000",
|
|
expectPriv: true,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "IPv6 loopback",
|
|
urlStr: "http://[::1]",
|
|
expectPriv: true,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "IPv6 loopback with port",
|
|
urlStr: "http://[::1]:8080",
|
|
expectPriv: true,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "private IP 10.x.x.x",
|
|
urlStr: "http://10.0.0.1",
|
|
expectPriv: true,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "private IP 192.168.x.x",
|
|
urlStr: "http://192.168.1.1",
|
|
expectPriv: true,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "private IP 172.16.x.x",
|
|
urlStr: "http://172.16.0.1",
|
|
expectPriv: true,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "Tailscale IP",
|
|
urlStr: "http://100.64.0.1",
|
|
expectPriv: true,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "public IP - Google DNS",
|
|
urlStr: "http://8.8.8.8",
|
|
expectPriv: false,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "public IP - Cloudflare DNS",
|
|
urlStr: "http://1.1.1.1",
|
|
expectPriv: false,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "invalid hostname",
|
|
urlStr: "http://this-should-not-resolve-ever-123456789.invalid",
|
|
expectPriv: false,
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
u, err := url.Parse(tt.urlStr)
|
|
require.NoError(t, err, "Failed to parse URL %s", tt.urlStr)
|
|
|
|
isPriv, err := IsURLPrivate(ctx, u)
|
|
|
|
if tt.expectError {
|
|
require.Error(t, err, "IsURLPrivate(%s) expected error but got none", tt.urlStr)
|
|
} else {
|
|
require.NoError(t, err, "IsURLPrivate(%s) unexpected error", tt.urlStr)
|
|
assert.Equal(t, tt.expectPriv, isPriv, "IsURLPrivate(%s)", tt.urlStr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIsURLPrivate_WithDomainName(t *testing.T) {
|
|
// Note: These tests rely on actual DNS resolution
|
|
// They test real public domains to ensure they are not flagged as private
|
|
ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
urlStr string
|
|
expectPriv bool
|
|
}{
|
|
{
|
|
name: "Google public domain",
|
|
urlStr: "https://www.google.com",
|
|
expectPriv: false,
|
|
},
|
|
{
|
|
name: "GitHub public domain",
|
|
urlStr: "https://github.com",
|
|
expectPriv: false,
|
|
},
|
|
{
|
|
// localhost.localtest.me is a well-known domain that resolves to 127.0.0.1
|
|
name: "localhost.localtest.me resolves to 127.0.0.1",
|
|
urlStr: "http://localhost.localtest.me",
|
|
expectPriv: true,
|
|
},
|
|
{
|
|
// 10.0.0.1.nip.io resolves to 10.0.0.1 (private IP)
|
|
name: "nip.io domain resolving to private 10.x IP",
|
|
urlStr: "http://10.0.0.1.nip.io",
|
|
expectPriv: true,
|
|
},
|
|
{
|
|
// 192.168.1.1.nip.io resolves to 192.168.1.1 (private IP)
|
|
name: "nip.io domain resolving to private 192.168.x IP",
|
|
urlStr: "http://192.168.1.1.nip.io",
|
|
expectPriv: true,
|
|
},
|
|
{
|
|
// 127.0.0.1.nip.io resolves to 127.0.0.1 (localhost)
|
|
name: "nip.io domain resolving to localhost",
|
|
urlStr: "http://127.0.0.1.nip.io",
|
|
expectPriv: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
u, err := url.Parse(tt.urlStr)
|
|
require.NoError(t, err, "Failed to parse URL %s", tt.urlStr)
|
|
|
|
isPriv, err := IsURLPrivate(ctx, u)
|
|
if err != nil {
|
|
t.Skipf("DNS resolution failed for %s (network issue?): %v", tt.urlStr, err)
|
|
return
|
|
}
|
|
|
|
assert.Equal(t, tt.expectPriv, isPriv, "IsURLPrivate(%s)", tt.urlStr)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIsURLPrivate_ContextCancellation(t *testing.T) {
|
|
ctx, cancel := context.WithCancel(t.Context())
|
|
cancel() // Cancel immediately
|
|
|
|
u, err := url.Parse("http://example.com")
|
|
require.NoError(t, err, "Failed to parse URL")
|
|
|
|
_, err = IsURLPrivate(ctx, u)
|
|
assert.Error(t, err, "IsURLPrivate with cancelled context expected error but got none")
|
|
}
|