mirror of
https://github.com/suitenumerique/drive.git
synced 2026-08-17 20:15:40 +02:00
🐛(backend) sanitize slash in template-created filenames
Titles containing '/' (e.g. "30/03/30 - liste à faire") produced a file_key with spurious path separators, crashing WOPI on open. Extract `format_template_filename()` to replace '/' with '-' when building the filename from a template title. Closes #626
This commit is contained in:
@@ -19,6 +19,7 @@ and this project adheres to
|
||||
### Changed
|
||||
|
||||
- 🐛(backend) replace VersionId by Etag for WOPI
|
||||
- 🐛(backend) sanitize slash in template-created filenames
|
||||
|
||||
### Removed
|
||||
|
||||
|
||||
@@ -659,7 +659,7 @@ class CreateItemSerializer(ItemSerializer):
|
||||
raise serializers.ValidationError(
|
||||
{"title": _("This field is required.")},
|
||||
)
|
||||
attrs["filename"] = f"{attrs['title']}.{extension}"
|
||||
attrs["filename"] = utils.format_template_filename(attrs["title"], extension)
|
||||
else:
|
||||
# Regular file upload
|
||||
if attrs.get("filename") is None:
|
||||
|
||||
@@ -225,6 +225,11 @@ def detect_mimetype(file_buffer: bytes, filename: str | None = None) -> str:
|
||||
return mimetype_from_content or "application/octet-stream"
|
||||
|
||||
|
||||
def format_template_filename(title, extension):
|
||||
"""Build a filename from a template title and extension, replacing '/' with '-'."""
|
||||
return f"{title}.{extension}".replace("/", "-")
|
||||
|
||||
|
||||
def sanitize_filename(filename):
|
||||
"""
|
||||
Sanitize a filename to be compliant to use on filesystem.
|
||||
|
||||
@@ -352,3 +352,32 @@ def test_api_items_children_from_template_correct_mimetype(extension, expected_m
|
||||
|
||||
child = Item.objects.get(id=response.json()["id"])
|
||||
assert child.mimetype == expected_mimetype
|
||||
|
||||
|
||||
def test_api_items_children_from_template_title_with_slash_is_sanitized():
|
||||
"""
|
||||
Creating a file from template with a slash in the title should
|
||||
produce a sanitized filename (no slash in the stored filename).
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
access = factories.UserItemAccessFactory(
|
||||
user=user, role="owner", item__type=ItemTypeChoices.FOLDER
|
||||
)
|
||||
|
||||
response = client.post(
|
||||
f"/api/v1.0/items/{access.item.id!s}/children/",
|
||||
{
|
||||
"title": "30/03/30 - liste à faire",
|
||||
"extension": "odt",
|
||||
"type": "file",
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 201
|
||||
|
||||
child = Item.objects.get(id=response.json()["id"])
|
||||
assert child.filename == "30-03-30 - liste à faire.odt"
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
"""Test for the format_template_filename function in the api utils module."""
|
||||
|
||||
import pytest
|
||||
|
||||
from core.api.utils import format_template_filename
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"title,extension,expected",
|
||||
[
|
||||
("my document", "odt", "my document.odt"),
|
||||
("30/03/30 - liste à faire", "odt", "30-03-30 - liste à faire.odt"),
|
||||
("budget/2026", "ods", "budget-2026.ods"),
|
||||
("a/b/c/d", "odp", "a-b-c-d.odp"),
|
||||
("/leading slash", "odt", "-leading slash.odt"),
|
||||
("trailing slash/", "odt", "trailing slash-.odt"),
|
||||
("../../etc/passwd", "odt", "..-..-etc-passwd.odt"),
|
||||
("notes..brouillon", "odt", "notes..brouillon.odt"),
|
||||
],
|
||||
)
|
||||
def test_api_utils_format_template_filename(title, extension, expected):
|
||||
"""Slashes in the title are replaced with dashes in the resulting filename."""
|
||||
assert format_template_filename(title, extension) == expected
|
||||
Reference in New Issue
Block a user