Skip to content

Commit c97a508

Browse files
committed
Merge remote-tracking branch 'upstream/main'
2 parents 67be155 + 240421c commit c97a508

File tree

6 files changed

+219
-26
lines changed

6 files changed

+219
-26
lines changed

django/template/defaulttags.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,10 @@ def render(self, context):
206206
unpack = num_loopvars > 1
207207
# Create a forloop value in the context. We'll update counters on each
208208
# iteration just below.
209-
loop_dict = context["forloop"] = {"parentloop": parentloop}
209+
loop_dict = context["forloop"] = {
210+
"parentloop": parentloop,
211+
"length": len_values,
212+
}
210213
for i, item in enumerate(values):
211214
# Shortcuts for current loop iteration number.
212215
loop_dict["counter0"] = i

docs/internals/security.txt

+179-13
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,13 @@ implications, please send a description of the issue via email to
2727
team <https://www.djangoproject.com/foundation/teams/#security-team>`_.
2828

2929
Once you've submitted an issue via email, you should receive an acknowledgment
30-
from a member of the security team within 48 hours, and depending on the
31-
action to be taken, you may receive further followup emails.
30+
from a member of the security team within 3 working days. After that, the
31+
security team will begin their analysis. Depending on the action to be taken,
32+
you may receive followup emails. It can take several weeks before the security
33+
team comes to a conclusion. There is no need to chase the security team unless
34+
you discover new, relevant information. All reports aim to be resolved within
35+
the industry-standard 90 days. Confirmed vulnerabilities with a
36+
:ref:`high severity level <severity-levels>` will be addressed promptly.
3237

3338
.. admonition:: Sending encrypted reports
3439

@@ -38,6 +43,157 @@ action to be taken, you may receive further followup emails.
3843

3944
.. _our public Trac instance: https://code.djangoproject.com/query
4045

