Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/workflows/check_commit_messages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
with:
persist-credentials: false

- name: Calculate commit prefix
- name: Set base and calculate commit prefix
id: vars
env:
BASE: ${{ github.event.pull_request.base.ref }}
Expand Down Expand Up @@ -80,6 +80,11 @@ jobs:
with:
persist-credentials: false

- name: Set base
env:
BASE: ${{ github.event.pull_request.base.ref }}
run: echo "BASE=$BASE" >> $GITHUB_ENV

- name: Fetch relevant branches
run: |
git fetch origin $BASE:base --depth=1
Expand Down
3 changes: 2 additions & 1 deletion django/core/management/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
)
from django.template import Context, Engine
from django.utils import archive
from django.utils._os import safe_join
from django.utils.http import parse_header_parameters
from django.utils.version import get_docs_version

Expand Down Expand Up @@ -345,7 +346,7 @@ def cleanup_url(url):
# Move the temporary file to a filename that has better
# chances of being recognized by the archive utils
if used_name != guessed_filename:
guessed_path = os.path.join(tempdir, guessed_filename)
guessed_path = safe_join(tempdir, guessed_filename)
shutil.move(the_path, guessed_path)
return guessed_path

Expand Down
6 changes: 4 additions & 2 deletions django/middleware/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
learn_cache_key,
patch_response_headers,
patch_vary_headers,
split_header_value,
)
from django.utils.deprecation import MiddlewareMixin
from django.utils.http import parse_http_date_safe
Expand Down Expand Up @@ -106,8 +107,9 @@ def process_response(self, request, response):
# Don't cache responses when the Cache-Control header is set to
# private, no-cache, or no-store.
cache_control = response.get("Cache-Control", "").lower()
cache_control_parts = list(split_header_value(cache_control))
if cache_control and any(
directive in cache_control
directive in cache_control_parts
for directive in (
"private",
"no-cache",
Expand Down Expand Up @@ -137,7 +139,7 @@ def process_response(self, request, response):
# header, unless allowed by "public" per RFC 9111, Section 3.5. No
# exceptions are made for "s-maxage" and "must-revalidate" since these
# are not currently implemented by Django.
if request.headers.get("Authorization") and "public" not in cache_control:
if request.headers.get("Authorization") and "public" not in cache_control_parts:
patch_vary_headers(response, ("Authorization",))
if timeout and response.status_code == 200:
cache_key = learn_cache_key(
Expand Down
8 changes: 5 additions & 3 deletions docs/internals/contributing/writing-code/unit-tests.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,12 @@ that your computer doesn't have installed. You can usually figure out which
package to install by doing a web search for the last line or so of the error
message. Try adding your operating system to the search query if needed.

If you have trouble installing the requirements, you can skip that step. See
If you have trouble installing an optional test dependency, you can skip that
dependency locally. For example, if installing ``pylibmc`` fails, comment out
its line in ``requirements/py3.txt`` and continue with ``./runtests.py``.
Tests that require ``pylibmc`` will be skipped automatically. See
:ref:`running-unit-tests-dependencies` for details on installing the optional
test dependencies. If you don't have an optional dependency installed, the
tests that require it will be skipped.
test dependencies.

Running the tests requires a Django settings module that defines the databases
to use. To help you get started, Django provides and uses a sample settings
Expand Down
15 changes: 15 additions & 0 deletions tests/admin_scripts/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -2778,6 +2778,21 @@ def test_custom_project_template_from_tarball_by_url(self):
self.assertTrue(os.path.isdir(testproject_dir))
self.assertTrue(os.path.exists(os.path.join(testproject_dir, "run.py")))

def test_custom_project_template_from_tarball_by_url_bad_filename(self):
"""
The startproject management command will raise SuspiciousFileOperation
on an ill-formed remote template archive filename.
"""
template_url = "%s/bad_template_filename.tgz" % self.live_server_url

args = ["startproject", "--template", template_url, "urltestproject"]

out, err = self.run_django_admin(args)
self.assertOutput(
err,
"is located outside of the base path component",
)

def test_custom_project_template_from_tarball_by_url_django_user_agent(self):
user_agent = None

Expand Down
6 changes: 6 additions & 0 deletions tests/admin_scripts/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from django.urls import path
from django.views.static import serve

from . import views

here = os.path.dirname(__file__)

urlpatterns = [
Expand All @@ -11,4 +13,8 @@
serve,
{"document_root": os.path.join(here, "custom_templates")},
),
path(
"bad_template_filename.tgz",
views.template_bad_filename,
),
]
17 changes: 17 additions & 0 deletions tests/admin_scripts/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from pathlib import Path

from django.http import FileResponse


def template_bad_filename(request):
content = Path(__file__).parent / "custom_templates" / "project_template.tgz"
f = open(content, "rb")
filename = "/nonexistent/archive.tgz"
response = FileResponse(
f,
as_attachment=True,
filename=filename,
)
# Force the filename to have a slash at the beginning.
response["Content-Disposition"] = f'attachment; filename="{filename}"'
return response
27 changes: 27 additions & 0 deletions tests/cache/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -3026,6 +3026,23 @@ def view(request, value):
response = view(request, "2")
self.assertEqual(response.content, b"Hello World 2")

def test_cache_control_not_cached_superstring(self):
"""
"myprivate", a hypothetical extension directive, is not confused for
"private".
"""

@cache_page(3)
@cache_control(myprivate=True)
def view(request, value):
return HttpResponse(f"Hello World {value}")

request = self.factory.get("/view/")
response = view(request, "1")
self.assertEqual(response.content, b"Hello World 1")
response = view(request, "2")
self.assertEqual(response.content, b"Hello World 1")

def test_vary_asterisk_not_cached(self):
views_with_cache = (
cache_page(3)(hello_world_view_patch_vary_headers_asterisk),
Expand Down Expand Up @@ -3064,6 +3081,16 @@ def test_authorization_header_exceptions(self):
response = view_with_cache(request, "1")
self.assertIs(has_vary_header(response, "Authorization"), False)

def test_authorization_header_exception_superstring(self):
"""
"nopublic", a hypothetical extension directive, is not confused for
"public".
"""
view_with_cache = cache_page(3)(cache_control(no_public=True)(hello_world_view))
request = self.factory.get("/view/", headers={"Authorization": "token"})
response = view_with_cache(request, "1")
self.assertIs(has_vary_header(response, "Authorization"), True)

def test_sensitive_cookie_not_cached(self):
"""
Django must prevent caching of responses that set a user-specific (and
Expand Down
Loading