From 9e4751197c4046a5a36e1284149eafc8fa56dace Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Wed, 8 Oct 2025 15:19:07 +0200 Subject: [PATCH 1/2] 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 8865e6f1..c849a872 100644 --- a/lib/Operation.php +++ b/lib/Operation.php @@ -107,13 +107,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', @@ -138,6 +145,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 1b4aacc7..21f23410 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" From 041acd9a10172d5a4affbe14166b4373fcfc4a54 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 10 Oct 2025 13:39:25 +0200 Subject: [PATCH 2/2] fix: Compatibility with stable30 Signed-off-by: Joas Schilling --- tests/Integration/features/bootstrap/FeatureContext.php | 9 ++++----- tests/Integration/features/mimetypes.feature | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/Integration/features/bootstrap/FeatureContext.php b/tests/Integration/features/bootstrap/FeatureContext.php index 01e2db33..41e87c81 100644 --- a/tests/Integration/features/bootstrap/FeatureContext.php +++ b/tests/Integration/features/bootstrap/FeatureContext.php @@ -104,12 +104,11 @@ public function createFlow(string $user, string $scope, int $statusCode, TableNo $this->setCurrentUser($user); $formData = $tableNode->getRowsHash(); - $checks = []; foreach ($formData as $key => $value) { if (strpos($key, 'checks-') === 0) { $value = str_replace('{{{FILES_ACCESSCONTROL_INTEGRATIONTEST_TAGID}}}', $this->tagId, $value); - $checks[] = json_decode($value, true); + $checks[] = json_decode($value, true, flags: JSON_THROW_ON_ERROR); unset($formData[$key]); } } @@ -118,7 +117,7 @@ public function createFlow(string $user, string $scope, int $statusCode, TableNo $formData['events'] = []; $this->sendingToWith('POST', '/apps/workflowengine/api/v1/workflows/' . $scope, $formData); - Assert::assertSame($statusCode, $this->response->getStatusCode(), 'HTTP status code mismatch'); + Assert::assertSame($statusCode, $this->response->getStatusCode(), 'HTTP status code mismatch:' . "\n" . $this->response->getBody()->getContents()); } /** @@ -144,10 +143,10 @@ public function theWebdavResponseShouldHaveAStatusCode($statusCode) { if (str_contains($statusCode, '|')) { $statusCodes = array_map('intval', explode('|', $statusCode)); } else { - $statusCodes = [(int) $statusCode]; + $statusCodes = [(int)$statusCode]; } if (!in_array($this->response->getStatusCode(), $statusCodes, true)) { - throw new \Exception("Expected $statusCode, got ".$this->response->getStatusCode()); + throw new \Exception("Expected $statusCode, got " . $this->response->getStatusCode()); } } diff --git a/tests/Integration/features/mimetypes.feature b/tests/Integration/features/mimetypes.feature index 21f23410..63049fd5 100644 --- a/tests/Integration/features/mimetypes.feature +++ b/tests/Integration/features/mimetypes.feature @@ -94,7 +94,7 @@ | 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"} | + | 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"