[BUGFIX] Files with unclean path indexed multiple times 69/22069/2
authorStefan Neufeind <typo3.neufeind@speedpartner.de>
Sat, 6 Apr 2013 15:11:52 +0000 (17:11 +0200)
committerWouter Wolters <typo3@wouterwolters.nl>
Sat, 6 Jul 2013 21:35:55 +0000 (23:35 +0200)
When adding a file or requesting a file by an identifier
cleanup any . and .. in the path before handing off
to the driver so files are not indexed multiple times.

Change-Id: I324c4637621165a56470fb95864eeff1254f62dc
Fixes: #46989
Releases: 6.2, 6.1, 6.0
Reviewed-on: https://review.typo3.org/22069
Reviewed-by: Wouter Wolters
Tested-by: Wouter Wolters
typo3/sysext/core/Classes/Resource/ResourceStorage.php
typo3/sysext/core/Classes/Utility/File/BasicFileUtility.php
typo3/sysext/core/Classes/Utility/PathUtility.php
typo3/sysext/core/Tests/Unit/Utility/PathUtilityTest.php

index 5a8bd10..913831a 100644 (file)
@@ -27,6 +27,7 @@ namespace TYPO3\CMS\Core\Resource;
  *  This copyright notice MUST APPEAR in all copies of the script!
  ***************************************************************/
 
+use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Core\Utility\PathUtility;
 
 /**
@@ -148,6 +149,12 @@ class ResourceStorage {
         */
        protected $signalSlotDispatcher;
 
+
+       /**
+       * @var \TYPO3\CMS\Core\Utility\File\BasicFileUtility
+       */
+       protected $basicFileUtility;
+
        /**
         * Capability for being browsable by (backend) users
         */
