Skip to content

Commit 5e6750b

Browse files
Merge pull request from GHSA-pghf-347x-c2gj
* Changes required to support v2.2.x branch Style corrections for tox. Remove djmaster from tox requirements. Switch to Github actions and latest toolbar tox/settings approach. Skip tests that are invalid for old versions. * Fix CVE-2021-30459 by creating signature from all data fields. Backport of 1c6ba3c1302bf545f8356dcd26255ab7db1ec408 Create a signature based on all fields in the form and attach to validate that the data being sent back is what the server generated initially. Change the hashing algorithm to SHA256 Force the values to a string for signing. Remove hashing mechanism from forms. Support sha1 algorithm for django < 3.1 * Bump to version 2.2.1
1 parent e154955 commit 5e6750b

22 files changed

+679
-123
lines changed

.github/workflows/test.yml

+196
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
name: Test
2+
3+
on: [push, pull_request]
4+
5+
jobs:
6+
mysql:
7+
runs-on: ubuntu-latest
8+
strategy:
9+
fail-fast: false
10+
max-parallel: 5
11+
matrix:
12+
python-version: ['3.5', '3.6', '3.7', '3.8']
13+
14+
services:
15+
mariadb:
16+
image: mariadb:10.3
17+
env:
18+
MYSQL_ROOT_PASSWORD: debug_toolbar
19+
options: >-
20+
--health-cmd "mysqladmin ping"
21+
--health-interval 10s
22+
--health-timeout 5s
23+
--health-retries 5
24+
ports:
25+
- 3306:3306
26+
27+
steps:
28+
- uses: actions/checkout@v2
29+
30+
- name: Set up Python ${{ matrix.python-version }}
31+
uses: actions/setup-python@v2
32+
with:
33+
python-version: ${{ matrix.python-version }}
34+
35+
- name: Get pip cache dir
36+
id: pip-cache
37+
run: |
38+
echo "::set-output name=dir::$(pip cache dir)"
39+
- name: Cache
40+
uses: actions/cache@v2
41+
with:
42+
path: ${{ steps.pip-cache.outputs.dir }}
43+
key:
44+
${{ matrix.python-version }}-v1-${{ hashFiles('**/setup.cfg') }}-${{ hashFiles('**/tox.ini') }}
45+
restore-keys: |
46+
${{ matrix.python-version }}-v1-
47+
- name: Install dependencies
48+
run: |
49+
python -m pip install --upgrade pip
50+
python -m pip install --upgrade tox tox-gh-actions
51+
- name: Test with tox
52+
run: tox
53+
env:
54+
DB_BACKEND: mysql
55+
DB_USER: root
56+
DB_PASSWORD: debug_toolbar
57+
DB_HOST: 127.0.0.1
58+
DB_PORT: 3306
59+
60+
- name: Upload coverage
61+
uses: codecov/codecov-action@v1
62+
with:
63+
name: Python ${{ matrix.python-version }}
64+
65+
postgres:
66+
runs-on: ubuntu-latest
67+
strategy:
68+
fail-fast: false
69+
max-parallel: 5
70+
matrix:
71+
python-version: ['3.5', '3.6', '3.7', '3.8']
72+
73+
services:
74+
postgres:
75+
image: 'postgres:9.5'
76+
env:
77+
POSTGRES_DB: debug_toolbar
78+
POSTGRES_USER: debug_toolbar
79+
POSTGRES_PASSWORD: debug_toolbar
80+
ports:
81+
- 5432:5432
82+
options: >-
83+
--health-cmd pg_isready
84+
--health-interval 10s
85+
--health-timeout 5s
86+
--health-retries 5
87+
steps:
88+
- uses: actions/checkout@v2
89+
90+
- name: Set up Python ${{ matrix.python-version }}
91+
uses: actions/setup-python@v2
92+
with:
93+
python-version: ${{ matrix.python-version }}
94+
95+
- name: Get pip cache dir
96+
id: pip-cache
97+
run: |
98+
echo "::set-output name=dir::$(pip cache dir)"
99+
- name: Cache
100+
uses: actions/cache@v2
101+
with:
102+
path: ${{ steps.pip-cache.outputs.dir }}
103+
key:
104+
${{ matrix.python-version }}-v1-${{ hashFiles('**/setup.cfg') }}-${{ hashFiles('**/tox.ini') }}
105+
restore-keys: |
106+
${{ matrix.python-version }}-v1-
107+
- name: Install dependencies
108+
run: |
109+
python -m pip install --upgrade pip
110+
python -m pip install --upgrade tox tox-gh-actions
111+
- name: Test with tox
112+
run: tox
113+
env:
114+
DB_BACKEND: postgresql
115+
DB_HOST: localhost
116+
DB_PORT: 5432
117+
118+
- name: Upload coverage
119+
uses: codecov/codecov-action@v1
120+
with:
121+
name: Python ${{ matrix.python-version }}
122+
123+
sqlite:
124+
runs-on: ubuntu-latest
125+
strategy:
126+
fail-fast: false
127+
max-parallel: 5
128+
matrix:
129+
python-version: ['3.5', '3.6', '3.7', '3.8']
130+
131+
steps:
132+
- uses: actions/checkout@v2
133+
134+
- name: Set up Python ${{ matrix.python-version }}
135+
uses: actions/setup-python@v2
136+
with:
137+
python-version: ${{ matrix.python-version }}
138+
139+
- name: Get pip cache dir
140+
id: pip-cache
141+
run: |
142+
echo "::set-output name=dir::$(pip cache dir)"
143+
- name: Cache
144+
uses: actions/cache@v2
145+
with:
146+
path: ${{ steps.pip-cache.outputs.dir }}
147+
key:
148+
${{ matrix.python-version }}-v1-${{ hashFiles('**/setup.cfg') }}-${{ hashFiles('**/tox.ini') }}
149+
restore-keys: |
150+
${{ matrix.python-version }}-v1-
151+
- name: Install dependencies
152+
run: |
153+
python -m pip install --upgrade pip
154+
python -m pip install --upgrade tox tox-gh-actions
155+
- name: Test with tox
156+
run: tox
157+
env:
158+
DB_BACKEND: sqlite3
159+
DB_NAME: ":memory:"
160+
161+
- name: Upload coverage
162+
uses: codecov/codecov-action@v1
163+
with:
164+
name: Python ${{ matrix.python-version }}
165+
166+
lint:
167+
runs-on: ubuntu-latest
168+
strategy:
169+
fail-fast: false
170+
171+
steps:
172+
- uses: actions/checkout@v2
173+
174+
- name: Set up Python ${{ matrix.python-version }}
175+
uses: actions/setup-python@v2
176+
with:
177+
python-version: 3.7
178+
179+
- name: Get pip cache dir
180+
id: pip-cache
181+
run: |
182+
echo "::set-output name=dir::$(pip cache dir)"
183+
- name: Cache
184+
uses: actions/cache@v2
185+
with:
186+
path: ${{ steps.pip-cache.outputs.dir }}
187+
key:
188+
${{ matrix.python-version }}-v1-${{ hashFiles('**/setup.cfg') }}-${{ hashFiles('**/tox.ini') }}
189+
restore-keys: |
190+
${{ matrix.python-version }}-v1-
191+
- name: Install dependencies
192+
run: |
193+
python -m pip install --upgrade pip
194+
python -m pip install --upgrade tox
195+
- name: Test with tox
196+
run: tox -e style,readme

