Information disclosure (path leakage in warning output)

MEDIUM
django/django
Commit: 7b26b64a63b5
Affected: Django stable/5.1.x prior to this commit (i.e., the 5.1.x series before 7b26b64a63b5...).
2026-04-05 14:29 UTC

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):
← Back to Alerts View on GitHub →