Closes #22654: Redact install paths from debug tracebacks (#22655)

This commit is contained in:
bctiemann 2026-07-14 15:44:19 -04:00 committed by GitHub
parent ad054fc694
commit 16875c747c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 49 additions and 1 deletions

View File

@ -1,3 +1,6 @@
import os
import re
import sys
import traceback
import jsonschema
@ -333,7 +336,23 @@ class ConfigTemplate(
is returned.
"""
if self.debug:
return ''.join(traceback.format_exception(exc))
# Strip deployment-specific path prefixes from File "..." lines to avoid disclosing
# the server's filesystem layout. install_root covers all NetBox source files plus
# any venv co-located inside the repo. When the venv lives outside the repo
# (the typical production pattern, e.g. ~/.venv/netbox/), sys.prefix differs from
# sys.base_prefix and the venv root is stripped separately so that the deployment
# user's home directory is not exposed. Stdlib paths not under either prefix are
# left as-is — they reveal only standard OS locations, not deployment structure.
install_root = os.path.dirname(settings.BASE_DIR) + os.sep
prefixes_to_strip = [install_root]
if sys.prefix != sys.base_prefix:
venv_root = sys.prefix + os.sep
if venv_root != install_root:
prefixes_to_strip.append(venv_root)
tb = ''.join(traceback.format_exception(exc))
for prefix in prefixes_to_strip:
tb = re.sub(r'(File ")' + re.escape(prefix), r'\1', tb)
return tb
if isinstance(exc, TemplateError):
parts = [f"{type(exc).__name__}: {exc}"]
if getattr(exc, 'name', None):

View File

@ -1,9 +1,12 @@
import io
import os
import sys
import tempfile
from pathlib import Path
from types import SimpleNamespace
from unittest.mock import patch
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.core.files.base import ContentFile
from django.core.files.storage import Storage
@ -1200,6 +1203,32 @@ class ConfigTemplateDebugTestCase(TestCase):
with self.assertRaises(TemplateSyntaxError):
render_jinja2("{% debug %}", {}, debug=False)
def test_format_render_error_debug_redacts_install_path(self):
"""format_render_error() strips the repo install-path prefix from debug tracebacks."""
t = ConfigTemplate(name='redact-test', template_code='hello', debug=True)
try:
raise ValueError("deliberate test error")
except ValueError as exc:
result = t.format_render_error(exc)
install_root = os.path.dirname(settings.BASE_DIR) + os.sep
self.assertIn('Traceback', result)
self.assertNotIn(install_root, result)
# Also verify the venv prefix is stripped when running inside a virtualenv.
if sys.prefix != sys.base_prefix:
venv_root = sys.prefix + os.sep
if venv_root != install_root:
self.assertNotIn(venv_root, result)
def test_format_render_error_non_debug_returns_concise_message(self):
"""format_render_error() returns a one-line message (no traceback) when debug=False."""
t = ConfigTemplate(name='nodebug-test', template_code='hello', debug=False)
try:
raise TemplateError("bad template")
except TemplateError as exc:
result = t.format_render_error(exc)
self.assertNotIn('Traceback', result)
self.assertIn('TemplateError', result)
class JinjaEnvFilterTestCase(TestCase):
"""