shiori/internal/webserver/utils_ip_test.go

230 lines
7.2 KiB
Go
Raw Normal View History

package webserver
import (
"fmt"
"math/rand"
"net"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestParseCidr(t *testing.T) {
res := parseCIDR("192.168.0.0/16", "internal 192.168.x.x")
assert.Equal(t, res.IP, net.IP([]byte{192, 168, 0, 0}))
assert.Equal(t, res.Mask, net.IPMask([]byte{255, 255, 0, 0}))
}
func TestParseCidrInvalidAddr(t *testing.T) {
assert.Panics(t, func() { parseCIDR("192.168.0.0/34", "internal 192.168.x.x") })
}
func TestIsPrivateIP(t *testing.T) {
assert.True(t, IsPrivateIP(net.ParseIP("127.0.0.1")), "should be private")
assert.True(t, IsPrivateIP(net.ParseIP("192.168.254.254")), "should be private")
assert.True(t, IsPrivateIP(net.ParseIP("10.255.0.3")), "should be private")
assert.True(t, IsPrivateIP(net.ParseIP("172.16.255.255")), "should be private")
assert.True(t, IsPrivateIP(net.ParseIP("172.31.255.255")), "should be private")
assert.True(t, !IsPrivateIP(net.ParseIP("128.0.0.1")), "should be private")
assert.True(t, !IsPrivateIP(net.ParseIP("192.169.255.255")), "should not be private")
assert.True(t, !IsPrivateIP(net.ParseIP("9.255.0.255")), "should not be private")
assert.True(t, !IsPrivateIP(net.ParseIP("172.32.255.255")), "should not be private")
assert.True(t, IsPrivateIP(net.ParseIP("::0")), "should be private")
assert.True(t, IsPrivateIP(net.ParseIP("::1")), "should be private")
assert.True(t, !IsPrivateIP(net.ParseIP("::2")), "should not be private")
assert.True(t, IsPrivateIP(net.ParseIP("fe80::1")), "should be private")
assert.True(t, IsPrivateIP(net.ParseIP("febf::1")), "should be private")
assert.True(t, !IsPrivateIP(net.ParseIP("fec0::1")), "should not be private")
assert.True(t, !IsPrivateIP(net.ParseIP("feff::1")), "should not be private")
assert.True(t, IsPrivateIP(net.ParseIP("ff00::1")), "should be private")
assert.True(t, IsPrivateIP(net.ParseIP("ff10::1")), "should be private")
assert.True(t, IsPrivateIP(net.ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")), "should be private")
assert.True(t, IsPrivateIP(net.ParseIP("2002::")), "should be private")
assert.True(t, IsPrivateIP(net.ParseIP("2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff")), "should be private")
assert.True(t, IsPrivateIP(net.ParseIP("0100::")), "should be private")
assert.True(t, IsPrivateIP(net.ParseIP("0100::0000:ffff:ffff:ffff:ffff")), "should be private")
assert.True(t, !IsPrivateIP(net.ParseIP("0100::0001:0000:0000:0000:0000")), "should be private")
}
func TestIsIpValidAndPublic(t *testing.T) {
// test empty address
assert.False(t, IsIPValidAndPublic(""))
// test public address
assert.True(t, IsIPValidAndPublic("31.41.244.124"))
assert.True(t, IsIPValidAndPublic("62.233.50.248"))
// trim head or tail space
assert.True(t, IsIPValidAndPublic(" 62.233.50.249"))
assert.True(t, IsIPValidAndPublic(" 62.233.50.250 "))
assert.True(t, IsIPValidAndPublic("62.233.50.251 "))
// test private address
assert.False(t, IsIPValidAndPublic("10.1.123.52"))
assert.False(t, IsIPValidAndPublic("192.168.123.24"))
assert.False(t, IsIPValidAndPublic("172.17.0.1"))
}
func BenchmarkIsPrivateIPv4(b *testing.B) {
// range: 2-254
n1 := 2 + rand.Intn(252)
n2 := 2 + rand.Intn(252)
for i := 0; i < b.N; i++ {
IsPrivateIP(net.ParseIP(fmt.Sprintf("192.168.%d.%d", n1, n2)))
}
}
func BenchmarkIsPrivateIPv6(b *testing.B) {
n1 := 2 + rand.Intn(252)
for i := 0; i < b.N; i++ {
IsPrivateIP(net.ParseIP(fmt.Sprintf("2002::%d", n1)))
}
}
func testIsPublicHttpRequestAddressHelper(
t *testing.T, wantIP string, headers map[string]string, isPublic bool,
) {
testIsPublicHttpRequestAddressHelperWrapped(t, nil, wantIP, headers, isPublic)
}
func testIsPublicHttpRequestAddressHelperWrapped(
t *testing.T, r *http.Request, wantIP string, headers map[string]string, isPublic bool,
) {
var (
err error
userIP string
)
if r == nil {
r = httptest.NewRequest("GET", "/", nil)
}
for k, v := range headers {
r.Header.Set(k, v)
}
origVal := GetUserRealIP(r)
if strings.Contains(origVal, ":") {
userIP, _, err = net.SplitHostPort(origVal)
if err != nil {
t.Error(err)
}
} else {
userIP = origVal
}
if isPublic {
// should equal first ip in list
assert.Equal(t, wantIP, userIP)
assert.True(t, IsIPValidAndPublic(userIP))
} else {
assert.Equal(t, origVal, r.RemoteAddr)
assert.False(t, IsIPValidAndPublic(userIP))
}
}
func TestGetUserRealIPWithSetRemoteAddr(t *testing.T) {
// Test Public RemoteAddr
testIsPublicHttpRequestAddressHelper(t, "", nil, false)
r := httptest.NewRequest("GET", "/", nil)
wantIP := "34.23.123.122"
r.RemoteAddr = fmt.Sprintf("%s:1234", wantIP)
testIsPublicHttpRequestAddressHelperWrapped(t, r, wantIP, nil, true)
}
func TestGetUserRealIPWithInvalidRemoteAddr(t *testing.T) {
// Test Public RemoteAddr
testIsPublicHttpRequestAddressHelper(t, "", nil, false)
r := httptest.NewRequest("GET", "/", nil)
wantIP := "34.23.123.122"
// without port
r.RemoteAddr = wantIP
testIsPublicHttpRequestAddressHelperWrapped(t, r, wantIP, nil, true)
}
func TestGetUserRealIPWithEmptyHeader(t *testing.T) {
// Test Empty X-Real-IP
testIsPublicHttpRequestAddressHelper(t, "", nil, false)
}
func TestGetUserRealIPWithInvalidHeaderValue(t *testing.T) {
for _, name := range userRealIpHeaderCandidates {
// invalid ip
m := map[string]string{
name: "31.41.24a.12",
}
testIsPublicHttpRequestAddressHelper(t, "", m, false)
}
}
func TestGetUserRealIPWithXRealIpHeader(t *testing.T) {
// Test public Real IP
for _, name := range userRealIpHeaderCandidates {
wantIP := "31.41.242.12"
m := map[string]string{
name: wantIP,
}
testIsPublicHttpRequestAddressHelper(t, wantIP, m, true)
}
}
func TestGetUserRealIPWithPrivateXRealIpHeader(t *testing.T) {
for _, name := range userRealIpHeaderCandidates {
wantIP := "192.168.123.123"
// test private ip in header
m := map[string]string{
name: wantIP,
}
testIsPublicHttpRequestAddressHelper(t, wantIP, m, false)
}
}
func TestGetUserRealIPWithXRealIpListHeader(t *testing.T) {
// Test Real IP List
for _, name := range userRealIpHeaderCandidates {
ipList := []string{"34.23.123.122", "34.23.123.123"}
// should equal first ip in list
wantIP := ipList[0]
// test private ip in header
m := map[string]string{
name: strings.Join(ipList, ", "),
}
testIsPublicHttpRequestAddressHelper(t, wantIP, m, true)
}
}
func TestGetUserRealIPWithXRealIpHeaderIgnoreComma(t *testing.T) {
// Test Real IP List with leading or tailing comma
wantIP := "34.23.123.124"
ipVariants := []string{
",34.23.123.124", " ,34.23.123.124", "\t,34.23.123.124",
",34.23.123.124,", " ,34.23.123.124, ", "\t,34.23.123.124,\t",
"34.23.123.124,", "34.23.123.124, ", "34.23.123.124,\t"}
for _, variant := range ipVariants {
for _, name := range userRealIpHeaderCandidates {
m := map[string]string{name: variant}
testIsPublicHttpRequestAddressHelper(t, wantIP, m, true)
}
}
}
func TestGetUserRealIPWithDifferentHeaderOrder(t *testing.T) {
var m map[string]string
wantIP := "34.23.123.124"
m = map[string]string{
"X-Real-Ip": "192.168.123.122",
"X-Forwarded-For": wantIP,
}
testIsPublicHttpRequestAddressHelper(t, wantIP, m, true)
m = map[string]string{
"X-Real-Ip": wantIP,
"X-Forwarded-For": "192.168.123.122",
}
testIsPublicHttpRequestAddressHelper(t, wantIP, m, true)
}