All checks set to exit_status: 0 initially to avoid blocking on pre-existing violations. Remove exit_status overrides as each check category is cleaned up incrementally. Dependency added to dev/test only.
111 lines
3.5 KiB
Elixir
111 lines
3.5 KiB
Elixir
defmodule ToweropsWeb.RemoteIpTest do
|
|
use ToweropsWeb.ConnCase, async: true
|
|
|
|
alias ToweropsWeb.RemoteIp
|
|
|
|
describe "from_conn/1" do
|
|
test "extracts the first forwarded IP when from a trusted proxy (10.x)", %{conn: conn} do
|
|
conn =
|
|
conn
|
|
|> Map.put(:remote_ip, {10, 0, 0, 1})
|
|
|> put_req_header("x-forwarded-for", "203.0.113.10, 10.0.0.1")
|
|
|
|
assert RemoteIp.from_conn(conn) == "203.0.113.10"
|
|
end
|
|
|
|
test "extracts the first forwarded IP when from a trusted proxy (172.16-31.x)", %{conn: conn} do
|
|
conn =
|
|
conn
|
|
|> Map.put(:remote_ip, {172, 16, 0, 1})
|
|
|> put_req_header("x-forwarded-for", "198.51.100.1, 172.16.0.1")
|
|
|
|
assert RemoteIp.from_conn(conn) == "198.51.100.1"
|
|
end
|
|
|
|
test "extracts the first forwarded IP when from a trusted proxy (192.168.x)", %{conn: conn} do
|
|
conn =
|
|
conn
|
|
|> Map.put(:remote_ip, {192, 168, 1, 1})
|
|
|> put_req_header("x-forwarded-for", "198.51.100.1")
|
|
|
|
assert RemoteIp.from_conn(conn) == "198.51.100.1"
|
|
end
|
|
|
|
test "uses x-real-ip when x-forwarded-for is missing and from trusted proxy", %{conn: conn} do
|
|
conn =
|
|
conn
|
|
|> Map.put(:remote_ip, {10, 0, 0, 1})
|
|
|> put_req_header("x-real-ip", "198.51.100.1")
|
|
|
|
assert RemoteIp.from_conn(conn) == "198.51.100.1"
|
|
end
|
|
|
|
test "uses remote_ip directly when neither forwarded header is present and from trusted proxy",
|
|
%{conn: conn} do
|
|
conn = Map.put(conn, :remote_ip, {10, 0, 0, 1})
|
|
|
|
assert RemoteIp.from_conn(conn) == "10.0.0.1"
|
|
end
|
|
|
|
test "uses remote_ip directly when NOT from a trusted proxy", %{conn: conn} do
|
|
# 8.8.8.8 is a public IP, not a trusted proxy — x-forwarded-for is ignored
|
|
conn =
|
|
conn
|
|
|> Map.put(:remote_ip, {8, 8, 8, 8})
|
|
|> put_req_header("x-forwarded-for", "203.0.113.10")
|
|
|
|
assert RemoteIp.from_conn(conn) == "8.8.8.8"
|
|
end
|
|
|
|
test "trims whitespace from forwarded IP", %{conn: conn} do
|
|
conn =
|
|
conn
|
|
|> Map.put(:remote_ip, {10, 0, 0, 1})
|
|
|> put_req_header("x-forwarded-for", " 203.0.113.10 , 10.0.0.1")
|
|
|
|
assert RemoteIp.from_conn(conn) == "203.0.113.10"
|
|
end
|
|
end
|
|
|
|
describe "format/1" do
|
|
test "formats IPv4 tuple" do
|
|
assert RemoteIp.format({192, 168, 1, 1}) == "192.168.1.1"
|
|
assert RemoteIp.format({10, 0, 0, 1}) == "10.0.0.1"
|
|
assert RemoteIp.format({0, 0, 0, 0}) == "0.0.0.0"
|
|
end
|
|
|
|
test "formats IPv6 addresses in uppercase hex" do
|
|
assert RemoteIp.format({8193, 3512, 0, 0, 0, 0, 0, 1}) == "2001:DB8:0:0:0:0:0:1"
|
|
end
|
|
|
|
test "formats loopback IPv6" do
|
|
assert RemoteIp.format({0, 0, 0, 0, 0, 0, 0, 1}) == "0:0:0:0:0:0:0:1"
|
|
end
|
|
|
|
test "returns nil for invalid tuple" do
|
|
assert RemoteIp.format(:error) == nil
|
|
assert RemoteIp.format("not-a-tuple") == nil
|
|
assert RemoteIp.format(nil) == nil
|
|
end
|
|
end
|
|
|
|
describe "from_socket/1" do
|
|
test "returns formatted IP from peer_data in assigns" do
|
|
socket = %Phoenix.Socket{
|
|
assigns: %{peer_data: %{address: {192, 168, 1, 42}, port: 12_345}}
|
|
}
|
|
|
|
assert RemoteIp.from_socket(socket) == "192.168.1.42"
|
|
end
|
|
|
|
test "returns nil when peer_data is missing" do
|
|
socket = %Phoenix.Socket{assigns: %{}}
|
|
assert RemoteIp.from_socket(socket) == nil
|
|
end
|
|
|
|
test "returns nil when peer_data has no address tuple" do
|
|
socket = %Phoenix.Socket{assigns: %{peer_data: %{}}}
|
|
assert RemoteIp.from_socket(socket) == nil
|
|
end
|
|
end
|
|
end
|