Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-django-debug-toolbar for openSUSE:Factory checked in at 2021-05-10 15:39:13 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-django-debug-toolbar (Old) and /work/SRC/openSUSE:Factory/.python-django-debug-toolbar.new.2988 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-django-debug-toolbar" Mon May 10 15:39:13 2021 rev:18 rq:891926 version:3.2.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-django-debug-toolbar/python-django-debug-toolbar.changes 2021-01-25 18:24:35.772513753 +0100 +++ /work/SRC/openSUSE:Factory/.python-django-debug-toolbar.new.2988/python-django-debug-toolbar.changes 2021-05-10 15:41:58.320947338 +0200 @@ -1,0 +2,6 @@ +Sun May 9 23:52:34 UTC 2021 - Daniel Molkentin <daniel.molken...@suse.com> + +- Update to v3.2.1 + * Fix CVE-2021-30459 by creating signature from all data fields + +------------------------------------------------------------------- Old: ---- django-debug-toolbar-3.2.tar.gz New: ---- django-debug-toolbar-3.2.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-django-debug-toolbar.spec ++++++ --- /var/tmp/diff_new_pack.gavX1D/_old 2021-05-10 15:41:58.748945664 +0200 +++ /var/tmp/diff_new_pack.gavX1D/_new 2021-05-10 15:41:58.748945664 +0200 @@ -19,7 +19,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define skip_python2 1 Name: python-django-debug-toolbar -Version: 3.2 +Version: 3.2.1 Release: 0 Summary: A configurable set of panels that display various debug information License: BSD-3-Clause ++++++ django-debug-toolbar-3.2.tar.gz -> django-debug-toolbar-3.2.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/.github/workflows/test.yml new/django-debug-toolbar-3.2.1/.github/workflows/test.yml --- old/django-debug-toolbar-3.2/.github/workflows/test.yml 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/.github/workflows/test.yml 2021-04-14 17:18:10.000000000 +0200 @@ -15,8 +15,7 @@ mariadb: image: mariadb:10.3 env: - MYSQL_ROOT_PASSWORD: mysql - MYSQL_DATABASE: mysql + MYSQL_ROOT_PASSWORD: debug_toolbar options: >- --health-cmd "mysqladmin ping" --health-interval 10s @@ -61,11 +60,10 @@ run: tox env: DB_BACKEND: mysql - DB_NAME: mysql DB_USER: root - DB_PASSWORD: mysql - DB_HOST: "127.0.0.1" - DB_PORT: "3306" + DB_PASSWORD: debug_toolbar + DB_HOST: 127.0.0.1 + DB_PORT: 3306 - name: Upload coverage uses: codecov/codecov-action@v1 @@ -84,7 +82,9 @@ postgres: image: 'postgres:9.5' env: - POSTGRES_PASSWORD: postgres + POSTGRES_DB: debug_toolbar + POSTGRES_USER: debug_toolbar + POSTGRES_PASSWORD: debug_toolbar ports: - 5432:5432 options: >- @@ -129,9 +129,6 @@ run: tox env: DB_BACKEND: postgresql - DB_NAME: postgres - DB_USER: postgres - DB_PASSWORD: postgres DB_HOST: localhost DB_PORT: 5432 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/.tx/config new/django-debug-toolbar-3.2.1/.tx/config --- old/django-debug-toolbar-3.2/.tx/config 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/.tx/config 2021-04-14 17:18:10.000000000 +0200 @@ -2,7 +2,7 @@ host = https://www.transifex.com lang_map = sr@latin:sr_Latn -[django-debug-toolbar.master] +[django-debug-toolbar.main] file_filter = debug_toolbar/locale/<lang>/LC_MESSAGES/django.po source_file = debug_toolbar/locale/en/LC_MESSAGES/django.po source_lang = en diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/Makefile new/django-debug-toolbar-3.2.1/Makefile --- old/django-debug-toolbar-3.2/Makefile 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/Makefile 2021-04-14 17:18:10.000000000 +0200 @@ -8,6 +8,7 @@ flake8 npx eslint --ignore-path .gitignore --fix . npx prettier --ignore-path .gitignore --write $(PRETTIER_TARGETS) + ! grep -r '\(style=\|onclick=\|<script>\|<style\)' debug_toolbar/templates/ style_check: package-lock.json isort -c . @@ -15,6 +16,7 @@ flake8 npx eslint --ignore-path .gitignore . npx prettier --ignore-path .gitignore --check $(PRETTIER_TARGETS) + ! grep -r '\(style=\|onclick=\|<script>\|<style\)' debug_toolbar/templates/ example: python example/manage.py migrate --noinput diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/README.rst new/django-debug-toolbar-3.2.1/README.rst --- old/django-debug-toolbar-3.2/README.rst 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/README.rst 2021-04-14 17:18:10.000000000 +0200 @@ -10,7 +10,7 @@ :target: https://github.com/jazzband/django-debug-toolbar/actions :alt: Build Status -.. image:: https://codecov.io/gh/jazzband/django-debug-toolbar/branch/master/graph/badge.svg +.. image:: https://codecov.io/gh/jazzband/django-debug-toolbar/branch/main/graph/badge.svg :target: https://codecov.io/gh/jazzband/django-debug-toolbar :alt: Test coverage status @@ -28,13 +28,13 @@ Here's a screenshot of the toolbar in action: -.. image:: https://raw.github.com/jazzband/django-debug-toolbar/master/example/django-debug-toolbar.png +.. image:: https://raw.github.com/jazzband/django-debug-toolbar/main/example/django-debug-toolbar.png :alt: Django Debug Toolbar screenshot In addition to the built-in panels, a number of third-party panels are contributed by the community. -The current stable version of the Debug Toolbar is 3.2. It works on +The current stable version of the Debug Toolbar is 3.2.1. It works on Django ??? 2.2. Documentation, including installation and configuration instructions, is diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/debug_toolbar/__init__.py new/django-debug-toolbar-3.2.1/debug_toolbar/__init__.py --- old/django-debug-toolbar-3.2/debug_toolbar/__init__.py 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/debug_toolbar/__init__.py 2021-04-14 17:18:10.000000000 +0200 @@ -1,13 +1,9 @@ __all__ = ["VERSION"] -try: - import pkg_resources - - VERSION = pkg_resources.get_distribution("django-debug-toolbar").version -except Exception: - VERSION = "unknown" - +# Do not use pkg_resources to find the version but set it here directly! +# see issue #1446 +VERSION = "3.2.1" # Code that discovers files or modules in INSTALLED_APPS imports this module. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/debug_toolbar/decorators.py new/django-debug-toolbar-3.2.1/debug_toolbar/decorators.py --- old/django-debug-toolbar-3.2/debug_toolbar/decorators.py 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/debug_toolbar/decorators.py 2021-04-14 17:18:10.000000000 +0200 @@ -1,6 +1,6 @@ import functools -from django.http import Http404 +from django.http import Http404, HttpResponseBadRequest def require_show_toolbar(view): @@ -15,3 +15,21 @@ return view(request, *args, **kwargs) return inner + + +def signed_data_view(view): + """Decorator that handles unpacking a signed data form""" + + @functools.wraps(view) + def inner(request, *args, **kwargs): + from debug_toolbar.forms import SignedDataForm + + data = request.GET if request.method == "GET" else request.POST + signed_form = SignedDataForm(data) + if signed_form.is_valid(): + return view( + request, *args, verified_data=signed_form.verified_data(), **kwargs + ) + return HttpResponseBadRequest("Invalid signature") + + return inner diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/debug_toolbar/forms.py new/django-debug-toolbar-3.2.1/debug_toolbar/forms.py --- old/django-debug-toolbar-3.2/debug_toolbar/forms.py 1970-01-01 01:00:00.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/debug_toolbar/forms.py 2021-04-14 17:18:10.000000000 +0200 @@ -0,0 +1,53 @@ +import json + +from django import forms +from django.core import signing +from django.core.exceptions import ValidationError +from django.utils.encoding import force_str + + +class SignedDataForm(forms.Form): + """Helper form that wraps a form to validate its contents on post. + + class PanelForm(forms.Form): + # fields + + On render: + form = SignedDataForm(initial=PanelForm(initial=data).initial) + + On POST: + signed_form = SignedDataForm(request.POST) + if signed_form.is_valid(): + panel_form = PanelForm(signed_form.verified_data) + if panel_form.is_valid(): + # Success + Or wrap the FBV with ``debug_toolbar.decorators.signed_data_view`` + """ + + salt = "django_debug_toolbar" + signed = forms.CharField(required=True, widget=forms.HiddenInput) + + def __init__(self, *args, **kwargs): + initial = kwargs.pop("initial", None) + if initial: + initial = {"signed": self.sign(initial)} + super().__init__(*args, initial=initial, **kwargs) + + def clean_signed(self): + try: + verified = json.loads( + signing.Signer(salt=self.salt).unsign(self.cleaned_data["signed"]) + ) + return verified + except signing.BadSignature: + raise ValidationError("Bad signature") + + def verified_data(self): + return self.is_valid() and self.cleaned_data["signed"] + + @classmethod + def sign(cls, data): + items = sorted(data.items(), key=lambda item: item[0]) + return signing.Signer(salt=cls.salt).sign( + json.dumps({key: force_str(value) for key, value in items}) + ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/debug_toolbar/middleware.py new/django-debug-toolbar-3.2.1/debug_toolbar/middleware.py --- old/django-debug-toolbar-3.2/debug_toolbar/middleware.py 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/debug_toolbar/middleware.py 2021-04-14 17:18:10.000000000 +0200 @@ -44,7 +44,7 @@ def __call__(self, request): # Decide whether the toolbar is active for this request. show_toolbar = get_show_toolbar() - if not show_toolbar(request) or request.path.startswith("/__debug__/"): + if not show_toolbar(request) or DebugToolbar.is_toolbar_request(request): return self.get_response(request) toolbar = DebugToolbar(request, self.get_response) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/debug_toolbar/panels/cache.py new/django-debug-toolbar-3.2.1/debug_toolbar/panels/cache.py --- old/django-debug-toolbar-3.2/debug_toolbar/panels/cache.py 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/debug_toolbar/panels/cache.py 2021-04-14 17:18:10.000000000 +0200 @@ -3,9 +3,19 @@ import time from collections import OrderedDict +try: + from django.utils.connection import ConnectionProxy +except ImportError: + ConnectionProxy = None + from django.conf import settings from django.core import cache -from django.core.cache import CacheHandler, caches as original_caches +from django.core.cache import ( + DEFAULT_CACHE_ALIAS, + CacheHandler, + cache as original_cache, + caches as original_caches, +) from django.core.cache.backends.base import BaseCache from django.dispatch import Signal from django.middleware import cache as middleware_cache @@ -246,8 +256,13 @@ else: cache.caches = CacheHandlerPatch() + # Wrap the patched cache inside Django's ConnectionProxy + if ConnectionProxy: + cache.cache = ConnectionProxy(cache.caches, DEFAULT_CACHE_ALIAS) + def disable_instrumentation(self): cache.caches = original_caches + cache.cache = original_cache # While it can be restored to the original, any views that were # wrapped with the cache_page decorator will continue to use a # monkey patched cache. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/debug_toolbar/panels/history/forms.py new/django-debug-toolbar-3.2.1/debug_toolbar/panels/history/forms.py --- old/django-debug-toolbar-3.2/debug_toolbar/panels/history/forms.py 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/debug_toolbar/panels/history/forms.py 2021-04-14 17:18:10.000000000 +0200 @@ -1,11 +1,4 @@ -import hashlib -import hmac - from django import forms -from django.conf import settings -from django.core.exceptions import ValidationError -from django.utils.crypto import constant_time_compare -from django.utils.encoding import force_bytes class HistoryStoreForm(forms.Form): @@ -16,26 +9,3 @@ """ store_id = forms.CharField(widget=forms.HiddenInput()) - hash = forms.CharField(widget=forms.HiddenInput()) - - def __init__(self, *args, **kwargs): - initial = kwargs.get("initial", None) - - if initial is not None: - initial["hash"] = self.make_hash(initial) - - super().__init__(*args, **kwargs) - - @staticmethod - def make_hash(data): - m = hmac.new(key=force_bytes(settings.SECRET_KEY), digestmod=hashlib.sha1) - m.update(force_bytes(data["store_id"])) - return m.hexdigest() - - def clean_hash(self): - hash = self.cleaned_data["hash"] - - if not constant_time_compare(hash, self.make_hash(self.data)): - raise ValidationError("Tamper alert") - - return hash diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/debug_toolbar/panels/history/panel.py new/django-debug-toolbar-3.2.1/debug_toolbar/panels/history/panel.py --- old/django-debug-toolbar-3.2/debug_toolbar/panels/history/panel.py 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/debug_toolbar/panels/history/panel.py 2021-04-14 17:18:10.000000000 +0200 @@ -8,6 +8,7 @@ from django.utils import timezone from django.utils.translation import gettext_lazy as _ +from debug_toolbar.forms import SignedDataForm from debug_toolbar.panels import Panel from debug_toolbar.panels.history import views from debug_toolbar.panels.history.forms import HistoryStoreForm @@ -76,7 +77,9 @@ for id, toolbar in reversed(self.toolbar._store.items()): stores[id] = { "toolbar": toolbar, - "form": HistoryStoreForm(initial={"store_id": id}), + "form": SignedDataForm( + initial=HistoryStoreForm(initial={"store_id": id}).initial + ), } return render_to_string( @@ -84,8 +87,10 @@ { "current_store_id": self.toolbar.store_id, "stores": stores, - "refresh_form": HistoryStoreForm( - initial={"store_id": self.toolbar.store_id} + "refresh_form": SignedDataForm( + initial=HistoryStoreForm( + initial={"store_id": self.toolbar.store_id} + ).initial ), }, ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/debug_toolbar/panels/history/views.py new/django-debug-toolbar-3.2.1/debug_toolbar/panels/history/views.py --- old/django-debug-toolbar-3.2/debug_toolbar/panels/history/views.py 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/debug_toolbar/panels/history/views.py 2021-04-14 17:18:10.000000000 +0200 @@ -1,15 +1,16 @@ from django.http import HttpResponseBadRequest, JsonResponse from django.template.loader import render_to_string -from debug_toolbar.decorators import require_show_toolbar +from debug_toolbar.decorators import require_show_toolbar, signed_data_view from debug_toolbar.panels.history.forms import HistoryStoreForm from debug_toolbar.toolbar import DebugToolbar @require_show_toolbar -def history_sidebar(request): +@signed_data_view +def history_sidebar(request, verified_data): """Returns the selected debug toolbar history snapshot.""" - form = HistoryStoreForm(request.GET) + form = HistoryStoreForm(verified_data) if form.is_valid(): store_id = form.cleaned_data["store_id"] @@ -32,9 +33,10 @@ @require_show_toolbar -def history_refresh(request): +@signed_data_view +def history_refresh(request, verified_data): """Returns the refreshed list of table rows for the History Panel.""" - form = HistoryStoreForm(request.GET) + form = HistoryStoreForm(verified_data) if form.is_valid(): requests = [] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/debug_toolbar/panels/request.py new/django-debug-toolbar-3.2.1/debug_toolbar/panels/request.py --- old/django-debug-toolbar-3.2/debug_toolbar/panels/request.py 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/debug_toolbar/panels/request.py 2021-04-14 17:18:10.000000000 +0200 @@ -44,7 +44,16 @@ view_info["view_func"] = get_name_from_obj(func) view_info["view_args"] = args view_info["view_kwargs"] = kwargs - view_info["view_urlname"] = getattr(match, "url_name", _("<unavailable>")) + + if getattr(match, "url_name", False): + url_name = match.url_name + if match.namespaces: + url_name = ":".join([*match.namespaces, url_name]) + else: + url_name = _("<unavailable>") + + view_info["view_urlname"] = url_name + except Http404: pass self.record_stats(view_info) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/debug_toolbar/panels/sql/forms.py new/django-debug-toolbar-3.2.1/debug_toolbar/panels/sql/forms.py --- old/django-debug-toolbar-3.2/debug_toolbar/panels/sql/forms.py 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/debug_toolbar/panels/sql/forms.py 2021-04-14 17:18:10.000000000 +0200 @@ -1,13 +1,8 @@ -import hashlib -import hmac import json from django import forms -from django.conf import settings from django.core.exceptions import ValidationError from django.db import connections -from django.utils.crypto import constant_time_compare -from django.utils.encoding import force_bytes from django.utils.functional import cached_property from debug_toolbar.panels.sql.utils import reformat_sql @@ -21,7 +16,6 @@ raw_sql: The sql statement with placeholders params: JSON encoded parameter values duration: time for SQL to execute passed in from toolbar just for redisplay - hash: the hash of (secret + sql + params) for tamper checking """ sql = forms.CharField() @@ -29,17 +23,6 @@ params = forms.CharField() alias = forms.CharField(required=False, initial="default") duration = forms.FloatField() - hash = forms.CharField() - - def __init__(self, *args, **kwargs): - initial = kwargs.get("initial") - if initial is not None: - initial["hash"] = self.make_hash(initial) - - super().__init__(*args, **kwargs) - - for name in self.fields: - self.fields[name].widget = forms.HiddenInput() def clean_raw_sql(self): value = self.cleaned_data["raw_sql"] @@ -65,23 +48,9 @@ return value - def clean_hash(self): - hash = self.cleaned_data["hash"] - - if not constant_time_compare(hash, self.make_hash(self.data)): - raise ValidationError("Tamper alert") - - return hash - def reformat_sql(self): return reformat_sql(self.cleaned_data["sql"], with_toggle=False) - def make_hash(self, data): - m = hmac.new(key=force_bytes(settings.SECRET_KEY), digestmod=hashlib.sha1) - for item in [data["sql"], data["params"]]: - m.update(force_bytes(item)) - return m.hexdigest() - @property def connection(self): return connections[self.cleaned_data["alias"]] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/debug_toolbar/panels/sql/panel.py new/django-debug-toolbar-3.2.1/debug_toolbar/panels/sql/panel.py --- old/django-debug-toolbar-3.2/debug_toolbar/panels/sql/panel.py 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/debug_toolbar/panels/sql/panel.py 2021-04-14 17:18:10.000000000 +0200 @@ -7,6 +7,7 @@ from django.urls import path from django.utils.translation import gettext_lazy as _, ngettext_lazy as __ +from debug_toolbar.forms import SignedDataForm from debug_toolbar.panels import Panel from debug_toolbar.panels.sql import views from debug_toolbar.panels.sql.forms import SQLSelectForm @@ -211,7 +212,9 @@ query["vendor"], query["trans_status"] ) - query["form"] = SQLSelectForm(auto_id=None, initial=copy(query)) + query["form"] = SignedDataForm( + auto_id=None, initial=SQLSelectForm(initial=copy(query)).initial + ) if query["sql"]: query["sql"] = reformat_sql(query["sql"], with_toggle=True) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/debug_toolbar/panels/sql/utils.py new/django-debug-toolbar-3.2.1/debug_toolbar/panels/sql/utils.py --- old/django-debug-toolbar-3.2/debug_toolbar/panels/sql/utils.py 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/debug_toolbar/panels/sql/utils.py 2021-04-14 17:18:10.000000000 +0200 @@ -4,6 +4,8 @@ from django.utils.html import escape from sqlparse import tokens as T +from debug_toolbar import settings as dt_settings + class BoldKeywordFilter: """sqlparse filter to bold SQL keywords""" @@ -31,7 +33,8 @@ def parse_sql(sql, aligned_indent=False): stack = sqlparse.engine.FilterStack() - stack.enable_grouping() + if dt_settings.get_config()["PRETTIFY_SQL"]: + stack.enable_grouping() if aligned_indent: stack.stmtprocess.append( sqlparse.filters.AlignedIndentFilter(char=" ", n="<br/>") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/debug_toolbar/panels/sql/views.py new/django-debug-toolbar-3.2.1/debug_toolbar/panels/sql/views.py --- old/django-debug-toolbar-3.2/debug_toolbar/panels/sql/views.py 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/debug_toolbar/panels/sql/views.py 2021-04-14 17:18:10.000000000 +0200 @@ -2,15 +2,16 @@ from django.template.loader import render_to_string from django.views.decorators.csrf import csrf_exempt -from debug_toolbar.decorators import require_show_toolbar +from debug_toolbar.decorators import require_show_toolbar, signed_data_view from debug_toolbar.panels.sql.forms import SQLSelectForm @csrf_exempt @require_show_toolbar -def sql_select(request): +@signed_data_view +def sql_select(request, verified_data): """Returns the output of the SQL SELECT statement""" - form = SQLSelectForm(request.POST or None) + form = SQLSelectForm(verified_data) if form.is_valid(): sql = form.cleaned_data["raw_sql"] @@ -34,9 +35,10 @@ @csrf_exempt @require_show_toolbar -def sql_explain(request): +@signed_data_view +def sql_explain(request, verified_data): """Returns the output of the SQL EXPLAIN on the given query""" - form = SQLSelectForm(request.POST or None) + form = SQLSelectForm(verified_data) if form.is_valid(): sql = form.cleaned_data["raw_sql"] @@ -69,9 +71,10 @@ @csrf_exempt @require_show_toolbar -def sql_profile(request): +@signed_data_view +def sql_profile(request, verified_data): """Returns the output of running the SQL and getting the profiling statistics""" - form = SQLSelectForm(request.POST or None) + form = SQLSelectForm(verified_data) if form.is_valid(): sql = form.cleaned_data["raw_sql"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/debug_toolbar/settings.py new/django-debug-toolbar-3.2.1/debug_toolbar/settings.py --- old/django-debug-toolbar-3.2/debug_toolbar/settings.py 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/debug_toolbar/settings.py 2021-04-14 17:18:10.000000000 +0200 @@ -37,6 +37,7 @@ "django.utils.deprecation", "django.utils.functional", ), + "PRETTIFY_SQL": True, "PROFILER_MAX_DEPTH": 10, "SHOW_TEMPLATE_CONTEXT": True, "SKIP_TEMPLATE_PREFIXES": ("django/forms/widgets/", "admin/widgets/"), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/debug_toolbar/static/debug_toolbar/css/toolbar.css new/django-debug-toolbar-3.2.1/debug_toolbar/static/debug_toolbar/css/toolbar.css --- old/django-debug-toolbar-3.2/debug_toolbar/static/debug_toolbar/css/toolbar.css 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/debug_toolbar/static/debug_toolbar/css/toolbar.css 2021-04-14 17:18:10.000000000 +0200 @@ -601,6 +601,9 @@ #djDebug .djdt-width-60 { width: 60%; } +#djDebug .djdt-max-height-100 { + max-height: 100%; +} #djDebug .djdt-highlighted { background-color: lightgrey; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/debug_toolbar/static/debug_toolbar/js/toolbar.js new/django-debug-toolbar-3.2.1/debug_toolbar/static/debug_toolbar/js/toolbar.js --- old/django-debug-toolbar-3.2/debug_toolbar/static/debug_toolbar/js/toolbar.js 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/debug_toolbar/static/debug_toolbar/js/toolbar.js 2021-04-14 17:18:10.000000000 +0200 @@ -44,6 +44,7 @@ inner.previousElementSibling.remove(); // Remove AJAX loader inner.innerHTML = data.content; $$.executeScripts(data.scripts); + $$.applyStyles(inner); }); } } @@ -270,6 +271,9 @@ options.path ? "; path=" + options.path : "", options.domain ? "; domain=" + options.domain : "", options.secure ? "; secure" : "", + "sameSite" in options + ? "; sameSite=" + options.samesite + : "; sameSite=Lax", ].join(""); return value; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/debug_toolbar/static/debug_toolbar/js/utils.js new/django-debug-toolbar-3.2.1/debug_toolbar/static/debug_toolbar/js/utils.js --- old/django-debug-toolbar-3.2/debug_toolbar/static/debug_toolbar/js/utils.js 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/debug_toolbar/static/debug_toolbar/js/utils.js 2021-04-14 17:18:10.000000000 +0200 @@ -32,6 +32,26 @@ document.head.appendChild(el); }); }, + applyStyles(container) { + /* + * Given a container element, apply styles set via data-djdt-styles attribute. + * The format is data-djdt-styles="styleName1:value;styleName2:value2" + * The style names should use the CSSStyleDeclaration camel cased names. + */ + container + .querySelectorAll("[data-djdt-styles]") + .forEach(function (element) { + const styles = element.dataset.djdtStyles || ""; + styles.split(";").forEach(function (styleText) { + const styleKeyPair = styleText.split(":"); + if (styleKeyPair.length === 2) { + const name = styleKeyPair[0].trim(); + const value = styleKeyPair[1].trim(); + element.style[name] = value; + } + }); + }); + }, }; function ajax(url, init) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/debug_toolbar/templates/debug_toolbar/panels/history.html new/django-debug-toolbar-3.2.1/debug_toolbar/templates/debug_toolbar/panels/history.html --- old/django-debug-toolbar-3.2/debug_toolbar/templates/debug_toolbar/panels/history.html 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/debug_toolbar/templates/debug_toolbar/panels/history.html 2021-04-14 17:18:10.000000000 +0200 @@ -3,7 +3,7 @@ {{ refresh_form }} <button class="refreshHistory">Refresh</button> </form> -<table style="max-height:100%;"> +<table class="djdt-max-height-100"> <thead> <tr> <th>{% trans "Time" %}</th> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/debug_toolbar/templates/debug_toolbar/panels/profiling.html new/django-debug-toolbar-3.2.1/debug_toolbar/templates/debug_toolbar/panels/profiling.html --- old/django-debug-toolbar-3.2/debug_toolbar/templates/debug_toolbar/panels/profiling.html 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/debug_toolbar/templates/debug_toolbar/panels/profiling.html 2021-04-14 17:18:10.000000000 +0200 @@ -14,7 +14,7 @@ {% for call in func_list %} <tr class="{% for parent_id in call.parent_ids %} djToggleDetails_{{ parent_id }}{% endfor %}" id="profilingMain_{{ call.id }}"> <td> - <div style="padding-left:{{ call.indent }}px"> + <div data-djdt-styles="paddingLeft:{{ call.indent }}px"> {% if call.has_subfuncs %} <button type="button" class="djProfileToggleDetails djToggleSwitch" data-toggle-name="profilingMain" data-toggle-id="{{ call.id }}">-</button> {% else %} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/debug_toolbar/templates/debug_toolbar/panels/sql.html new/django-debug-toolbar-3.2.1/debug_toolbar/templates/debug_toolbar/panels/sql.html --- old/django-debug-toolbar-3.2/debug_toolbar/templates/debug_toolbar/panels/sql.html 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/debug_toolbar/templates/debug_toolbar/panels/sql.html 2021-04-14 17:18:10.000000000 +0200 @@ -2,7 +2,7 @@ <ul> {% for alias, info in databases %} <li> - <strong><span class="djdt-color" style="background-color:rgb({{ info.rgb_color|join:', ' }})"></span> {{ alias }}</strong> + <strong><span class="djdt-color" data-djdt-styles="backgroundColor:rgb({{ info.rgb_color|join:', ' }})"></span> {{ alias }}</strong> {{ info.time_spent|floatformat:"2" }} ms ({% blocktrans count info.num_queries as num %}{{ num }} query{% plural %}{{ num }} queries{% endblocktrans %} {% if info.similar_count %} {% blocktrans with count=info.similar_count trimmed %} @@ -40,7 +40,7 @@ <tbody> {% for query in queries %} <tr class="{% if query.is_slow %} djDebugRowWarning{% endif %}" id="sqlMain_{{ forloop.counter }}"> - <td><span class="djdt-color" style="background-color:rgb({{ query.rgb_color|join:', '}})"></span></td> + <td><span class="djdt-color" data-djdt-styles="backgroundColor:rgb({{ query.rgb_color|join:', '}})"></span></td> <td class="djdt-toggle"> <button type="button" class="djToggleSwitch" data-toggle-name="sqlMain" data-toggle-id="{{ forloop.counter }}">+</button> </td> @@ -48,13 +48,13 @@ <div class="djDebugSql">{{ query.sql|safe }}</div> {% if query.similar_count %} <strong> - <span class="djdt-color" style="background-color:{{ query.similar_color }}"></span> + <span class="djdt-color" data-djdt-styles="backgroundColor:{{ query.similar_color }}"></span> {% blocktrans with count=query.similar_count %}{{ count }} similar queries.{% endblocktrans %} </strong> {% endif %} {% if query.duplicate_count %} <strong> - <span class="djdt-color" style="background-color:{{ query.duplicate_color }}"></span> + <span class="djdt-color" data-djdt-styles="backgroundColor:{{ query.duplicate_color }}"></span> {% blocktrans with dupes=query.duplicate_count %}Duplicated {{ dupes }} times.{% endblocktrans %} </strong> {% endif %} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/debug_toolbar/toolbar.py new/django-debug-toolbar-3.2.1/debug_toolbar/toolbar.py --- old/django-debug-toolbar-3.2/debug_toolbar/toolbar.py 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/debug_toolbar/toolbar.py 2021-04-14 17:18:10.000000000 +0200 @@ -9,7 +9,8 @@ from django.core.exceptions import ImproperlyConfigured from django.template import TemplateSyntaxError from django.template.loader import render_to_string -from django.urls import path +from django.urls import path, resolve +from django.urls.exceptions import Resolver404 from django.utils.module_loading import import_string from debug_toolbar import settings as dt_settings @@ -133,6 +134,19 @@ cls._urlpatterns = urlpatterns return cls._urlpatterns + @classmethod + def is_toolbar_request(cls, request): + """ + Determine if the request is for a DebugToolbar view. + """ + # The primary caller of this function is in the middleware which may + # not have resolver_match set. + try: + resolver_match = request.resolver_match or resolve(request.path) + except Resolver404: + return False + return resolver_match.namespaces and resolver_match.namespaces[-1] == app_name + app_name = "djdt" urlpatterns = DebugToolbar.get_urls() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/docs/changes.rst new/django-debug-toolbar-3.2.1/docs/changes.rst --- old/django-debug-toolbar-3.2/docs/changes.rst 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/docs/changes.rst 2021-04-14 17:18:10.000000000 +0200 @@ -1,6 +1,24 @@ Change log ========== +Next version +------------ + + +3.2.1 (2021-04-14) +------------------ + +* Fixed SQL Injection vulnerability, CVE-2021-30459. The toolbar now + calculates a signature on all fields for the SQL select, explain, + and analyze forms. +* Changed ``djdt.cookie.set()`` to set ``sameSite=Lax`` by default if + callers do not provide a value. +* Added ``PRETTIFY_SQL`` configuration option to support controlling + SQL token grouping. By default it's set to True. When set to False, + a performance improvement can be seen by the SQL panel. +* Fixed issue with toolbar expecting URL paths to start with `/__debug__/` + while the documentation indicates it's not required. + 3.2 (2020-12-03) ---------------- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/docs/conf.py new/django-debug-toolbar-3.2.1/docs/conf.py --- old/django-debug-toolbar-3.2/docs/conf.py 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/docs/conf.py 2021-04-14 17:18:10.000000000 +0200 @@ -25,7 +25,7 @@ copyright = copyright.format(datetime.date.today().year) # The full version, including alpha/beta/rc tags -release = "3.2" +release = "3.2.1" # -- General configuration --------------------------------------------------- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/docs/configuration.rst new/django-debug-toolbar-3.2.1/docs/configuration.rst --- old/django-debug-toolbar-3.2/docs/configuration.rst 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/docs/configuration.rst 2021-04-14 17:18:10.000000000 +0200 @@ -180,6 +180,39 @@ Useful for eliminating server-related entries which can result in enormous DOM structures and toolbar rendering delays. +* ``PRETTIFY_SQL`` + + Default: ``True`` + + Panel: SQL + + Controls SQL token grouping. + + Token grouping allows pretty print of similar tokens, + like aligned indentation for every selected field. + + When set to ``True``, it might cause render slowdowns + when a view make long SQL textual queries. + + **Without grouping**:: + + SELECT "auth_user"."id", "auth_user"."password", "auth_user"."last_login", "auth_user"."is_superuser", "auth_user"."username", "auth_user"."first_name", "auth_user"."last_name" + FROM "auth_user" + WHERE "auth_user"."username" = '''test_username''' + LIMIT 21 + + **With grouping**:: + + SELECT "auth_user"."id", + "auth_user"."password", + "auth_user"."last_login", + "auth_user"."is_superuser", + "auth_user"."username", + "auth_user"."first_name", + "auth_user"."last_name", + FROM "auth_user" + WHERE "auth_user"."username" = '''test_username''' + LIMIT 21 * ``PROFILER_MAX_DEPTH`` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/docs/contributing.rst new/django-debug-toolbar-3.2.1/docs/contributing.rst --- old/django-debug-toolbar-3.2/docs/contributing.rst 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/docs/contributing.rst 2021-04-14 17:18:10.000000000 +0200 @@ -53,7 +53,7 @@ Once you've set up a development environment as explained above, you can run the test suite for the versions of Django and Python installed in that -environment:: +environment using the SQLite database:: $ make test @@ -79,8 +79,23 @@ $ DJANGO_SELENIUM_TESTS=true make coverage $ DJANGO_SELENIUM_TESTS=true tox -At this time, there isn't an easy way to test against databases other than -SQLite. +To test via `tox` against other databases, you'll need to create the user, +database and assign the proper permissions. For PostgreSQL in a `psql` +shell (note this allows the debug_toolbar user the permission to create +databases):: + + psql> CREATE USER debug_toolbar WITH PASSWORD 'debug_toolbar'; + psql> ALTER USER debug_toolbar CREATEDB; + psql> CREATE DATABASE debug_toolbar; + psql> GRANT ALL PRIVILEGES ON DATABASE debug_toolbar to debug_toolbar; + +For MySQL/MariaDB in a `mysql` shell:: + + mysql> CREATE DATABASE debug_toolbar; + mysql> CREATE USER 'debug_toolbar'@'localhost' IDENTIFIED BY 'debug_toolbar'; + mysql> GRANT ALL PRIVILEGES ON debug_toolbar.* TO 'debug_toolbar'@'localhost'; + mysql> GRANT ALL PRIVILEGES ON test_debug_toolbar.* TO 'debug_toolbar'@'localhost'; + Style ----- @@ -141,8 +156,8 @@ Commit. #. Bump version numbers in ``docs/changes.rst``, ``docs/conf.py``, - ``README.rst`` and ``setup.py``. Add the release date to - ``docs/changes.rst``. Commit. + ``README.rst``, ``debug_toolbar/__init__.py`` and ``setup.py``. + Add the release date to ``docs/changes.rst``. Commit. #. Tag the new version. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/docs/installation.rst new/django-debug-toolbar-3.2.1/docs/installation.rst --- old/django-debug-toolbar-3.2/docs/installation.rst 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/docs/installation.rst 2021-04-14 17:18:10.000000000 +0200 @@ -83,8 +83,8 @@ ------------------------ The Debug Toolbar is shown only if your IP address is listed in the -:django:setting:`INTERNAL_IPS` setting. This means that for local -development, you *must* add ``'127.0.0.1'`` to :django:setting:`INTERNAL_IPS`; +:setting:`INTERNAL_IPS` setting. This means that for local +development, you *must* add ``'127.0.0.1'`` to :setting:`INTERNAL_IPS`; you'll need to create this setting if it doesn't already exist in your settings module:: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/docs/panels.rst new/django-debug-toolbar-3.2.1/docs/panels.rst --- old/django-debug-toolbar-3.2/docs/panels.rst 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/docs/panels.rst 2021-04-14 17:18:10.000000000 +0200 @@ -177,6 +177,18 @@ Inspector panel also logs to the console by default, but may be instructed not to. +LDAP Tracing +~~~~~~~~~~~~ + +URL: https://github.com/danyi1212/django-windowsauth + +Path: ``windows_auth.panels.LDAPPanel`` + +LDAP Operations performed during the request, including timing, request and response messages, +the entries received, write changes list, stack-tracing and error debugging. +This panel also shows connection usage metrics when it is collected. +`Check out the docs <https://django-windowsauth.readthedocs.io/en/latest/howto/debug_toolbar.html>`_. + Line Profiler ~~~~~~~~~~~~~ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/example/settings.py new/django-debug-toolbar-3.2.1/example/settings.py --- old/django-debug-toolbar-3.2/example/settings.py 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/example/settings.py 2021-04-14 17:18:10.000000000 +0200 @@ -73,26 +73,23 @@ # To use another database, set the DB_BACKEND environment variable. if os.environ.get("DB_BACKEND", "").lower() == "postgresql": - # % su postgres - # % createuser debug_toolbar - # % createdb debug_toolbar -O debug_toolbar + # See docs/contributing for instructions on configuring PostgreSQL. DATABASES = { "default": { "ENGINE": "django.db.backends.postgresql", "NAME": "debug_toolbar", "USER": "debug_toolbar", + "PASSWORD": "debug_toolbar", } } if os.environ.get("DB_BACKEND", "").lower() == "mysql": - # % mysql - # mysql> CREATE DATABASE debug_toolbar; - # mysql> CREATE USER 'debug_toolbar'@'localhost'; - # mysql> GRANT ALL PRIVILEGES ON debug_toolbar.* TO 'debug_toolbar'@'localhost'; + # See docs/contributing for instructions on configuring MySQL/MariaDB. DATABASES = { "default": { "ENGINE": "django.db.backends.mysql", "NAME": "debug_toolbar", "USER": "debug_toolbar", + "PASSWORD": "debug_toolbar", } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/setup.cfg new/django-debug-toolbar-3.2.1/setup.cfg --- old/django-debug-toolbar-3.2/setup.cfg 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/setup.cfg 2021-04-14 17:18:10.000000000 +0200 @@ -1,6 +1,6 @@ [metadata] name = django-debug-toolbar -version = 3.2 +version = 3.2.1 description = A configurable set of panels that display various debug information about the current request/response. long_description = file: README.rst author = Rob Hudson diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/tests/panels/test_history.py new/django-debug-toolbar-3.2.1/tests/panels/test_history.py --- old/django-debug-toolbar-3.2/tests/panels/test_history.py 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/tests/panels/test_history.py 2021-04-14 17:18:10.000000000 +0200 @@ -1,9 +1,7 @@ -from unittest.mock import patch - from django.test import RequestFactory, override_settings from django.urls import resolve, reverse -from debug_toolbar.panels.history.forms import HistoryStoreForm +from debug_toolbar.forms import SignedDataForm from debug_toolbar.toolbar import DebugToolbar from ..base import BaseTestCase, IntegrationTestCase @@ -83,33 +81,15 @@ response = self.client.get(reverse("djdt:history_sidebar")) self.assertEqual(response.status_code, 400) - data = { - "store_id": "foo", - "hash": "invalid", - } + data = {"signed": SignedDataForm.sign({"store_id": "foo"}) + "invalid"} response = self.client.get(reverse("djdt:history_sidebar"), data=data) self.assertEqual(response.status_code, 400) - @patch("debug_toolbar.panels.history.views.DebugToolbar.fetch") - def test_history_sidebar_hash(self, fetch): - """Validate the hashing mechanism.""" - fetch.return_value.panels = [] - data = { - "store_id": "foo", - "hash": "3280d66a3cca10098a44907c5a1fd255265eed31", - } - response = self.client.get(reverse("djdt:history_sidebar"), data=data) - self.assertEqual(response.status_code, 200) - self.assertEqual(response.json(), {}) - def test_history_sidebar(self): """Validate the history sidebar view.""" self.client.get("/json_view/") store_id = list(DebugToolbar._store.keys())[0] - data = { - "store_id": store_id, - "hash": HistoryStoreForm.make_hash({"store_id": store_id}), - } + data = {"signed": SignedDataForm.sign({"store_id": store_id})} response = self.client.get(reverse("djdt:history_sidebar"), data=data) self.assertEqual(response.status_code, 200) self.assertEqual( @@ -130,25 +110,20 @@ }, ) - def test_history_refresh_invalid(self): + def test_history_refresh_invalid_signature(self): response = self.client.get(reverse("djdt:history_refresh")) self.assertEqual(response.status_code, 400) - data = { - "store_id": "foo", - "hash": "invalid", - } + data = {"signed": "eyJzdG9yZV9pZCI6ImZvbyIsImhhc2giOiI4YWFiMzIzZGZhODIyMW"} response = self.client.get(reverse("djdt:history_refresh"), data=data) self.assertEqual(response.status_code, 400) + self.assertEqual(b"Invalid signature", response.content) def test_history_refresh(self): """Verify refresh history response has request variables.""" data = {"foo": "bar"} self.client.get("/json_view/", data, content_type="application/json") - data = { - "store_id": "foo", - "hash": "3280d66a3cca10098a44907c5a1fd255265eed31", - } + data = {"signed": SignedDataForm.sign({"store_id": "foo"})} response = self.client.get(reverse("djdt:history_refresh"), data=data) self.assertEqual(response.status_code, 200) data = response.json() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/tests/panels/test_request.py new/django-debug-toolbar-3.2.1/tests/panels/test_request.py --- old/django-debug-toolbar-3.2/tests/panels/test_request.py 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/tests/panels/test_request.py 2021-04-14 17:18:10.000000000 +0200 @@ -84,3 +84,10 @@ content = self.panel.content self.assertIn("foo", content) self.assertIn("bar", content) + + def test_namespaced_url(self): + self.request.path = "/admin/login/" + response = self.panel.process_request(self.request) + self.panel.generate_stats(self.request, response) + panel_stats = self.panel.get_stats() + self.assertEqual(panel_stats["view_urlname"], "admin:login") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/tests/panels/test_sql.py new/django-debug-toolbar-3.2.1/tests/panels/test_sql.py --- old/django-debug-toolbar-3.2/tests/panels/test_sql.py 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/tests/panels/test_sql.py 2021-04-14 17:18:10.000000000 +0200 @@ -9,6 +9,8 @@ from django.shortcuts import render from django.test.utils import override_settings +from debug_toolbar import settings as dt_settings + from ..base import BaseTestCase try: @@ -357,3 +359,40 @@ # ensure the stacktrace is populated self.assertTrue(len(query[1]["stacktrace"]) > 0) + + @override_settings( + DEBUG_TOOLBAR_CONFIG={"PRETTIFY_SQL": True}, + ) + def test_prettify_sql(self): + """ + Test case to validate that the PRETTIFY_SQL setting changes the output + of the sql when it's toggled. It does not validate what it does + though. + """ + list(User.objects.filter(username__istartswith="spam")) + + response = self.panel.process_request(self.request) + self.panel.generate_stats(self.request, response) + pretty_sql = self.panel._queries[-1][1]["sql"] + self.assertEqual(len(self.panel._queries), 1) + + # Reset the queries + self.panel._queries = [] + # Run it again, but with prettyify off. Verify that it's different. + dt_settings.get_config()["PRETTIFY_SQL"] = False + list(User.objects.filter(username__istartswith="spam")) + response = self.panel.process_request(self.request) + self.panel.generate_stats(self.request, response) + self.assertEqual(len(self.panel._queries), 1) + self.assertNotEqual(pretty_sql, self.panel._queries[-1][1]["sql"]) + + self.panel._queries = [] + # Run it again, but with prettyify back on. + # This is so we don't have to check what PRETTIFY_SQL does exactly, + # but we know it's doing something. + dt_settings.get_config()["PRETTIFY_SQL"] = True + list(User.objects.filter(username__istartswith="spam")) + response = self.panel.process_request(self.request) + self.panel.generate_stats(self.request, response) + self.assertEqual(len(self.panel._queries), 1) + self.assertEqual(pretty_sql, self.panel._queries[-1][1]["sql"]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/tests/panels/test_staticfiles.py new/django-debug-toolbar-3.2.1/tests/panels/test_staticfiles.py --- old/django-debug-toolbar-3.2/tests/panels/test_staticfiles.py 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/tests/panels/test_staticfiles.py 2021-04-14 17:18:10.000000000 +0200 @@ -1,13 +1,11 @@ import os +import unittest +import django from django.conf import settings from django.contrib.staticfiles import finders -from django.core.checks import Warning -from django.test import SimpleTestCase from django.test.utils import override_settings -from debug_toolbar.panels.staticfiles import StaticFilesPanel - from ..base import BaseTestCase PATH_DOES_NOT_EXIST = os.path.join(settings.BASE_DIR, "tests", "invalid_static") @@ -54,6 +52,7 @@ ) self.assertValidHTML(content) + @unittest.skipIf(django.VERSION >= (4,), "Django>=4 handles missing dirs itself.") @override_settings( STATICFILES_DIRS=[PATH_DOES_NOT_EXIST] + settings.STATICFILES_DIRS, STATIC_ROOT=PATH_DOES_NOT_EXIST, @@ -81,20 +80,3 @@ self.assertEqual( self.panel.get_staticfiles_dirs(), finders.FileSystemFinder().locations ) - - -@override_settings(DEBUG=True) -class StaticFilesPanelChecksTestCase(SimpleTestCase): - @override_settings(STATICFILES_DIRS=[PATH_DOES_NOT_EXIST]) - def test_run_checks(self): - messages = StaticFilesPanel.run_checks() - self.assertEqual( - messages, - [ - Warning( - "debug_toolbar requires the STATICFILES_DIRS directories to exist.", - hint="Running manage.py collectstatic may help uncover the issue.", - id="debug_toolbar.staticfiles.W001", - ) - ], - ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/tests/settings.py new/django-debug-toolbar-3.2.1/tests/settings.py --- old/django-debug-toolbar-3.2/tests/settings.py 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/tests/settings.py 2021-04-14 17:18:10.000000000 +0200 @@ -81,8 +81,8 @@ DATABASES = { "default": { - "ENGINE": "django.db.backends.%s" % os.getenv("DB_BACKEND"), - "NAME": os.getenv("DB_NAME"), + "ENGINE": "django.db.backends.%s" % os.getenv("DB_BACKEND", "sqlite3"), + "NAME": os.getenv("DB_NAME", ":memory:"), "USER": os.getenv("DB_USER"), "PASSWORD": os.getenv("DB_PASSWORD"), "HOST": os.getenv("DB_HOST", ""), @@ -93,6 +93,8 @@ }, } +DEFAULT_AUTO_FIELD = "django.db.models.AutoField" + # Debug Toolbar configuration DEBUG_TOOLBAR_CONFIG = { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/tests/test_checks.py new/django-debug-toolbar-3.2.1/tests/test_checks.py --- old/django-debug-toolbar-3.2/tests/test_checks.py 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/tests/test_checks.py 2021-04-14 17:18:10.000000000 +0200 @@ -1,5 +1,7 @@ import os +import unittest +import django from django.conf import settings from django.core.checks import Warning, run_checks from django.test import SimpleTestCase, override_settings @@ -89,6 +91,7 @@ messages, ) + @unittest.skipIf(django.VERSION >= (4,), "Django>=4 handles missing dirs itself.") @override_settings( STATICFILES_DIRS=[PATH_DOES_NOT_EXIST], ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/tests/test_forms.py new/django-debug-toolbar-3.2.1/tests/test_forms.py --- old/django-debug-toolbar-3.2/tests/test_forms.py 1970-01-01 01:00:00.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/tests/test_forms.py 2021-04-14 17:18:10.000000000 +0200 @@ -0,0 +1,56 @@ +from datetime import datetime + +import django +from django import forms +from django.test import TestCase + +from debug_toolbar.forms import SignedDataForm + +# Django 3.1 uses sha256 by default. +SIGNATURE = ( + "v02QBcJplEET6QXHNWejnRcmSENWlw6_RjxLTR7QG9g" + if django.VERSION >= (3, 1) + else "ukcAFUqYhUUnqT-LupnYoo-KvFg" +) + +DATA = {"value": "foo", "date": datetime(2020, 1, 1)} +SIGNED_DATA = f'{{"date": "2020-01-01 00:00:00", "value": "foo"}}:{SIGNATURE}' + + +class FooForm(forms.Form): + value = forms.CharField() + # Include a datetime in the tests because it's not serializable back + # to a datetime by SignedDataForm + date = forms.DateTimeField() + + +class TestSignedDataForm(TestCase): + def test_signed_data(self): + data = {"signed": SignedDataForm.sign(DATA)} + form = SignedDataForm(data=data) + self.assertTrue(form.is_valid()) + # Check the signature value + self.assertEqual(data["signed"], SIGNED_DATA) + + def test_verified_data(self): + form = SignedDataForm(data={"signed": SignedDataForm.sign(DATA)}) + self.assertEqual( + form.verified_data(), + { + "value": "foo", + "date": "2020-01-01 00:00:00", + }, + ) + # Take it back to the foo form to validate the datetime is serialized + foo_form = FooForm(data=form.verified_data()) + self.assertTrue(foo_form.is_valid()) + self.assertDictEqual(foo_form.cleaned_data, DATA) + + def test_initial_set_signed(self): + form = SignedDataForm(initial=DATA) + self.assertEqual(form.initial["signed"], SIGNED_DATA) + + def test_prevents_tampering(self): + data = {"signed": SIGNED_DATA.replace('"value": "foo"', '"value": "bar"')} + form = SignedDataForm(data=data) + self.assertFalse(form.is_valid()) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/tests/test_integration.py new/django-debug-toolbar-3.2.1/tests/test_integration.py --- old/django-debug-toolbar-3.2/tests/test_integration.py 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/tests/test_integration.py 2021-04-14 17:18:10.000000000 +0200 @@ -12,6 +12,7 @@ from django.test import RequestFactory from django.test.utils import override_settings +from debug_toolbar.forms import SignedDataForm from debug_toolbar.middleware import DebugToolbarMiddleware, show_toolbar from debug_toolbar.panels import Panel from debug_toolbar.toolbar import DebugToolbar @@ -101,6 +102,25 @@ self.client.get("/cached_view/") self.assertEqual(len(self.toolbar.get_panel_by_id("CachePanel").calls), 5) + def test_is_toolbar_request(self): + self.request.path = "/__debug__/render_panel/" + self.assertTrue(self.toolbar.is_toolbar_request(self.request)) + + self.request.path = "/invalid/__debug__/render_panel/" + self.assertFalse(self.toolbar.is_toolbar_request(self.request)) + + self.request.path = "/render_panel/" + self.assertFalse(self.toolbar.is_toolbar_request(self.request)) + + @override_settings(ROOT_URLCONF="tests.urls_invalid") + def test_is_toolbar_request_without_djdt_urls(self): + """Test cases when the toolbar urls aren't configured.""" + self.request.path = "/__debug__/render_panel/" + self.assertFalse(self.toolbar.is_toolbar_request(self.request)) + + self.request.path = "/render_panel/" + self.assertFalse(self.toolbar.is_toolbar_request(self.request)) + @override_settings(DEBUG=True) class DebugToolbarIntegrationTestCase(IntegrationTestCase): @@ -193,12 +213,15 @@ def test_sql_select_checks_show_toolbar(self): url = "/__debug__/sql_select/" data = { - "sql": "SELECT * FROM auth_user", - "raw_sql": "SELECT * FROM auth_user", - "params": "{}", - "alias": "default", - "duration": "0", - "hash": "6e12daa636b8c9a8be993307135458f90a877606", + "signed": SignedDataForm.sign( + { + "sql": "SELECT * FROM auth_user", + "raw_sql": "SELECT * FROM auth_user", + "params": "{}", + "alias": "default", + "duration": "0", + } + ) } response = self.client.post(url, data) @@ -216,12 +239,15 @@ def test_sql_explain_checks_show_toolbar(self): url = "/__debug__/sql_explain/" data = { - "sql": "SELECT * FROM auth_user", - "raw_sql": "SELECT * FROM auth_user", - "params": "{}", - "alias": "default", - "duration": "0", - "hash": "6e12daa636b8c9a8be993307135458f90a877606", + "signed": SignedDataForm.sign( + { + "sql": "SELECT * FROM auth_user", + "raw_sql": "SELECT * FROM auth_user", + "params": "{}", + "alias": "default", + "duration": "0", + } + ) } response = self.client.post(url, data) @@ -246,12 +272,15 @@ ) query = base_query + """ '{"foo": "bar"}'""" data = { - "sql": query, - "raw_sql": base_query + " %s", - "params": '["{\\"foo\\": \\"bar\\"}"]', - "alias": "default", - "duration": "0", - "hash": "2b7172eb2ac8e2a8d6f742f8a28342046e0d00ba", + "signed": SignedDataForm.sign( + { + "sql": query, + "raw_sql": base_query + " %s", + "params": '["{\\"foo\\": \\"bar\\"}"]', + "alias": "default", + "duration": "0", + } + ) } response = self.client.post(url, data) self.assertEqual(response.status_code, 200) @@ -268,12 +297,15 @@ def test_sql_profile_checks_show_toolbar(self): url = "/__debug__/sql_profile/" data = { - "sql": "SELECT * FROM auth_user", - "raw_sql": "SELECT * FROM auth_user", - "params": "{}", - "alias": "default", - "duration": "0", - "hash": "6e12daa636b8c9a8be993307135458f90a877606", + "signed": SignedDataForm.sign( + { + "sql": "SELECT * FROM auth_user", + "raw_sql": "SELECT * FROM auth_user", + "params": "{}", + "alias": "default", + "duration": "0", + } + ) } response = self.client.post(url, data) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/tests/urls.py new/django-debug-toolbar-3.2.1/tests/urls.py --- old/django-debug-toolbar-3.2/tests/urls.py 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/tests/urls.py 2021-04-14 17:18:10.000000000 +0200 @@ -1,3 +1,4 @@ +from django.contrib import admin from django.contrib.auth.views import LoginView from django.urls import include, path, re_path @@ -22,5 +23,6 @@ path("json_view/", views.json_view), path("redirect/", views.redirect_view), path("login_without_redirect/", LoginView.as_view(redirect_field_name=None)), + path("admin/", admin.site.urls), path("__debug__/", include(debug_toolbar.urls)), ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/tests/urls_invalid.py new/django-debug-toolbar-3.2.1/tests/urls_invalid.py --- old/django-debug-toolbar-3.2/tests/urls_invalid.py 1970-01-01 01:00:00.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/tests/urls_invalid.py 2021-04-14 17:18:10.000000000 +0200 @@ -0,0 +1,2 @@ +"""Invalid urls.py file for testing""" +urlpatterns = [] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-debug-toolbar-3.2/tox.ini new/django-debug-toolbar-3.2.1/tox.ini --- old/django-debug-toolbar-3.2/tox.ini 2020-12-03 09:11:15.000000000 +0100 +++ new/django-debug-toolbar-3.2.1/tox.ini 2021-04-14 17:18:10.000000000 +0200 @@ -3,20 +3,20 @@ docs style readme - py{36,37,38,39}-dj22-sqlite - py{36,37,38,39}-dj{30,31}-sqlite - py{36,37,38,39}-djmaster-sqlite - py{37,38,39}-dj{22,30,31}-{postgresql,mysql} + py{36,37}-dj{22,30,31,32}-sqlite + py{38,39}-dj{22,30,31,32,main}-sqlite + py{36,37,38,39}-dj{22,30,31,32}-{postgresql,mysql} [testenv] deps = dj22: Django==2.2.* dj30: Django==3.0.* dj31: Django==3.1.* + dj32: Django>=3.2a1,<4.0 sqlite: mock postgresql: psycopg2-binary mysql: mysqlclient - djmaster: https://github.com/django/django/archive/master.tar.gz + djmain: https://github.com/django/django/archive/main.tar.gz coverage Jinja2 html5lib @@ -35,10 +35,32 @@ PYTHONPATH = {toxinidir} PYTHONWARNINGS = d py38-dj31-postgresql: DJANGO_SELENIUM_TESTS = true + DB_NAME = {env:DB_NAME:debug_toolbar} + DB_USER = {env:DB_USER:debug_toolbar} + DB_HOST = {env:DB_HOST:localhost} + DB_PASSWORD = {env:DB_PASSWORD:debug_toolbar} whitelist_externals = make pip_pre = True commands = make coverage TEST_ARGS='{posargs:tests}' +[testenv:py{36,37,38,39}-dj{22,30,31,32}-postgresql] +setenv = + {[testenv]setenv} + DB_BACKEND = postgresql + DB_PORT = {env:DB_PORT:5432} + +[testenv:py{36,37,38,39}-dj{22,30,31,32}-mysql] +setenv = + {[testenv]setenv} + DB_BACKEND = mysql + DB_PORT = {env:DB_PORT:3306} + +[testenv:py{36,37,38,39}-dj{22,30,31,32,main}-sqlite] +setenv = + {[testenv]setenv} + DB_BACKEND = sqlite3 + DB_NAME = ":memory:" + [testenv:docs] commands = make -C {toxinidir}/docs spelling deps =