🐛(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:
neilcroft
2026-06-04 11:26:58 +02:00
parent 43bc6ece92
commit 05754638ad
5 changed files with 59 additions and 1 deletions
+1
View File
@@ -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
+1 -1
View File
@@ -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:
+5
View File
@@ -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