46+
Reporting guidelines
47+
--------------------
48+
49+
Include a runnable proof of concept
50+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
51+
52+
Please privately share a minimal Django project or code snippet that
53+
demonstrates the potential vulnerability. Include clear instructions on how to
54+
set up, run, and reproduce the issue.
55+
56+
Please do not attach screenshots of code.
57+
58+
User input must be sanitized
59+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
60+
61+
Reports based on a failure to sanitize user input are not valid security
62+
vulnerabilities. It is the developer's responsibility to properly handle user
63+
input. This principle is explained in our :ref:`security documentation
64+
<sanitize-user-input>`.
65+
66+
For example, the following is **not considered valid** because ``email`` has
67+
not been sanitized::
68+
69+
from django.core.mail import send_mail
70+
from django.http import JsonResponse
71+
72+
73+
def my_proof_of_concept(request):
74+
email = request.GET.get("email", "")
75+
send_mail("Email subject", "Email body", email, ["[email protected]"])
76+
return JsonResponse(status=200)
77+
78+
Developers must **always validate and sanitize input** before using it. The
79+
correct approach would be to use a Django form to ensure ``email`` is properly
80+
validated::
81+
82+
from django import forms
83+
from django.core.mail import send_mail
84+
from django.http import JsonResponse
85+
86+
87+
class EmailForm(forms.Form):
88+
email = forms.EmailField()
89+
90+
91+
def my_proof_of_concept(request):
92+
form = EmailForm(request.GET)
93+
if form.is_valid():
94+
send_mail(
95+
"Email subject",
96+
"Email body",
97+
form.cleaned_data["email"],
98+
99+
)
100+
return JsonResponse(status=200)
101+
return JsonResponse(form.errors, status=400)
102+
103+
Similarly, as Django's raw SQL constructs (such as :meth:`~.QuerySet.extra` and
104+
:class:`.RawSQL` expression) provide developers with full control over the
105+
query, they are insecure if user input is not properly handled. As explained in
106+
our :ref:`security documentation <sql-injection-protection>`, it is the
107+
developer's responsibility to safely process user input for these functions.
108+
109+
For instance, the following is **not considered valid** because ``query`` has
110+
not been sanitized::
111+
112+
from django.shortcuts import HttpResponse
113+
from .models import MyModel
114+
115+
116+
def my_proof_of_concept(request):
117+
query = request.GET.get("query", "")
118+
q = MyModel.objects.extra(select={"id": query})
119+
return HttpResponse(q.values())
120+
121+
Request headers and URLs must be under 8K bytes
122+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
123+
124+
To prevent denial-of-service (DoS) attacks, production-grade servers impose
125+
limits on request header and URL sizes. For example, by default Gunicorn allows
126+
up to roughly:
127+
128+
* `4k bytes for a URL`_
129+
* `8K bytes for a request header`_
130+
131+
Other web servers, such as Nginx and Apache, have similar restrictions to
132+
prevent excessive resource consumption.
133+
134+
Consequently, the Django security team will not consider reports that rely on
135+
request headers or URLs exceeding 8K bytes, as such inputs are already
136+
mitigated at the server level in production environments.
137+
138+
.. admonition:: :djadmin:`runserver` should never be used in production
139+
140+
Django's built-in development server does not enforce these limits because
141+
it is not designed to be a production server.
142+
143+
.. _`4k bytes for a URL`: https://docs.gunicorn.org/en/stable/settings.html#limit-request-line
144+
.. _`8k bytes for a request header`: https://docs.gunicorn.org/en/stable/settings.html#limit-request-field-size
145+
146+
The request body must be under 2.5 MB
147+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
148+
149+
The :setting:`DATA_UPLOAD_MAX_MEMORY_SIZE` setting limits the default maximum
150+
request body size to 2.5 MB.
151+
152+
As this is enforced on all production-grade Django projects by default, a proof
153+
of concept must not exceed 2.5 MB in the request body to be considered valid.
154+
155+
Issues resulting from large, but potentially reasonable setting values, should
156+
be reported using the `public ticket tracker`_ for hardening.
157+
158+
.. _public ticket tracker: https://code.djangoproject.com/
159+
160+
Code under test must feasibly exist in a Django project
161+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
162+
163+
The proof of concept must plausibly occur in a production-grade Django
164+
application, reflecting real-world scenarios and following standard development
165+
practices.
166+
167+
Django contains many private and undocumented functions that are not part of
168+
its public API. If a vulnerability depends on directly calling these internal
169+
functions in an unsafe way, it will not be considered a valid security issue.
170+
171+
Content displayed by the Django Template Language must be under 100 KB
172+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
173+
174+
The Django Template Language (DTL) is designed for building the content needed
175+
to display web pages. In particular its text filters are meant for that kind of
176+
usage.
177+
178+
For reference, the complete works of Shakespeare have about 3.5 million bytes
179+
in plain-text ASCII encoding. Displaying such in a single request is beyond the
180+
scope of almost all websites, and so outside the scope of the DTL too.
181+
182+
Text processing is expensive. Django makes no guarantee that DTL text filters
183+
are never subject to degraded performance if passed deliberately crafted,
184+
sufficiently large inputs. Under default configurations, Django makes it
185+
difficult for sites to accidentally accept such payloads from untrusted
186+
sources, but, if it is necessary to display large amounts of user-provided
187+
content, it’s important that basic security measures are taken.
188+
189+
User-provided content should always be constrained to known maximum length. It
190+
should be filtered to remove malicious content, and validated to match expected
191+
formats. It should then be processed offline, if necessary, before being
192+
displayed.
193+
194+
Proof of concepts which use over 100 KB of data to be processed by the DTL will
195+
be considered invalid.
196+
41197
.. _security-report-evaluation:
42198

43199
How does Django evaluate a report
@@ -110,20 +266,15 @@ will not issue patches or new releases for those versions.
110266

111267
.. _main development branch: https://github.com/django/django/
112268

113-
.. _security-disclosure:
269+
.. _severity-levels:
114270

115-
How Django discloses security issues
116-
====================================
271+
Security issue severity levels
272+
==============================
117273

118-
Our process for taking a security issue from private discussion to
119-
public disclosure involves multiple steps.
274+
The severity level of a security vulnerability is determined by the attack
275+
type.
120276

121-
Approximately one week before public disclosure, we send two notifications:
122-
123-
First, we notify |django-announce| of the date and approximate time of the
124-
upcoming security release, as well as the severity of the issues. This is to
125-
aid organizations that need to ensure they have staff available to handle
126-
triaging our announcement and upgrade Django as needed. Severity levels are:
277+
Severity levels are:
127278

128279
* **High**
129280

