Skip to content

Commit bec9e0d

Browse files
authored
Managed Files: Sanitized file name before downloading (#12406)
1 parent c4c6fc3 commit bec9e0d

File tree

2 files changed

+29
-1
lines changed

2 files changed

+29
-1
lines changed

dojo/utils.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2629,9 +2629,11 @@ def generate_file_response(file_object: FileUpload) -> FileResponse:
26292629
raise TypeError(msg)
26302630
# Determine the path of the file on disk within the MEDIA_ROOT
26312631
file_path = f"{settings.MEDIA_ROOT}/{file_object.file.url.lstrip(settings.MEDIA_URL)}"
2632+
# Clean the title by removing some problematic characters
2633+
cleaned_file_name = re.sub(r'[<>:"/\\|?*`=\'&%#;]', "-", file_object.title)
26322634

26332635
return generate_file_response_from_file_path(
2634-
file_path, file_name=file_object.title, file_size=file_object.file.size,
2636+
file_path, file_name=cleaned_file_name, file_size=file_object.file.size,
26352637
)
26362638

26372639

unittests/test_rest_framework.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import json
33
import logging
44
import pathlib
5+
import re
56
from collections import OrderedDict
67
from enum import Enum
78
from json import dumps
@@ -1141,6 +1142,31 @@ def test_request_response_get(self):
11411142
response = self.client.get(f"/api/v2/{level}/files/")
11421143
self.assertEqual(200, response.status_code)
11431144

1145+
def test_file_with_quoted_name(self):
1146+
level = "findings/7"
1147+
with (get_unit_tests_scans_path("acunetix") / "one_finding.xml").open(encoding="utf-8") as testfile:
1148+
# Create a new file first
1149+
payload = {
1150+
"title": 'A file "title" with Quotes & other bad chars #broken',
1151+
"file": testfile,
1152+
}
1153+
response = self.client.post(f"/api/v2/{level}/files/", payload)
1154+
self.assertEqual(201, response.status_code, response.data)
1155+
file_id = response.data.get("id")
1156+
1157+
# Download the file and ensure the content is accurate
1158+
response = self.client.get(f"/api/v2/{level}/files/download/{file_id}/")
1159+
downloaded_file = b"".join(response.streaming_content).decode().replace("\\n", "\n")
1160+
file_data = (get_unit_tests_scans_path("acunetix") / "one_finding.xml").read_text(encoding="utf-8")
1161+
self.assertEqual(file_data, downloaded_file)
1162+
# Check the name of the file is correct
1163+
if (match := re.search(r'filename="?(?P<filename>[^";]+)"?', response.get("Content-Disposition"))):
1164+
filename = match.group("filename")
1165+
self.assertEqual(filename, "A file -title- with Quotes - other bad chars -broken.xml")
1166+
else:
1167+
msg = "Content-Disposition header must contain the filename parameter"
1168+
raise NotImplementedError(msg)
1169+
11441170

11451171
class FindingsTest(BaseClass.BaseClassTest):
11461172
fixtures = ["dojo_testdata.json"]

0 commit comments

Comments
 (0)