mirror of
https://github.com/basecamp/once-campfire.git
synced 2026-08-09 08:28:44 +09:00
4cfcc2a370
Matches the fizzy guard: the local-use NAT64 prefix (64:ff9b:1::/48, RFC 8215) embeds an IPv4 target in its low 32 bits just like the well-known prefix, so run it through the same embedded-IPv4 recheck. Local-use NAT64 to a public address now resolves (keeping unfurls working for self-hosters on such networks) while local-use NAT64 to an internal address stays blocked.
134 lines
5.0 KiB
Ruby
134 lines
5.0 KiB
Ruby
require "test_helper"
|
|
require "restricted_http/private_network_guard"
|
|
|
|
class RestrictedHTTP::PrivateNetworkGuardTest < ActiveSupport::TestCase
|
|
test "private_ip? returns true for 'This' network (RFC1700)" do
|
|
assert_private_ip "0.0.0.0"
|
|
assert_private_ip "0.255.255.255"
|
|
end
|
|
|
|
test "private_ip? returns true for loopback addresses" do
|
|
assert_private_ip "127.0.0.0"
|
|
assert_private_ip "127.0.0.1"
|
|
assert_private_ip "127.255.255.255"
|
|
end
|
|
|
|
test "private_ip? returns true for RFC1918 private addresses" do
|
|
assert_private_ip "10.0.0.0"
|
|
assert_private_ip "10.255.255.255"
|
|
assert_private_ip "172.16.0.0"
|
|
assert_private_ip "172.31.255.255"
|
|
assert_private_ip "192.168.0.0"
|
|
assert_private_ip "192.168.255.255"
|
|
end
|
|
|
|
test "private_ip? returns true for link-local addresses" do
|
|
assert_private_ip "169.254.0.1"
|
|
assert_private_ip "169.254.169.254" # AWS IMDS
|
|
assert_private_ip "169.254.255.255"
|
|
end
|
|
|
|
test "private_ip? returns false for public addresses" do
|
|
assert_not RestrictedHTTP::PrivateNetworkGuard.private_ip?("93.184.216.34")
|
|
assert_not RestrictedHTTP::PrivateNetworkGuard.private_ip?("8.8.8.8")
|
|
end
|
|
|
|
# IPv6 address format tests (SSRF bypass prevention)
|
|
|
|
test "private_ip? returns true for IPv4-mapped IPv6 addresses with private IPs" do
|
|
assert_private_ip "::ffff:192.168.1.1"
|
|
assert_private_ip "::ffff:10.0.0.1"
|
|
assert_private_ip "::ffff:172.16.0.1"
|
|
end
|
|
|
|
test "private_ip? returns true for IPv4-mapped IPv6 addresses with link-local IPs" do
|
|
assert_private_ip "::ffff:169.254.169.254" # AWS metadata via mapped format
|
|
end
|
|
|
|
test "private_ip? returns true for IPv4-mapped IPv6 addresses even with public IPs" do
|
|
# Block all ipv4_mapped? since DNS never returns this format legitimately
|
|
assert_private_ip "::ffff:93.184.216.34"
|
|
end
|
|
|
|
test "private_ip? returns true for IPv4-compatible IPv6 addresses with private IPs" do
|
|
assert_private_ip "::192.168.1.1"
|
|
assert_private_ip "::10.0.0.1"
|
|
end
|
|
|
|
test "private_ip? returns true for IPv4-compatible IPv6 addresses with link-local IPs" do
|
|
assert_private_ip "::169.254.169.254" # AWS metadata via compat format - the reported bypass
|
|
end
|
|
|
|
test "private_ip? returns true for IPv4-compatible IPv6 addresses even with public IPs" do
|
|
# Block all ipv4_compat? since DNS never returns this format legitimately
|
|
assert_private_ip "::93.184.216.34"
|
|
end
|
|
|
|
test "private_ip? returns true for carrier-grade NAT addresses (RFC6598)" do
|
|
assert_private_ip "100.64.0.1"
|
|
assert_private_ip "100.127.255.255"
|
|
end
|
|
|
|
test "private_ip? returns true for NAT64 addresses embedding a private IPv4" do
|
|
assert_private_ip "64:ff9b::a9fe:a9fe" # NAT64 -> 169.254.169.254 (AWS metadata)
|
|
assert_private_ip "64:ff9b::a00:5" # NAT64 -> 10.0.0.5
|
|
end
|
|
|
|
test "private_ip? returns true for local-use NAT64 addresses embedding a private IPv4 (RFC8215)" do
|
|
assert_private_ip "64:ff9b:1::a00:1" # local-use NAT64 -> 10.0.0.1
|
|
end
|
|
|
|
test "private_ip? returns false for local-use NAT64 addresses embedding a public IPv4 (RFC8215)" do
|
|
assert_not RestrictedHTTP::PrivateNetworkGuard.private_ip?("64:ff9b:1::808:808") # -> 8.8.8.8
|
|
end
|
|
|
|
test "private_ip? returns false for NAT64 addresses embedding a public IPv4" do
|
|
# DNS64 legitimately synthesizes these for public sites on IPv6-only hosts.
|
|
assert_not RestrictedHTTP::PrivateNetworkGuard.private_ip?("64:ff9b::808:808") # -> 8.8.8.8
|
|
end
|
|
|
|
test "private_ip? returns true for 6to4 and Teredo transition addresses" do
|
|
assert_private_ip "2002:a9fe:a9fe::" # 6to4 embedding 169.254.169.254
|
|
assert_private_ip "2001::1" # Teredo
|
|
end
|
|
|
|
test "private_ip? returns true for IPv6 loopback, ULA, and link-local" do
|
|
assert_private_ip "::1"
|
|
assert_private_ip "fd00:ec2::254" # AWS IMDSv6 (ULA)
|
|
assert_private_ip "fe80::1"
|
|
end
|
|
|
|
test "private_ip? returns true for IPv6 multicast, documentation, and benchmarking ranges" do
|
|
assert_private_ip "ff02::1"
|
|
assert_private_ip "2001:db8::1"
|
|
assert_private_ip "2001:2::1" # benchmarking (RFC5180), matches 198.18.0.0/15
|
|
end
|
|
|
|
test "private_ip? returns false for public IPv6 addresses" do
|
|
assert_not RestrictedHTTP::PrivateNetworkGuard.private_ip?("2606:4700:4700::1111")
|
|
end
|
|
|
|
test "private_ip? returns true for invalid addresses" do
|
|
assert RestrictedHTTP::PrivateNetworkGuard.private_ip?("not-an-ip")
|
|
assert RestrictedHTTP::PrivateNetworkGuard.private_ip?("")
|
|
end
|
|
|
|
test "resolve raises Violation for private hostname" do
|
|
Resolv.stubs(:getaddress).returns("192.168.1.1")
|
|
assert_raises RestrictedHTTP::Violation do
|
|
RestrictedHTTP::PrivateNetworkGuard.resolve("private.example.com")
|
|
end
|
|
end
|
|
|
|
test "resolve returns IP for public hostname" do
|
|
Resolv.stubs(:getaddress).returns("93.184.216.34")
|
|
assert_equal "93.184.216.34", RestrictedHTTP::PrivateNetworkGuard.resolve("example.com")
|
|
end
|
|
|
|
private
|
|
def assert_private_ip(address)
|
|
assert RestrictedHTTP::PrivateNetworkGuard.private_ip?(address),
|
|
"Expected #{address} to be classified as private"
|
|
end
|
|
end
|