README.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ Here's a screenshot of the toolbar in action:
3131
In addition to the built-in panels, a number of third-party panels are
3232
contributed by the community.
3333

34-
The current stable version of the Debug Toolbar is 2.2. It works on
34+
The current stable version of the Debug Toolbar is 2.2.1. It works on
3535
Django ≥ 1.11.
3636

3737
Documentation, including installation and configuration instructions, is

debug_toolbar/decorators.py

+19-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import functools
22

3-
from django.http import Http404
3+
from django.http import Http404, HttpResponseBadRequest
44

55

66
def require_show_toolbar(view):
@@ -15,3 +15,21 @@ def inner(request, *args, **kwargs):
1515
return view(request, *args, **kwargs)
1616

1717
return inner
18+
19+
20+
def signed_data_view(view):
21+
"""Decorator that handles unpacking a signed data form"""
22+
23+
@functools.wraps(view)
24+
def inner(request, *args, **kwargs):
25+
from debug_toolbar.forms import SignedDataForm
26+
27+
data = request.GET if request.method == "GET" else request.POST
28+
signed_form = SignedDataForm(data)
29+
if signed_form.is_valid():
30+
return view(
31+
request, *args, verified_data=signed_form.verified_data(), **kwargs
32+
)
33+
return HttpResponseBadRequest("Invalid signature")
34+
35+
return inner

