diff options
| -rw-r--r-- | django/core/management/templates.py | 3 | ||||
| -rw-r--r-- | tests/admin_scripts/tests.py | 15 | ||||
| -rw-r--r-- | tests/admin_scripts/urls.py | 6 | ||||
| -rw-r--r-- | tests/admin_scripts/views.py | 17 |
4 files changed, 40 insertions, 1 deletions
diff --git a/django/core/management/templates.py b/django/core/management/templates.py index ea2c4a294f..1751f12cbc 100644 --- a/django/core/management/templates.py +++ b/django/core/management/templates.py @@ -18,6 +18,7 @@ from django.core.management.utils import ( ) 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 @@ -345,7 +346,7 @@ class TemplateCommand(BaseCommand): # 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 diff --git a/tests/admin_scripts/tests.py b/tests/admin_scripts/tests.py index 819ba931d6..3eb7b97c99 100644 --- a/tests/admin_scripts/tests.py +++ b/tests/admin_scripts/tests.py @@ -2778,6 +2778,21 @@ class StartProject(LiveServerTestCase, AdminScriptTestCase): 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 diff --git a/tests/admin_scripts/urls.py b/tests/admin_scripts/urls.py index a6cc7fe1b5..3e7aa063d3 100644 --- a/tests/admin_scripts/urls.py +++ b/tests/admin_scripts/urls.py @@ -3,6 +3,8 @@ import os from django.urls import path from django.views.static import serve +from . import views + here = os.path.dirname(__file__) urlpatterns = [ @@ -11,4 +13,8 @@ urlpatterns = [ serve, {"document_root": os.path.join(here, "custom_templates")}, ), + path( + "bad_template_filename.tgz", + views.template_bad_filename, + ), ] diff --git a/tests/admin_scripts/views.py b/tests/admin_scripts/views.py new file mode 100644 index 0000000000..d57803659d --- /dev/null +++ b/tests/admin_scripts/views.py @@ -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 |
