mirror of
https://github.com/netbox-community/netbox.git
synced 2026-04-17 22:49:59 +02:00
Closes #21468: copy_safe_request() should retain non-sensitive HTTP request headers
This commit is contained in:
@@ -38,6 +38,7 @@ FILTER_TREENODE_NEGATION_LOOKUP_MAP = dict(
|
|||||||
# HTTP Request META safe copy
|
# HTTP Request META safe copy
|
||||||
#
|
#
|
||||||
|
|
||||||
|
# Non-HTTP_ META keys to include when copying a request (whitelist)
|
||||||
HTTP_REQUEST_META_SAFE_COPY = [
|
HTTP_REQUEST_META_SAFE_COPY = [
|
||||||
'CONTENT_LENGTH',
|
'CONTENT_LENGTH',
|
||||||
'CONTENT_TYPE',
|
'CONTENT_TYPE',
|
||||||
@@ -61,6 +62,13 @@ HTTP_REQUEST_META_SAFE_COPY = [
|
|||||||
'SERVER_PORT',
|
'SERVER_PORT',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# HTTP_ META keys known to carry sensitive data; excluded when copying a request (denylist)
|
||||||
|
HTTP_REQUEST_META_SENSITIVE = {
|
||||||
|
'HTTP_AUTHORIZATION',
|
||||||
|
'HTTP_COOKIE',
|
||||||
|
'HTTP_PROXY_AUTHORIZATION',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# CSV-style format delimiters
|
# CSV-style format delimiters
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from netaddr import AddrFormatError, IPAddress
|
|||||||
|
|
||||||
from netbox.registry import registry
|
from netbox.registry import registry
|
||||||
|
|
||||||
from .constants import HTTP_REQUEST_META_SAFE_COPY
|
from .constants import HTTP_REQUEST_META_SAFE_COPY, HTTP_REQUEST_META_SENSITIVE
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'NetBoxFakeRequest',
|
'NetBoxFakeRequest',
|
||||||
@@ -45,11 +45,14 @@ def copy_safe_request(request, include_files=True):
|
|||||||
request: The original request object
|
request: The original request object
|
||||||
include_files: Whether to include request.FILES.
|
include_files: Whether to include request.FILES.
|
||||||
"""
|
"""
|
||||||
meta = {
|
meta = {}
|
||||||
k: request.META[k]
|
for k, v in request.META.items():
|
||||||
for k in HTTP_REQUEST_META_SAFE_COPY
|
if not isinstance(v, str):
|
||||||
if k in request.META and isinstance(request.META[k], str)
|
continue
|
||||||
}
|
if k in HTTP_REQUEST_META_SAFE_COPY:
|
||||||
|
meta[k] = v
|
||||||
|
elif k.startswith('HTTP_') and k not in HTTP_REQUEST_META_SENSITIVE:
|
||||||
|
meta[k] = v
|
||||||
data = {
|
data = {
|
||||||
'META': meta,
|
'META': meta,
|
||||||
'COOKIES': request.COOKIES,
|
'COOKIES': request.COOKIES,
|
||||||
|
|||||||
@@ -1,7 +1,42 @@
|
|||||||
|
from django.contrib.auth.models import AnonymousUser
|
||||||
from django.test import RequestFactory, TestCase
|
from django.test import RequestFactory, TestCase
|
||||||
from netaddr import IPAddress
|
from netaddr import IPAddress
|
||||||
|
|
||||||
from utilities.request import get_client_ip
|
from utilities.request import copy_safe_request, get_client_ip
|
||||||
|
|
||||||
|
|
||||||
|
class CopySafeRequestTests(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.factory = RequestFactory()
|
||||||
|
|
||||||
|
def _make_request(self, **kwargs):
|
||||||
|
request = self.factory.get('/', **kwargs)
|
||||||
|
request.user = AnonymousUser()
|
||||||
|
return request
|
||||||
|
|
||||||
|
def test_standard_meta_keys_copied(self):
|
||||||
|
request = self._make_request(HTTP_USER_AGENT='TestAgent/1.0')
|
||||||
|
fake = copy_safe_request(request)
|
||||||
|
self.assertEqual(fake.META.get('HTTP_USER_AGENT'), 'TestAgent/1.0')
|
||||||
|
|
||||||
|
def test_arbitrary_http_headers_copied(self):
|
||||||
|
"""Arbitrary HTTP_ headers (e.g. X-NetBox-*) should be included."""
|
||||||
|
request = self._make_request(HTTP_X_NETBOX_BRANCH='my-branch')
|
||||||
|
fake = copy_safe_request(request)
|
||||||
|
self.assertEqual(fake.META.get('HTTP_X_NETBOX_BRANCH'), 'my-branch')
|
||||||
|
|
||||||
|
def test_sensitive_headers_excluded(self):
|
||||||
|
"""Authorization and Cookie headers must not be copied."""
|
||||||
|
request = self._make_request(HTTP_AUTHORIZATION='Bearer secret')
|
||||||
|
fake = copy_safe_request(request)
|
||||||
|
self.assertNotIn('HTTP_AUTHORIZATION', fake.META)
|
||||||
|
|
||||||
|
def test_non_string_meta_values_excluded(self):
|
||||||
|
"""Non-string META values must not be copied."""
|
||||||
|
request = self._make_request()
|
||||||
|
request.META['HTTP_X_CUSTOM_INT'] = 42
|
||||||
|
fake = copy_safe_request(request)
|
||||||
|
self.assertNotIn('HTTP_X_CUSTOM_INT', fake.META)
|
||||||
|
|
||||||
|
|
||||||
class GetClientIPTests(TestCase):
|
class GetClientIPTests(TestCase):
|
||||||
|
|||||||
Reference in New Issue
Block a user