debug_toolbar/forms.py

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import json
2+
from collections import OrderedDict
3+
4+
from django import forms
5+
from django.core import signing
6+
from django.core.exceptions import ValidationError
7+
from django.utils.encoding import force_str
8+
9+
10+
class SignedDataForm(forms.Form):
11+
"""Helper form that wraps a form to validate its contents on post.
12+
13+
class PanelForm(forms.Form):
14+
# fields
15+
16+
On render:
17+
form = SignedDataForm(initial=PanelForm(initial=data).initial)
18+
19+
On POST:
20+
signed_form = SignedDataForm(request.POST)
21+
if signed_form.is_valid():
22+
panel_form = PanelForm(signed_form.verified_data)
23+
if panel_form.is_valid():
24+
# Success
25+
Or wrap the FBV with ``debug_toolbar.decorators.signed_data_view``
26+
"""
27+
28+
salt = "django_debug_toolbar"
29+
signed = forms.CharField(required=True, widget=forms.HiddenInput)
30+
31+
def __init__(self, *args, **kwargs):
32+
initial = kwargs.pop("initial", None)
33+
if initial:
34+
initial = {"signed": self.sign(initial)}
35+
super().__init__(*args, initial=initial, **kwargs)
36+
37+
def clean_signed(self):
38+
try:
39+
verified = json.loads(
40+
signing.Signer(salt=self.salt).unsign(self.cleaned_data["signed"])
41+
)
42+
return verified
43+
except signing.BadSignature:
44+
raise ValidationError("Bad signature")
45+
46+
def verified_data(self):
47+
return self.is_valid() and self.cleaned_data["signed"]
48+
49+
@classmethod
50+
def sign(cls, data):
51+
items = sorted(data.items(), key=lambda item: item[0])
52+
return signing.Signer(salt=cls.salt).sign(
53+
json.dumps(OrderedDict((key, force_str(value)) for key, value in items))
54+
)

debug_toolbar/panels/cache.py

+16-10
Original file line numberDiff line numberDiff line change
@@ -216,20 +216,26 @@ def _store_call_info(
216216
@property
217217
def nav_subtitle(self):
218218
cache_calls = len(self.calls)
219-
return __(
220-
"%(cache_calls)d call in %(time).2fms",
221-
"%(cache_calls)d calls in %(time).2fms",
222-
cache_calls,
223-
) % {"cache_calls": cache_calls, "time": self.total_time}
219+
return (
220+
__(
221+
"%(cache_calls)d call in %(time).2fms",
222+
"%(cache_calls)d calls in %(time).2fms",
223+
cache_calls,
224+
)
225+
% {"cache_calls": cache_calls, "time": self.total_time}
226+
)
224227

225228
@property
226229
def title(self):
227230
count = len(getattr(settings, "CACHES", ["default"]))
228-
return __(
229-
"Cache calls from %(count)d backend",
230-
"Cache calls from %(count)d backends",
231-
count,
232-
) % {"count": count}
231+
return (
232+
__(
233+
"Cache calls from %(count)d backend",
234+
"Cache calls from %(count)d backends",
235+
count,
236+
)
237+
% {"count": count}
238+
)
233239

234240
def enable_instrumentation(self):
235241
if isinstance(middleware_cache.caches, CacheHandlerPatch):

debug_toolbar/panels/history/forms.py

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from django import forms
2+
3+
4+
class HistoryStoreForm(forms.Form):
5+
"""
6+
Validate params
7+
8+
store_id: The key for the store instance to be fetched.
9+
"""
10+
11+
store_id = forms.CharField(widget=forms.HiddenInput())

0 commit comments

Comments
 (0)