Information disclosure (path leakage in warning output)
Description
The commit hardens warning output by caching and reusing internal Django file prefixes and using skip_file_prefixes to suppress Django’s own filesystem paths from warning traces. Previously, certain warnings could leak internal Django file paths in stack traces (information disclosure). The change introduces django_file_prefixes() to return a tuple of internal Django prefixes and updates warning calls to pass these prefixes, effectively filtering out Django’s own path components from warning output. This reduces leakage of internal filesystem structure in error/warning messages.
Proof of Concept
PoC (conceptual):
Prerequisites:
- A Django 5.1.x environment predating this commit.
- A scenario that triggers a warning which prints a traceback (e.g., a warning emitted from Django internals that includes a stack trace).
Steps:
1) Run a minimal Django app/request that exercises the warning path (the code paths touched by this patch are cross-cutting (template warnings and deprecation warnings) and will emit a stack trace that includes file paths).
2) Capture the warning output (e.g., via logging or Python warnings capture) and observe the traceback frames.
3) Observe that, in the pre-fix environment, the traceback includes internal Django filesystem paths (e.g., paths under /path/to/project/django/...).
4) Build and run the same scenario after applying this commit (or using a Django build from this commit).
5) Re-capture the warning output and observe that the internal Django prefixes are filtered out; the traceback now omits Django’s internal paths and starts from application/user code paths.
Notes:
- The exact file paths shown depend on the checkout layout and environment.
- This PoC is conceptual because it depends on a specific warning path that prints a traceback; the important observation is the leakage before the fix and its mitigation after the fix.
Commit Details
Author: Adam Johnson
Date: 2025-09-12 08:35 UTC
Message:
Refs #35667 -- Cached Django file prefixes for warnings.
Triage Assessment
Vulnerability Type: Information disclosure
Confidence: MEDIUM
Reasoning:
The commit introduces a mechanism to filter or skip Django's internal file prefixes from warning traces, reducing exposure of internal filesystem paths in warning output. This mitigates information disclosure through error/warning messages. While not a classic vulnerability patch, it addresses security-related leakage of internal paths.
Verification Assessment
Vulnerability Type: Information disclosure (path leakage in warning output)
Confidence: MEDIUM
Affected Versions: Django stable/5.1.x prior to this commit (i.e., the 5.1.x series before 7b26b64a63b5...).
Code Diff
diff --git a/django/template/base.py b/django/template/base.py
index b87291746b68..5e541c3960ee 100644
--- a/django/template/base.py
+++ b/django/template/base.py
@@ -52,13 +52,12 @@
import inspect
import logging
-import os
import re
import warnings
from enum import Enum
-import django
from django.template.context import BaseContext
+from django.utils.deprecation import django_file_prefixes
from django.utils.formats import localize
from django.utils.html import conditional_escape
from django.utils.regex_helper import _lazy_re_compile
@@ -329,7 +328,7 @@ def source(self):
"PartialTemplate.source is only available when template "
"debugging is enabled.",
RuntimeWarning,
- skip_file_prefixes=(os.path.dirname(django.__file__),),
+ skip_file_prefixes=django_file_prefixes(),
)
return self.find_partial_source(template.source)
diff --git a/django/utils/deprecation.py b/django/utils/deprecation.py
index 7d5289bd3cc5..376eb33fae42 100644
--- a/django/utils/deprecation.py
+++ b/django/utils/deprecation.py
@@ -9,6 +9,15 @@
import django
+@functools.cache
+def django_file_prefixes():
+ try:
+ file = django.__file__
+ except AttributeError:
+ return ()
+ return (os.path.dirname(file),)
+
+
class RemovedInDjango61Warning(DeprecationWarning):
pass
@@ -237,7 +246,7 @@ def remap_deprecated_args(args, kwargs):
f"Passing positional argument(s) {remapped_names_str} to {func_name}() "
"is deprecated. Use keyword arguments instead.",
deprecation_warning,
- skip_file_prefixes=(os.path.dirname(django.__file__),),
+ skip_file_prefixes=django_file_prefixes(),
)
return remaining_args, updated_kwargs
diff --git a/tests/deprecation/tests.py b/tests/deprecation/tests.py
index 66f6a4d9221b..3d384b38b7a5 100644
--- a/tests/deprecation/tests.py
+++ b/tests/deprecation/tests.py
@@ -1,7 +1,32 @@
+import os
import warnings
+import django
from django.test import SimpleTestCase
-from django.utils.deprecation import RemovedAfterNextVersionWarning, RenameMethodsBase
+from django.utils.deprecation import (
+ RemovedAfterNextVersionWarning,
+ RenameMethodsBase,
+ django_file_prefixes,
+)
+
+
+class DjangoFilePrefixesTests(SimpleTestCase):
+ def setUp(self):
+ django_file_prefixes.cache_clear()
+
+ def test_no_file(self):
+ orig_file = django.__file__
+ try:
+ del django.__file__
+ self.assertEqual(django_file_prefixes(), ())
+ finally:
+ django.__file__ = orig_file
+
+ def test_with_file(self):
+ prefixes = django_file_prefixes()
+ self.assertIsInstance(prefixes, tuple)
+ self.assertEqual(len(prefixes), 1)
+ self.assertTrue(prefixes[0].endswith(f"{os.path.sep}django"))
class RenameManagerMethods(RenameMethodsBase):