@@ -144,6 +295,21 @@ triaging our announcement and upgrade Django as needed. Severity levels are:
144295
* Unvalidated redirects/forwards
145296
* Issues requiring an uncommon configuration option
146297

298+
.. _security-disclosure:
299+
300+
How Django discloses security issues
301+
====================================
302+
303+
Our process for taking a security issue from private discussion to
304+
public disclosure involves multiple steps.
305+
306+
Approximately one week before public disclosure, we send two notifications:
307+
308+
First, we notify |django-announce| of the date and approximate time of the
309+
upcoming security release, as well as the severity of the issues. This is to
310+
aid organizations that need to ensure they have staff available to handle
311+
triaging our announcement and upgrade Django as needed.
312+
147313
Second, we notify a list of :ref:`people and organizations
148314
<security-notifications>`, primarily composed of operating-system vendors and
149315
other distributors of Django. This email is signed with the PGP key of someone

docs/ref/templates/builtins.txt

+5-11
Original file line numberDiff line numberDiff line change
@@ -423,10 +423,15 @@ Variable Description
423423
loop (0-indexed)
424424
``forloop.first`` True if this is the first time through the loop
425425
``forloop.last`` True if this is the last time through the loop
426+
``forloop.length`` The length of the loop
426427
``forloop.parentloop`` For nested loops, this is the loop surrounding
427428
the current one
428429
========================== ===============================================
429430

431+
.. versionchanged:: 6.0
432+
433+
The variable ``forloop.length`` was added.
434+
430435
``for`` ... ``empty``
431436
---------------------
432437

@@ -2920,17 +2925,6 @@ Django's built-in :tfilter:`escape` filter. The default value for
29202925
email addresses that contain single quotes (``'``), things won't work as
29212926
expected. Apply this filter only to plain text.
29222927

2923-
.. warning::
2924-
2925-
Using ``urlize`` or ``urlizetrunc`` can incur a performance penalty, which
2926-
can become severe when applied to user controlled values such as content
2927-
stored in a :class:`~django.db.models.TextField`. You can use
2928-
:tfilter:`truncatechars` to add a limit to such inputs:
2929-
2930-
.. code-block:: html+django
2931-
2932-
{{ value|truncatechars:500|urlize }}
2933-
29342928
.. templatefilter:: urlizetrunc
29352929

29362930
``urlizetrunc``

docs/releases/6.0.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,8 @@ Signals
207207
Templates
208208
~~~~~~~~~
209209

210-
* ...
210+
* The new variable ``forloop.length`` is now available within a :ttag:`for`
211+
loop.
211212

212213
Tests
213214
~~~~~

docs/topics/security.txt

+10
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@ Security in Django
55
This document is an overview of Django's security features. It includes advice
66
on securing a Django-powered site.
77

8+
.. _sanitize-user-input:
9+
10+
Always sanitize user input
11+
==========================
12+
13+
The golden rule of web application security is to never trust user-controlled
14+
data. Hence, all user input should be sanitized before being used in your
15+
application. See the :doc:`forms documentation </topics/forms/index>` for
16+
details on validating user inputs in Django.
17+
818
.. _cross-site-scripting:
919

1020
Cross site scripting (XSS) protection

tests/template_tests/syntax_tests/test_for.py

+19
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,25 @@ def test_invalid_in_keyword(self):
356356
with self.assertRaisesMessage(TemplateSyntaxError, msg):
357357
self.engine.render_to_string("invalid_for_loop", {"items": (1, 2)})
358358

359+
@setup(
360+
{
361+
"forloop-length": "{% for val in values %}{{ forloop.length }}{% endfor %}",
362+
"forloop-length-reversed": "{% for val in values reversed %}"
363+
"{{ forloop.length }}{% endfor %}",
364+
}
365+
)
366+
def test_forloop_length(self):
367+
cases = [
368+
([1, 2, 3], "333"),
369+
([1, 2, 3, 4, 5, 6], "666666"),
370+
([], ""),
371+
]
372+
for values, expected_output in cases:
373+
for template in ["forloop-length", "forloop-length-reversed"]:
374+
with self.subTest(expected_output=expected_output, template=template):
375+
output = self.engine.render_to_string(template, {"values": values})
376+
self.assertEqual(output, expected_output)
377+
359378

360379
class ForNodeTests(SimpleTestCase):
361380
def test_repr(self):

0 commit comments

Comments
 (0)