@@ -207,6 +214,7 @@ class ResourceStorage {
                // TODO do not set the "public" capability if no public URIs can be generated
                $this->processConfiguration();
                $this->resetFileAndFolderNameFiltersToDefault();
+               $this->basicFileUtility = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Utility\\File\BasicFileUtility');
        }
 
        /**
@@ -403,7 +411,7 @@ class ResourceStorage {
                                        $this->isOnline = TRUE;
                                } else {
                                        // check if the storage is disabled temporary for now
-                                       $registryObject = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Registry');
+                                       $registryObject = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Registry');
                                        $offlineUntil = $registryObject->get('core', 'sys_file_storage-' . $this->getUid() . '-offline-until');
                                        if ($offlineUntil && $offlineUntil > time()) {
                                                $this->isOnline = FALSE;
@@ -440,7 +448,7 @@ class ResourceStorage {
         * @return void
         */
        public function markAsTemporaryOffline() {
-               $registryObject = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Registry');
+               $registryObject = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Registry');
                $registryObject->set('core', 'sys_file_storage-' . $this->getUid() . '-offline-until', time() + 60 * 5);
                $this->storageRecord['is_online'] = 0;
                $this->isOnline = FALSE;
@@ -660,21 +668,21 @@ class ResourceStorage {
         * @return boolean TRUE if extension/filename is allowed
         */
        protected function checkFileExtensionPermission($fileName) {
-               $isAllowed = \TYPO3\CMS\Core\Utility\GeneralUtility::verifyFilenameAgainstDenyPattern($fileName);
+               $isAllowed = GeneralUtility::verifyFilenameAgainstDenyPattern($fileName);
                if ($isAllowed) {
-                       $fileInfo = \TYPO3\CMS\Core\Utility\GeneralUtility::split_fileref($fileName);
+                       $fileInfo = GeneralUtility::split_fileref($fileName);
                        // Set up the permissions for the file extension
                        $fileExtensionPermissions = $GLOBALS['TYPO3_CONF_VARS']['BE']['fileExtensions']['webspace'];
-                       $fileExtensionPermissions['allow'] = \TYPO3\CMS\Core\Utility\GeneralUtility::uniqueList(strtolower($fileExtensionPermissions['allow']));
-                       $fileExtensionPermissions['deny'] = \TYPO3\CMS\Core\Utility\GeneralUtility::uniqueList(strtolower($fileExtensionPermissions['deny']));
+                       $fileExtensionPermissions['allow'] = GeneralUtility::uniqueList(strtolower($fileExtensionPermissions['allow']));
+                       $fileExtensionPermissions['deny'] = GeneralUtility::uniqueList(strtolower($fileExtensionPermissions['deny']));
                        $fileExtension = strtolower($fileInfo['fileext']);
                        if ($fileExtension !== '') {
                                // If the extension is found amongst the allowed types, we return TRUE immediately
-                               if ($fileExtensionPermissions['allow'] === '*' || \TYPO3\CMS\Core\Utility\GeneralUtility::inList($fileExtensionPermissions['allow'], $fileExtension)) {
+                               if ($fileExtensionPermissions['allow'] === '*' || GeneralUtility::inList($fileExtensionPermissions['allow'], $fileExtension)) {
                                        return TRUE;
                                }
                                // If the extension is found amongst the denied types, we return FALSE immediately
-                               if ($fileExtensionPermissions['deny'] === '*' || \TYPO3\CMS\Core\Utility\GeneralUtility::inList($fileExtensionPermissions['deny'], $fileExtension)) {
+                               if ($fileExtensionPermissions['deny'] === '*' || GeneralUtility::inList($fileExtensionPermissions['deny'], $fileExtension)) {
                                        return FALSE;
                                }
                                // If no match we return TRUE
@@ -708,6 +716,7 @@ class ResourceStorage {
         * @return FileInterface
         */
        public function addFile($localFilePath, Folder $targetFolder, $fileName = '', $conflictMode = 'changeName') {
+               $localFilePath = PathUtility::cleanDirectoryName($localFilePath);
                // TODO check permissions (write on target, upload, ...)
                if (!file_exists($localFilePath)) {
                        throw new \InvalidArgumentException('File "' . $localFilePath . '" does not exist.', 1319552745);
@@ -797,6 +806,7 @@ class ResourceStorage {
         * @return FileInterface
         */
        public function getFile($identifier) {
+               $identifier = PathUtility::cleanDirectoryNameAndFile($identifier);
                return $this->driver->getFile($identifier);
        }
 
@@ -891,6 +901,7 @@ class ResourceStorage {
         */
        public function hasFile($identifier) {
                // @todo: access check?
+               $identifier = PathUtility::cleanDirectoryNameAndFile($identifier);
                return $this->driver->fileExists($identifier);
        }
 
@@ -1072,7 +1083,7 @@ class ResourceStorage {
                        throw new Exception\UploadException('The upload has failed, no uploaded file found!', 1322110455);
                }
                // Max upload size (kb) for files.
-               $maxUploadFileSize = \TYPO3\CMS\Core\Utility\GeneralUtility::getMaxUploadFileSize() * 1024;
+               $maxUploadFileSize = GeneralUtility::getMaxUploadFileSize() * 1024;
                if ($uploadedFileSize >= $maxUploadFileSize) {
                        throw new Exception\UploadSizeException('The uploaded file exceeds the size-limit of ' . $maxUploadFileSize . ' bytes', 1322110041);
                }
@@ -1609,7 +1620,7 @@ class ResourceStorage {
                if (!$this->checkFolderActionPermission('add', $parentFolder)) {
                        throw new Exception\InsufficientFolderWritePermissionsException('You are not allowed to create directories in the folder "' . $parentFolder->getIdentifier() . '"', 1323059807);
                }
-               $folderParts = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode('/', $folderName, TRUE);
+               $folderParts = GeneralUtility::trimExplode('/', $folderName, TRUE);
                foreach ($folderParts as $folder) {
                        // TODO check if folder creation succeeded
                        if ($this->hasFolderInFolder($folder, $parentFolder)) {
@@ -1908,7 +1919,7 @@ class ResourceStorage {
        protected function getUniqueName(Folder $folder, $theFile, $dontCheckForUnique = FALSE) {
                static $maxNumber = 99, $uniqueNamePrefix = '';
                // Fetches info about path, name, extention of $theFile
-               $origFileInfo = \TYPO3\CMS\Core\Utility\GeneralUtility::split_fileref($theFile);
+               $origFileInfo = GeneralUtility::split_fileref($theFile);
                // Adds prefix
                if ($uniqueNamePrefix) {
                        $origFileInfo['file'] = $uniqueNamePrefix . $origFileInfo['file'];
@@ -1964,21 +1975,21 @@ class ResourceStorage {
         * @return \TYPO3\CMS\Extbase\Object\ObjectManager
         */
        protected function getObjectManager() {
-               return \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Object\\ObjectManager');
+               return GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Object\\ObjectManager');
        }
 
        /**
         * @return ResourceFactory
         */
        protected function getFileFactory() {
-               return \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\ResourceFactory');
+               return GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\ResourceFactory');
        }
 
        /**
         * @return \TYPO3\CMS\Core\Resource\FileRepository
         */
        protected function getFileRepository() {
-               return \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\FileRepository');
+               return GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\FileRepository');
        }
 
        /**
@@ -1986,7 +1997,7 @@ class ResourceStorage {
         */
        protected function getFileProcessingService() {
                if (!$this->fileProcessingService) {
-                       $this->fileProcessingService = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\Service\\FileProcessingService', $this, $this->driver);
+                       $this->fileProcessingService = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\Service\\FileProcessingService', $this, $this->driver);
                }
                return $this->fileProcessingService;
        }
index 44ab443..05ae850 100644 (file)
@@ -27,6 +27,9 @@ namespace TYPO3\CMS\Core\Utility\File;
  *  This copyright notice MUST APPEAR in all copies of the script!
  ***************************************************************/
 
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Core\Utility\PathUtility;
+
 /**
  * Contains class with basic file management functions
  *
@@ -131,13 +134,13 @@ class BasicFileUtility {
         * @todo Define visibility
         */
        public function init($mounts, $f_ext) {
-               \TYPO3\CMS\Core\Utility\GeneralUtility::logDeprecatedFunction('All methods in this class should not be used anymore since TYPO3 6.0. Please use corresponding TYPO3\\CMS\\Core\\Resource\\ResourceStorage (fetched via BE_USERS->getFileStorages()), as all functions should be found there (in a cleaner manner).');
-               $this->f_ext['webspace']['allow'] = \TYPO3\CMS\Core\Utility\GeneralUtility::uniqueList(strtolower($f_ext['webspace']['allow']));
-               $this->f_ext['webspace']['deny'] = \TYPO3\CMS\Core\Utility\GeneralUtility::uniqueList(strtolower($f_ext['webspace']['deny']));
-               $this->f_ext['ftpspace']['allow'] = \TYPO3\CMS\Core\Utility\GeneralUtility::uniqueList(strtolower($f_ext['ftpspace']['allow']));
-               $this->f_ext['ftpspace']['deny'] = \TYPO3\CMS\Core\Utility\GeneralUtility::uniqueList(strtolower($f_ext['ftpspace']['deny']));
+               GeneralUtility::logDeprecatedFunction('All methods in this class should not be used anymore since TYPO3 6.0. Please use corresponding TYPO3\\CMS\\Core\\Resource\\ResourceStorage (fetched via BE_USERS->getFileStorages()), as all functions should be found there (in a cleaner manner).');
+               $this->f_ext['webspace']['allow'] = GeneralUtility::uniqueList(strtolower($f_ext['webspace']['allow']));
+               $this->f_ext['webspace']['deny'] = GeneralUtility::uniqueList(strtolower($f_ext['webspace']['deny']));
+               $this->f_ext['ftpspace']['allow'] = GeneralUtility::uniqueList(strtolower($f_ext['ftpspace']['allow']));
+               $this->f_ext['ftpspace']['deny'] = GeneralUtility::uniqueList(strtolower($f_ext['ftpspace']['deny']));
                $this->mounts = $mounts;
-               $this->webPath = \TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_DOCUMENT_ROOT');
+               $this->webPath = GeneralUtility::getIndpEnv('TYPO3_DOCUMENT_ROOT');
                $this->isInit = 1;
                $this->maxInputNameLen = $GLOBALS['TYPO3_CONF_VARS']['SYS']['maxFileNameLength'] ? $GLOBALS['TYPO3_CONF_VARS']['SYS']['maxFileNameLength'] : $this->maxInputNameLen;
        }
@@ -167,7 +170,7 @@ class BasicFileUtility {
        public function getTotalFileInfo($wholePath) {
                // @todo: deprecate this function, and replace its use in the storage/mounts
                $theuser = getmyuid();
-               $info = \TYPO3\CMS\Core\Utility\GeneralUtility::split_fileref($wholePath);
+               $info = GeneralUtility::split_fileref($wholePath);
                $info['tstamp'] = @filemtime($wholePath);
                $info['size'] = @filesize($wholePath);
                $info['type'] = @filetype($wholePath);
@@ -191,11 +194,11 @@ class BasicFileUtility {
                        $ik = strtolower($iconkey);
                        if ($ik) {
                                // If the extension is found amongst the allowed types, we return TRUE immediately
-                               if ($this->f_ext[$type]['allow'] == '*' || \TYPO3\CMS\Core\Utility\GeneralUtility::inList($this->f_ext[$type]['allow'], $ik)) {
+                               if ($this->f_ext[$type]['allow'] == '*' || GeneralUtility::inList($this->f_ext[$type]['allow'], $ik)) {
                                        return TRUE;
                                }
                                // If the extension is found amongst the denied types, we return FALSE immediately
-                               if ($this->f_ext[$type]['deny'] == '*' || \TYPO3\CMS\Core\Utility\GeneralUtility::inList($this->f_ext[$type]['deny'], $ik)) {
+                               if ($this->f_ext[$type]['deny'] == '*' || GeneralUtility::inList($this->f_ext[$type]['deny'], $ik)) {
                                        return FALSE;
                                }
                                // If no match we return TRUE
@@ -243,7 +246,7 @@ class BasicFileUtility {
                        $testPath = $this->slashPath($path);
                        $testPathWeb = $this->slashPath($this->webPath);
                        if ($testPathWeb && $testPath) {
-                               return \TYPO3\CMS\Core\Utility\GeneralUtility::isFirstPartOfStr($testPath, $testPathWeb);
+                               return GeneralUtility::isFirstPartOfStr($testPath, $testPathWeb);
                        }
                }
                return TRUE;
@@ -260,7 +263,7 @@ class BasicFileUtility {
         * @todo Define visibility
         */
        public function checkIfAllowed($ext, $theDest, $filename = '') {
-               return \TYPO3\CMS\Core\Utility\GeneralUtility::verifyFilenameAgainstDenyPattern($filename) && $this->is_allowed($ext, ($this->is_webpath($theDest) ? 'webspace' : 'ftpspace'));
+               return GeneralUtility::verifyFilenameAgainstDenyPattern($filename) && $this->is_allowed($ext, ($this->is_webpath($theDest) ? 'webspace' : 'ftpspace'));
        }
 
        /**
@@ -285,7 +288,7 @@ class BasicFileUtility {
        public function is_directory($theDir) {
                // @todo: should go into the LocalDriver in a protected way (not important to the outside world)
                if ($this->isPathValid($theDir)) {
-                       $theDir = $this->cleanDirectoryName($theDir);
+                       $theDir = PathUtility::cleanDirectoryName($theDir);
                        if (@is_dir($theDir)) {
                                return $theDir;
                        }
@@ -303,7 +306,7 @@ class BasicFileUtility {
         */
        public function isPathValid($theFile) {
                // @todo: should go into the LocalDriver in a protected way (not important to the outside world)
-               return \TYPO3\CMS\Core\Utility\GeneralUtility::validPathStr($theFile);
+               return GeneralUtility::validPathStr($theFile);
        }
 
        /**
@@ -322,7 +325,7 @@ class BasicFileUtility {
                // @todo: should go into the LocalDriver in a protected way (not important to the outside world)
                $theDest = $this->is_directory($theDest);
                // $theDest is cleaned up
-               $origFileInfo = \TYPO3\CMS\Core\Utility\GeneralUtility::split_fileref($theFile);
+               $origFileInfo = GeneralUtility::split_fileref($theFile);
                // Fetches info about path, name, extension of $theFile
                if ($theDest) {
                        if ($this->getUniqueNamePrefix) {
@@ -374,7 +377,7 @@ class BasicFileUtility {
                // @todo: deprecate this function, now done in the Storage object
                if ($thePath && $this->isPathValid($thePath) && is_array($this->mounts)) {
                        foreach ($this->mounts as $k => $val) {
-                               if (\TYPO3\CMS\Core\Utility\GeneralUtility::isFirstPartOfStr($thePath, $val['path'])) {
+                               if (GeneralUtility::isFirstPartOfStr($thePath, $val['path'])) {
                                        return $k;
                                }
                        }
@@ -391,7 +394,7 @@ class BasicFileUtility {
                // @todo: where and when to use this function?
                if (is_array($this->mounts)) {
                        foreach ($this->mounts as $k => $val) {
-                               if (\TYPO3\CMS\Core\Utility\GeneralUtility::isFirstPartOfStr($val['path'], PATH_site . $GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'])) {
+                               if (GeneralUtility::isFirstPartOfStr($val['path'], PATH_site . $GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'])) {
                                        return $k;
                                }
                        }
@@ -442,15 +445,15 @@ class BasicFileUtility {
         *
         *********************/
        /**
-        * Removes all dots, slashes and spaces after a path...
+        * Removes all dots, slashes and spaces after a path
         *
-        * @param       string          Input string
-        * @return      string          Output string
-        * @todo Define visibility
+        * @param string $theDir Input string
+        * @return string Output string
+        * @deprecated since 6.1, will be removed in two versions, use \TYPO3\CMS\Core\Utility\PathUtility::cleanDirectoryName() instead
         */
        public function cleanDirectoryName($theDir) {
-               // @todo: should go into the LocalDriver in a protected way (not important to the outside world)
-               return preg_replace('/[\\/\\. ]*$/', '', $this->rmDoubleSlash($theDir));
+               GeneralUtility::logDeprecatedFunction();
+               return PathUtility::cleanDirectoryName($theDir);
        }
 
        /**
@@ -505,7 +508,7 @@ class BasicFileUtility {
                                        $this->csConvObj = $GLOBALS['LANG']->csConvObj;
                                } else {
                                        // The object may not exist yet, so we need to create it now. Happens in the Install Tool for example.
-                                       $this->csConvObj = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Charset\\CharsetConverter');
+                                       $this->csConvObj = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Charset\\CharsetConverter');
                                }
                        }
                        // Define character set
@@ -531,4 +534,4 @@ class BasicFileUtility {
 }
 
 
-?>
\ No newline at end of file
+?>
index 3ec139c..157357a 100644 (file)
@@ -129,7 +129,6 @@ class PathUtility {
                return rtrim($path, $separator) . $separator;
        }
 
-
        /**
         * Returns trailing name component of path
         * Since basename() is locale dependent we need to access
@@ -193,6 +192,86 @@ class PathUtility {
                setlocale(LC_CTYPE, $currentLocale);
                return $pathinfo;
        }
+
+
+       /*********************
+        *
+        * Cleaning functions
+        *
+        *********************/
+       /**
+        * Removes all dots, slashes and spaces after a path...
+        *
+        * @param string $theDir Input string
+        * @return string Output string
+        * @see cleanDirectoryNameAndFile()
+        */
+       static public function cleanDirectoryName($theDir) {
+               if (GeneralUtility::isFirstPartOfStr($theDir, '/')) {
+                       $isAbsolutePath = TRUE;
+                       $theDir = ltrim($theDir, '/');
+               } else {
+                       $isAbsolutePath = FALSE;
+               }
+
+               $theDirParts = explode('/', $theDir);
+               $theDirPartsCount = count($theDirParts);
+
+               for ($partCount = 0; $partCount < $theDirPartsCount; $partCount++) {
+                       if ($theDirParts[$partCount] === '.') {
+                               // . in path: remove element
+                               array_splice($theDirParts, $partCount, 1);
+                               $partCount--;
+                               $theDirPartsCount--;
+                       }
+                       if (($partCount > 0) && ($theDirParts[$partCount] === '')) {
+                               // double-slashes in path: remove element
+                               // but first part may be empty (absolute path)
+                               array_splice($theDirParts, $partCount, 1);
+                               $partCount--;
+                               $theDirPartsCount--;
+                       } elseif ($theDirParts[$partCount] === '..') {
+                               if ($partCount >= 1) {
+                                       // /../ in path: remove this and previous element
+                                       array_splice($theDirParts, $partCount - 1, 2);
+                                       $partCount -= 2;
+                                       $theDirPartsCount -= 2;
+                               } elseif ($isAbsolutePath) {
+                                       // illegal path / security-check
+                                       // can't go higher than root dir
+                                       // simply remove this part and continue?
+                                       array_splice($theDirParts, $partCount, 1);
+                                       $partCount--;
+                                       $theDirPartsCount--;
+                               }
+                       }
+               }
+               $theDir = implode('/', $theDirParts);
+               $theDir = $isAbsolutePath ? '/' . $theDir : $theDir;
+               return rtrim($theDir, ' /');
+       }
+
+       /**
+        * Clean path with a filename
+        *
+        * Behaves similar to realpath() but doesn't check if path/file exists
+        *
+        * @param string $theDirAndFile Input string
+        * @return string Output string
+        * @see cleanDirectoryName()
+        */
+       static public function cleanDirectoryNameAndFile($theDirAndFile) {
+               $pos = strrpos($theDirAndFile, '/');
+               if ($pos === FALSE) {
+                       // no directory-name included
+                       return $theDirAndFile;
+               } else {
+                       $theDir = substr($theDirAndFile, 0, $pos);
+                       $theFile = substr($theDirAndFile, $pos + 1);
+                       $theDir = self::cleanDirectoryName($theDir);
+                       return $theDir . '/' . $theFile;
+               }
+       }
 }
 
 
index cb9410b..268f27e 100644 (file)
@@ -200,6 +200,87 @@ class PathUtilityTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                );
        }
 
+       /**
+        * Data provider for cleanDirectoryNameCorrectlyCleansName
+        *
+        * @return array
+        */
+       public function cleanDirectoryNameCorrectlyCleansNameDataProvider() {
+               return array(
+                       'removes single-dot-elements' => array(
+                               'abc/./def/././ghi',
+                               'abc/def/ghi'
+                       ),
+                       'removes ./ at beginning' => array(
+                               './abc/def/ghi',
+                               'abc/def/ghi'
+                       ),
+                       'removes double-slashes' => array(
+                               'abc//def/ghi',
+                               'abc/def/ghi'
+                       ),
+                       'removes double-slashes from front, but keeps absolute path' => array(
+                               '//abc/def/ghi',
+                               '/abc/def/ghi'
+                       ),
+                       'makes double-dot-elements go one level higher, test #1' => array(
+                               'abc/def/ghi/../..',
+                               'abc'
+                       ),
+                       'makes double-dot-elements go one level higher, test #2' => array(
+                               'abc/def/ghi/../123/456/..',
+                               'abc/def/123'
+                       ),
+                       'makes double-dot-elements go one level higher, test #3' => array(
+                               'abc/../../def/ghi',
+                               '../def/ghi'
+                       ),
+                       'truncates slash at the end' => array(
+                               'abc/def/ghi/',
+                               'abc/def/ghi'
+                       ),
+                       'keeps slash in front of absolute paths' => array(
+                               '/abc/def/ghi',
+                               '/abc/def/ghi'
+                       ),
+                       'keeps slash in front of absolute paths even if double-dot-elements want to go higher' => array(
+                               '/abc/../../def/ghi',
+                               '/def/ghi'
+                       ),
+                       'works with EXT-syntax-paths' => array(
+                               'EXT:abc/def/ghi/',
+                               'EXT:abc/def/ghi'
+                       ),
+                       'truncates ending slash with space' => array(
+                               'abc/def/ ',
+                               'abc/def'
+                       ),
+                       'truncates ending space' => array(
+                               'abc/def ',
+                               'abc/def'
+                       ),
+                       'truncates ending dot' => array(
+                               'abc/def/.',
+                               'abc/def'
+                       ),
+                       'does not truncates ending dot if part of name' => array(
+                               'abc/def.',
+                               'abc/def.'
+                       ),
+               );
+       }
+
+       /**
+        * @test
+        * @dataProvider cleanDirectoryNameCorrectlyCleansNameDataProvider
+        */
+       public function cleanDirectoryNameCorrectlyCleansName($inputName, $expectedResult) {
+               $this->assertEquals(
+                       $expectedResult,
+                       \TYPO3\CMS\Core\Utility\PathUtility::cleanDirectoryName($inputName)
+               );
+       }
+
 }
 
 ?>
\ No newline at end of file