Explorar el Código

Closes #21468: copy_safe_request() should retain non-sensitive HTTP request headers (#21577)

- Define `HTTP_REQUEST_META_SENSITIVE` to serve as a blacklist for
  known-sensitive headers
- Modify `copy_safe_request()` to copy all non-sensitive headers
  (ignoring any not defined as strings)
- Add the `CopySafeRequestTests` test suite
Jeremy Stretch hace 11 horas
padre
commit
cb5521f818

+ 8 - 0
netbox/utilities/constants.py

@@ -38,6 +38,7 @@ FILTER_TREENODE_NEGATION_LOOKUP_MAP = dict(
 # HTTP Request META safe copy
 #
 
+# Non-HTTP_ META keys to include when copying a request (whitelist)
 HTTP_REQUEST_META_SAFE_COPY = [
     'CONTENT_LENGTH',
     'CONTENT_TYPE',
@@ -61,6 +62,13 @@ HTTP_REQUEST_META_SAFE_COPY = [
     '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

+ 9 - 6
netbox/utilities/request.py

@@ -8,7 +8,7 @@ from netaddr import AddrFormatError, IPAddress
 
 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__ = (
     'NetBoxFakeRequest',
@@ -45,11 +45,14 @@ def copy_safe_request(request, include_files=True):
         request: The original request object
         include_files: Whether to include request.FILES.
     """
-    meta = {
-        k: request.META[k]
-        for k in HTTP_REQUEST_META_SAFE_COPY
-        if k in request.META and isinstance(request.META[k], str)
-    }
+    meta = {}
+    for k, v in request.META.items():
+        if not isinstance(v, 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 = {
         'META': meta,
         'COOKIES': request.COOKIES,

+ 36 - 1
netbox/utilities/tests/test_request.py

@@ -1,7 +1,42 @@
+from django.contrib.auth.models import AnonymousUser
 from django.test import RequestFactory, TestCase
 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):