diff --git a/modules/cms/classes/MediaLibrary.php b/modules/cms/classes/MediaLibrary.php index 2bfadb3ef..42fa3e968 100644 --- a/modules/cms/classes/MediaLibrary.php +++ b/modules/cms/classes/MediaLibrary.php @@ -125,9 +125,9 @@ class MediaLibrary /** * Finds files in the Library. * @param string $searchTerm Specifies the search term. - * @param string $sortBy Determines the sorting preference. + * @param string $sortBy Determines the sorting preference. * Supported values are 'title', 'size', 'lastModified' (see SORT_BY_XXX class constants). - * @param string $filter Determines the document type filtering preference. + * @param string $filter Determines the document type filtering preference. * Supported values are 'image', 'video', 'audio', 'document' (see FILE_TYPE_XXX constants of MediaLibraryItem class). * @return array Returns an array of MediaLibraryItem objects. */ @@ -384,10 +384,31 @@ class MediaLibrary if ($normalizeOnly) return $path; - if (strpos($path, '..') !== false) - throw new ApplicationException(Lang::get('cms::lang.media.invalid_path', ['path'=>$path])); + $regexDirectorySeparator = preg_quote(DIRECTORY_SEPARATOR, '/'); + $regexDot = preg_quote('.', '/'); + $regex = [ - if (strpos($path, './') !== false || strpos($path, '//') !== false) + /** + * Checks for parent or current directory reference at beginning of path + */ + '(^'.$regexDot.'+?'.$regexDirectorySeparator.')', + + /** + * Check for parent or current directory reference in middle of path + */ + '('.$regexDirectorySeparator.$regexDot.'+?'.$regexDirectorySeparator.')', + + /** + * Check for parent or current directory reference at end of path + */ + '('.$regexDirectorySeparator.$regexDot.'+?$)', + ]; + + /** + * Now, let's combine everything to one regex + */ + $regex = '/'.implode('|', $regex).'/'; + if (preg_match($regex, $path) !== 0 || strpos($path, '//') !== false) throw new ApplicationException(Lang::get('cms::lang.media.invalid_path', ['path'=>$path])); return $path; @@ -537,7 +558,7 @@ class MediaLibrary /** * Sorts the item list by title, size or last modified date. * @param array $itemList Specifies the item list to sort. - * @param string $sortBy Determines the sorting preference. + * @param string $sortBy Determines the sorting preference. * Supported values are 'title', 'size', 'lastModified' (see SORT_BY_XXX class constants). */ protected function sortItemList(&$itemList, $sortBy) @@ -567,7 +588,7 @@ class MediaLibrary /** * Filters item list by file type. * @param array $itemList Specifies the item list to sort. - * @param string $filter Determines the document type filtering preference. + * @param string $filter Determines the document type filtering preference. * Supported values are 'image', 'video', 'audio', 'document' (see FILE_TYPE_XXX constants of MediaLibraryItem class). */ protected function filterItemList(&$itemList, $filter) @@ -586,7 +607,7 @@ class MediaLibrary /** * Initializes and returns the Media Library disk. - * This method should always be used instead of trying to access the + * This method should always be used instead of trying to access the * $storageDisk property directly as initializing the disc requires * communicating with the remote storage. * @return mixed Returns the storage disk object. diff --git a/modules/cms/widgets/MediaManager.php b/modules/cms/widgets/MediaManager.php index 2114c47e5..d84e07acb 100644 --- a/modules/cms/widgets/MediaManager.php +++ b/modules/cms/widgets/MediaManager.php @@ -945,6 +945,25 @@ class MediaManager extends WidgetBase } } + /** + * Creates a slug form the string. A modified version of Laravel's Str::slug + * with the main difference that it accepts @-signs + * @param string + * @return string + */ + public static function slug($string) + { + $title = Str::ascii($title); + // Convert all dashes/underscores into separator + $flip = $separator == '-' ? '_' : '-'; + $title = preg_replace('!['.preg_quote($flip).']+!u', $separator, $title); + // Remove all characters that are not the separator, letters, numbers, whitespace or @. + $title = preg_replace('![^'.preg_quote($separator).'\pL\pN\s@]+!u', '', mb_strtolower($title)); + // Replace all separator characters and whitespace by a single separator + $title = preg_replace('!['.preg_quote($separator).'\s]+!u', $separator, $title); + return trim($title, $separator); + } + protected function checkUploadPostback() { $fileName = null; @@ -973,7 +992,7 @@ class MediaManager extends WidgetBase * File name contains non-latin characters, attempt to slug the value */ if (!$this->validateFileName($fileName)) { - $fileNameSlug = Str::slug(File::name($fileName), '-'); + $fileNameSlug = static::slug(File::name($fileName), '-'); $fileName = $fileNameSlug.'.'.$extension; } @@ -1001,7 +1020,7 @@ class MediaManager extends WidgetBase protected function validateFileName($name) { - if (!preg_match('/^[0-9a-z\.\s_\-]+$/i', $name)) { + if (!preg_match('/^[0-9a-z@\.\s_\-]+$/i', $name)) { return false; } diff --git a/tests/unit/cms/classes/MediaLibraryTest.php b/tests/unit/cms/classes/MediaLibraryTest.php new file mode 100644 index 000000000..5ce1ecb67 --- /dev/null +++ b/tests/unit/cms/classes/MediaLibraryTest.php @@ -0,0 +1,55 @@ +setExpectedException('ApplicationException'); + MediaLibrary::validatePath($path); + } + + /** + * @dataProvider validPathsProvider + */ + public function testValidPathsOnValidatePath($path) + { + MediaLibrary::validatePath($path); + } +}