mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-08-28 05:33:24 +00:00
Security: harden is_public_ip
This commit is contained in:
@@ -6,6 +6,17 @@ from urllib.parse import urlparse
|
||||
|
||||
import httpx
|
||||
|
||||
# Ranges ipaddress does not report as private, but which routinely front
|
||||
# internal infrastructure.
|
||||
_NON_PUBLIC_NETWORKS = (
|
||||
# RFC 6598 shared address space: ISP CGNAT, and the default pod/service
|
||||
# CIDR on several managed Kubernetes offerings.
|
||||
ipaddress.ip_network("100.64.0.0/10"),
|
||||
# RFC 6052 NAT64 well-known prefix: 64:ff9b::7f00:1 is 127.0.0.1 wherever
|
||||
# a NAT64 gateway exists.
|
||||
ipaddress.ip_network("64:ff9b::/96"),
|
||||
)
|
||||
|
||||
|
||||
def is_public_ip(ip: str | int) -> bool:
|
||||
try:
|
||||
@@ -16,6 +27,7 @@ def is_public_ip(ip: str | int) -> bool:
|
||||
or obj.is_link_local
|
||||
or obj.is_multicast
|
||||
or obj.is_unspecified
|
||||
or any(obj in network for network in _NON_PUBLIC_NETWORKS)
|
||||
)
|
||||
except ValueError: # pragma: no cover
|
||||
return False
|
||||
|
||||
@@ -4,6 +4,7 @@ import httpx
|
||||
import pytest
|
||||
|
||||
from paperless.network import PinnedHostHTTPTransport
|
||||
from paperless.network import is_public_ip
|
||||
|
||||
|
||||
def test_pinned_host_transport_blocks_internal_rebinding():
|
||||
@@ -48,3 +49,42 @@ def test_pinned_host_transport_rewrites_to_vetted_ip():
|
||||
response = transport.handle_request(request)
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"address",
|
||||
[
|
||||
"127.0.0.1",
|
||||
"10.0.0.5",
|
||||
"169.254.169.254",
|
||||
"::1",
|
||||
"fc00::1",
|
||||
"fe80::1",
|
||||
# RFC 6598 shared address space, incl. both edges of the /10
|
||||
"100.64.0.0",
|
||||
"100.64.0.1",
|
||||
"100.127.255.255",
|
||||
# RFC 6052 NAT64 well-known prefix, embedding 127.0.0.1 and 10.0.0.5
|
||||
"64:ff9b::7f00:1",
|
||||
"64:ff9b::a00:5",
|
||||
],
|
||||
)
|
||||
def test_is_public_ip_blocks_non_public_addresses(address):
|
||||
assert not is_public_ip(address)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"address",
|
||||
[
|
||||
"8.8.8.8",
|
||||
"142.250.185.196",
|
||||
"2606:4700:4700::1111",
|
||||
# just outside the ranges above, must stay reachable
|
||||
"100.63.255.255",
|
||||
"100.128.0.0",
|
||||
"64:ff9a::1",
|
||||
"64:ff9c::1",
|
||||
],
|
||||
)
|
||||
def test_is_public_ip_allows_public_addresses(address):
|
||||
assert is_public_ip(address)
|
||||
|
||||
Reference in New Issue
Block a user