From ca92090cca9400964021cda7f23437ce18c72f6a Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Wed, 8 Oct 2025 15:19:07 +0200 Subject: [PATCH] fix(trashbin): Strip off ".d{timestamp}" in trashbin folders Signed-off-by: Joas Schilling --- lib/Operation.php | 28 +++++++++++++++++++- tests/Integration/features/mimetypes.feature | 13 +++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/lib/Operation.php b/lib/Operation.php index 9f5b6cc6..ef1dc0ef 100644 --- a/lib/Operation.php +++ b/lib/Operation.php @@ -108,13 +108,20 @@ protected function isBlockablePath(IStorage $storage, string $path): bool { } // '', admin, 'files', 'path/to/file.txt' - $segment = explode('/', $fullPath, 4); + // '', admin, 'files_trashbin', 'versions', 'path/to/file.txt' + $segment = explode('/', $fullPath, 5); if (isset($segment[2]) && $segment[1] === '__groupfolders' && $segment[2] === 'trash') { // Special case, a file was deleted inside a groupfolder return true; } + if (isset($segment[3]) && $segment[2] === 'files_trashbin' + && ($segment[3] === 'keys' || !isset($segment[4]))) { + // Ignore encryption keys in trashbin and the files and version folder themselves + return false; + } + return isset($segment[2]) && in_array($segment[2], [ 'files', 'thumbnails', @@ -139,6 +146,25 @@ protected function translatePath(IStorage $storage, string $path): string { // Remove trailing ".v{timestamp}" $innerPath = substr($innerPath, 0, -12); } + return 'files/' . $innerPath; + } elseif ($folder === 'files_trashbin') { + // 'files', 'path/to/file.txt' + // 'versions', 'path/to/file.txt' + $segments = explode('/', $innerPath, 2); + if (isset($segments[1])) { + $innerPath = $segments[1]; + } + + if (preg_match('/.+\.d\d{10}$/', basename($innerPath))) { + // Remove trailing ".d{timestamp}" of trashbin + $innerPath = substr($innerPath, 0, -12); + } + + if ($folder === 'versions' && preg_match('/.+\.v\d{10}$/', basename($innerPath))) { + // Remove trailing ".v{timestamp}" of versions inside trashbin + $innerPath = substr($innerPath, 0, -12); + } + return 'files/' . $innerPath; } elseif ($folder === 'thumbnails') { [$fileId,] = explode('/', $innerPath, 2); diff --git a/tests/Integration/features/mimetypes.feature b/tests/Integration/features/mimetypes.feature index 170fc9dc..607a6780 100644 --- a/tests/Integration/features/mimetypes.feature +++ b/tests/Integration/features/mimetypes.feature @@ -85,3 +85,16 @@ And The webdav response should have a status code "403" When User "test1" copies file "/hello" to "/hello.txt" And The webdav response should have a status code "403" + + Scenario: Blocking by mimetype works in trashbin + Given User "test1" uploads file "data/textfile.txt" to "/foobar.txt" + And user "admin" creates global flow with 200 + | name | Admin flow | + | class | OCA\FilesAccessControl\Operation | + | entity | OCA\WorkflowEngine\Entity\File | + | events | [] | + | operation | deny | + | checks-0 | {"class":"OCA\\\\WorkflowEngine\\\\Check\\\\FileMimeType", "operator": "!is", "value": "httpd/unix-directory"} | + | checks-1 | {"class":"OCA\\\\WorkflowEngine\\\\Check\\\\FileMimeType", "operator": "!is", "value": "text/plain"} | + When User "test1" deletes file "/foobar.txt" + Then The webdav response should have a status code "204"