client_ip_test.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
  2. // SPDX-License-Identifier: Apache-2.0
  3. package request // import "miniflux.app/v2/internal/http/request"
  4. import (
  5. "net/http"
  6. "testing"
  7. )
  8. var trustedProxyNetworks = []string{"127.0.0.0/8", "192.168.0.0/16"}
  9. func TestFindClientIPWithoutHeaders(t *testing.T) {
  10. r := &http.Request{RemoteAddr: "192.168.0.1:4242"}
  11. if ip := FindClientIP(r, false, nil); ip != "192.168.0.1" {
  12. t.Fatalf(`Unexpected result, got: %q`, ip)
  13. }
  14. r = &http.Request{RemoteAddr: "192.168.0.1"}
  15. if ip := FindClientIP(r, false, nil); ip != "192.168.0.1" {
  16. t.Fatalf(`Unexpected result, got: %q`, ip)
  17. }
  18. r = &http.Request{RemoteAddr: "fe80::14c2:f039:edc7:edc7"}
  19. if ip := FindClientIP(r, false, nil); ip != "fe80::14c2:f039:edc7:edc7" {
  20. t.Fatalf(`Unexpected result, got: %q`, ip)
  21. }
  22. r = &http.Request{RemoteAddr: "fe80::14c2:f039:edc7:edc7%eth0"}
  23. if ip := FindClientIP(r, false, nil); ip != "fe80::14c2:f039:edc7:edc7" {
  24. t.Fatalf(`Unexpected result, got: %q`, ip)
  25. }
  26. r = &http.Request{RemoteAddr: "[fe80::14c2:f039:edc7:edc7%eth0]:4242"}
  27. if ip := FindClientIP(r, false, nil); ip != "fe80::14c2:f039:edc7:edc7" {
  28. t.Fatalf(`Unexpected result, got: %q`, ip)
  29. }
  30. r = &http.Request{RemoteAddr: "@"}
  31. if ip := FindClientIP(r, false, nil); ip != "127.0.0.1" {
  32. t.Fatalf(`Unexpected result, got: %q`, ip)
  33. }
  34. r = &http.Request{RemoteAddr: ""}
  35. if ip := FindClientIP(r, false, nil); ip != "127.0.0.1" {
  36. t.Fatalf(`Unexpected result, got: %q`, ip)
  37. }
  38. }
  39. func TestFindClientIPWithXFFHeader(t *testing.T) {
  40. // Test with multiple IPv4 addresses.
  41. headers := http.Header{}
  42. headers.Set("X-Forwarded-For", "203.0.113.195, 70.41.3.18, 150.172.238.178")
  43. r := &http.Request{RemoteAddr: "192.168.0.1:4242", Header: headers}
  44. if ip := FindClientIP(r, true, trustedProxyNetworks); ip != "150.172.238.178" {
  45. t.Fatalf(`Unexpected result, got: %q`, ip)
  46. }
  47. // Test with single IPv6 address.
  48. headers = http.Header{}
  49. headers.Set("X-Forwarded-For", "2001:db8:85a3:8d3:1319:8a2e:370:7348")
  50. r = &http.Request{RemoteAddr: "192.168.0.1:4242", Header: headers}
  51. if ip := FindClientIP(r, true, trustedProxyNetworks); ip != "2001:db8:85a3:8d3:1319:8a2e:370:7348" {
  52. t.Fatalf(`Unexpected result, got: %q`, ip)
  53. }
  54. // Test with single IPv6 address with zone
  55. headers = http.Header{}
  56. headers.Set("X-Forwarded-For", "fe80::14c2:f039:edc7:edc7%eth0")
  57. r = &http.Request{RemoteAddr: "192.168.0.1:4242", Header: headers}
  58. if ip := FindClientIP(r, true, trustedProxyNetworks); ip != "fe80::14c2:f039:edc7:edc7" {
  59. t.Fatalf(`Unexpected result, got: %q`, ip)
  60. }
  61. // Test with single IPv4 address.
  62. headers = http.Header{}
  63. headers.Set("X-Forwarded-For", "70.41.3.18")
  64. r = &http.Request{RemoteAddr: "192.168.0.1:4242", Header: headers}
  65. if ip := FindClientIP(r, true, trustedProxyNetworks); ip != "70.41.3.18" {
  66. t.Fatalf(`Unexpected result, got: %q`, ip)
  67. }
  68. // Test with invalid IP address.
  69. headers = http.Header{}
  70. headers.Set("X-Forwarded-For", "fake IP")
  71. r = &http.Request{RemoteAddr: "192.168.0.1:4242", Header: headers}
  72. if ip := FindClientIP(r, true, trustedProxyNetworks); ip != "192.168.0.1" {
  73. t.Fatalf(`Unexpected result, got: %q`, ip)
  74. }
  75. }
  76. func TestFindClientIPSkipsTrustedProxiesInXFFHeader(t *testing.T) {
  77. scenarios := []struct {
  78. values []string
  79. expected string
  80. }{
  81. {[]string{"203.0.113.195, 192.168.1.2"}, "203.0.113.195"},
  82. {[]string{"1.2.3.4, 203.0.113.195, 192.168.1.2"}, "203.0.113.195"},
  83. {[]string{"1.2.3.4", "203.0.113.195, 192.168.1.2"}, "203.0.113.195"},
  84. {[]string{"192.168.1.3, 192.168.1.2"}, "192.168.1.3"},
  85. {[]string{"fake IP, 192.168.1.2"}, "192.168.0.1"},
  86. }
  87. for _, scenario := range scenarios {
  88. headers := http.Header{}
  89. for _, value := range scenario.values {
  90. headers.Add("X-Forwarded-For", value)
  91. }
  92. r := &http.Request{RemoteAddr: "192.168.0.1:4242", Header: headers}
  93. if ip := FindClientIP(r, true, trustedProxyNetworks); ip != scenario.expected {
  94. t.Errorf(`Expected %q for X-Forwarded-For %q, got %q`, scenario.expected, scenario.values, ip)
  95. }
  96. }
  97. }
  98. func TestClientIPWithXRealIPHeader(t *testing.T) {
  99. headers := http.Header{}
  100. headers.Set("X-Real-Ip", "192.168.122.1")
  101. r := &http.Request{RemoteAddr: "192.168.0.1:4242", Header: headers}
  102. if ip := FindClientIP(r, true, trustedProxyNetworks); ip != "192.168.122.1" {
  103. t.Fatalf(`Unexpected result, got: %q`, ip)
  104. }
  105. }
  106. func TestClientIPWithBothHeaders(t *testing.T) {
  107. headers := http.Header{}
  108. headers.Set("X-Forwarded-For", "203.0.113.195, 70.41.3.18, 150.172.238.178")
  109. headers.Set("X-Real-Ip", "192.168.122.1")
  110. r := &http.Request{RemoteAddr: "192.168.0.1:4242", Header: headers}
  111. if ip := FindClientIP(r, true, trustedProxyNetworks); ip != "150.172.238.178" {
  112. t.Fatalf(`Unexpected result, got: %q`, ip)
  113. }
  114. }
  115. func TestClientIPWithUnixSocketRemoteAddrAndBothHeaders(t *testing.T) {
  116. headers := http.Header{}
  117. headers.Set("X-Forwarded-For", "203.0.113.195, 70.41.3.18, 150.172.238.178")
  118. headers.Set("X-Real-Ip", "192.168.122.1")
  119. r := &http.Request{RemoteAddr: "@", Header: headers}
  120. if ip := FindClientIP(r, true, trustedProxyNetworks); ip != "150.172.238.178" {
  121. t.Fatalf(`Unexpected result, got: %q`, ip)
  122. }
  123. }
  124. func TestIsTrustedIP(t *testing.T) {
  125. trustedNetworks := []string{"127.0.0.1/8", "10.0.0.0/8", "::1/128", "invalid"}
  126. scenarios := []struct {
  127. ip string
  128. expected bool
  129. }{
  130. {"127.0.0.1", true},
  131. {"10.0.0.1", true},
  132. {"::1", true},
  133. {"192.168.1.1", false},
  134. {"invalid", false},
  135. {"@", false},
  136. {"/tmp/miniflux.sock", false},
  137. {"", false},
  138. }
  139. for _, scenario := range scenarios {
  140. result := IsTrustedIP(scenario.ip, trustedNetworks)
  141. if result != scenario.expected {
  142. t.Errorf("Expected %v for IP %s, got %v", scenario.expected, scenario.ip, result)
  143. }
  144. }
  145. if IsTrustedIP("127.0.0.1", nil) {
  146. t.Error("Expected false when no trusted networks are defined")
  147. }
  148. if IsTrustedIP("127.0.0.1", []string{}) {
  149. t.Error("Expected false when trusted networks list is empty")
  150. }
  151. }
  152. func TestFindRemoteIP(t *testing.T) {
  153. scenarios := []struct {
  154. ip string
  155. expected string
  156. }{
  157. {"192.168.0.1:4242", "192.168.0.1"},
  158. {"[2001:db8::1]:4242", "2001:db8::1"},
  159. {"fe80::14c2:f039:edc7:edc7%eth0", "fe80::14c2:f039:edc7:edc7"},
  160. {"", "127.0.0.1"},
  161. {"@", "127.0.0.1"},
  162. {"invalid", "127.0.0.1"},
  163. }
  164. for _, scenario := range scenarios {
  165. r := &http.Request{RemoteAddr: scenario.ip}
  166. result := FindRemoteIP(r)
  167. if result != scenario.expected {
  168. t.Errorf("Expected %q for RemoteAddr %q, got %q", scenario.expected, scenario.ip, result)
  169. }
  170. }
  171. }