Phauthentic /
file-storage
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
| 1 | <?php |
||||
| 2 | |||||
| 3 | /** |
||||
| 4 | * Copyright (c) Florian Krämer (https://florian-kraemer.net) |
||||
| 5 | * Licensed under The MIT License |
||||
| 6 | * For full copyright and license information, please see the LICENSE.txt |
||||
| 7 | * Redistributions of files must retain the above copyright notice. |
||||
| 8 | * |
||||
| 9 | * @copyright Copyright (c) Florian Krämer (https://florian-kraemer.net) |
||||
| 10 | * @author Florian Krämer |
||||
| 11 | * @link https://github.com/Phauthentic |
||||
| 12 | * @license https://opensource.org/licenses/MIT MIT License |
||||
| 13 | */ |
||||
| 14 | |||||
| 15 | declare(strict_types=1); |
||||
| 16 | |||||
| 17 | namespace Phauthentic\Infrastructure\Storage\Utility; |
||||
| 18 | |||||
| 19 | /** |
||||
| 20 | * Filename Sanitizer |
||||
| 21 | * |
||||
| 22 | * @link https://stackoverflow.com/questions/2021624/string-sanitizer-for-filename |
||||
| 23 | */ |
||||
| 24 | class FilenameSanitizer implements FilenameSanitizerInterface |
||||
| 25 | { |
||||
| 26 | /** |
||||
| 27 | * @var array |
||||
| 28 | */ |
||||
| 29 | protected array $config = []; |
||||
| 30 | |||||
| 31 | /** |
||||
| 32 | * @var array |
||||
| 33 | */ |
||||
| 34 | protected array $defaultConfig = [ |
||||
| 35 | 'lowercase' => false, |
||||
| 36 | 'removeAllNonAlphaNumerical' => false, |
||||
| 37 | 'beautify' => true, |
||||
| 38 | 'enforceMaxLength' => true, |
||||
| 39 | 'maxLength' => 255, |
||||
| 40 | 'removeControlChars' => true, |
||||
| 41 | 'removeNonPrintingChars' => true, |
||||
| 42 | 'removeUriReservedChars' => false, |
||||
| 43 | 'urlSafe' => false, |
||||
| 44 | ]; |
||||
| 45 | |||||
| 46 | /** |
||||
| 47 | * File system reserved characters |
||||
| 48 | * |
||||
| 49 | * @link https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words |
||||
| 50 | * @var string |
||||
| 51 | */ |
||||
| 52 | protected string $filesystemReservedChars = '[<>:"/\\|?*]'; |
||||
| 53 | |||||
| 54 | /** |
||||
| 55 | * URL unsafe characters |
||||
| 56 | * |
||||
| 57 | * @link https://www.ietf.org/rfc/rfc1738.txt |
||||
| 58 | * @var string |
||||
| 59 | */ |
||||
| 60 | protected string $urlUnsafeChars = '[{}^\~`]'; |
||||
| 61 | |||||
| 62 | /** |
||||
| 63 | * @link https://tools.ietf.org/html/rfc3986#section-2.2 |
||||
| 64 | * @var string |
||||
| 65 | */ |
||||
| 66 | protected string $uriReservedChars = '[#\[\]@!$&\'()+,;=]'; |
||||
| 67 | |||||
| 68 | /** |
||||
| 69 | * Non-printing characters DEL, NO-BREAK SPACE, SOFT HYPHEN |
||||
| 70 | * |
||||
| 71 | * @var string |
||||
| 72 | */ |
||||
| 73 | protected string $nonPrintingChars = '[\x7F\xA0\xAD]'; |
||||
| 74 | |||||
| 75 | /** |
||||
| 76 | * Control Characters |
||||
| 77 | * |
||||
| 78 | * @link http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx |
||||
| 79 | * @var string |
||||
| 80 | */ |
||||
| 81 | protected string $controlChars = '[\x00-\x1F]'; |
||||
| 82 | |||||
| 83 | /** |
||||
| 84 | * @param array $config Config array |
||||
| 85 | */ |
||||
| 86 | 9 | public function __construct(array $config = []) |
|||
| 87 | { |
||||
| 88 | 9 | $this->config = $config + $this->defaultConfig; |
|||
| 89 | 9 | } |
|||
| 90 | |||||
| 91 | /** |
||||
| 92 | * @param string $filename Filename |
||||
| 93 | * @param string $replacement Replacement character |
||||
| 94 | * @return string |
||||
| 95 | */ |
||||
| 96 | 8 | protected function replaceCharacters(string $filename, string $replacement = '-'): string |
|||
| 97 | { |
||||
| 98 | 8 | $regex = []; |
|||
| 99 | 8 | $regex[] = $this->filesystemReservedChars; |
|||
| 100 | 8 | $regex[] = $this->config['urlSafe'] === true ? $this->urlUnsafeChars : ''; |
|||
| 101 | 8 | $regex[] = $this->config['removeUriReservedChars'] === true ? $this->uriReservedChars : ''; |
|||
| 102 | 8 | $regex[] = $this->config['removeNonPrintingChars'] === true ? $this->nonPrintingChars : ''; |
|||
| 103 | 8 | $regex[] = $this->config['removeControlChars'] === true ? $this->controlChars : ''; |
|||
| 104 | 8 | $regex = '~' . implode('|', array_filter($regex)) . '~x'; |
|||
| 105 | |||||
| 106 | 8 | return (string)preg_replace($regex, $replacement, $filename); |
|||
| 107 | } |
||||
| 108 | |||||
| 109 | /** |
||||
| 110 | * @param string $string String |
||||
| 111 | * @return string |
||||
| 112 | */ |
||||
| 113 | 8 | public function sanitize(string $string): string |
|||
| 114 | { |
||||
| 115 | 8 | $string = $this->replaceCharacters($string); |
|||
| 116 | |||||
| 117 | 8 | if ($this->config['lowercase'] === true) { |
|||
| 118 | 1 | $string = $this->stringToLowerCase($string); |
|||
| 119 | } |
||||
| 120 | |||||
| 121 | 8 | if ($this->config['removeAllNonAlphaNumerical']) { |
|||
| 122 | 1 | $string = $this->removeAllNonAlphaNumerical($string); |
|||
| 123 | } |
||||
| 124 | |||||
| 125 | 8 | if ($this->config['beautify'] === true) { |
|||
| 126 | 8 | $string = $this->beautify($string); |
|||
| 127 | } |
||||
| 128 | |||||
| 129 | 8 | if ($this->config['enforceMaxLength'] === true) { |
|||
| 130 | 8 | $string = $this->enforceMaxLength($string, $this->config['maxLength']); |
|||
| 131 | } |
||||
| 132 | |||||
| 133 | 8 | return $string; |
|||
| 134 | } |
||||
| 135 | |||||
| 136 | /** |
||||
| 137 | * Enforces the max length of a filename |
||||
| 138 | * |
||||
| 139 | * @link http://en.wikipedia.org/wiki/Comparison_of_file_systems#Limits |
||||
| 140 | * @link http://serverfault.com/a/9548/44086 |
||||
| 141 | * @param string $filename Filename |
||||
| 142 | * @param int $maxLength Max length, 255 by default |
||||
| 143 | * @return string |
||||
| 144 | */ |
||||
| 145 | 8 | protected function enforceMaxLength(string $filename, int $maxLength = 255): string |
|||
| 146 | { |
||||
| 147 | 8 | $ext = pathinfo($filename, PATHINFO_EXTENSION); |
|||
| 148 | 8 | $length = $maxLength - ($ext ? strlen($ext) + 1 : 0); |
|||
|
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||||
| 149 | |||||
| 150 | 8 | $filename = mb_strcut( |
|||
| 151 | 8 | pathinfo($filename, PATHINFO_FILENAME), |
|||
|
0 ignored issues
–
show
It seems like
pathinfo($filename, Phau...lity\PATHINFO_FILENAME) can also be of type array; however, parameter $string of mb_strcut() does only seem to accept string, maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 152 | 8 | 0, |
|||
| 153 | $length, |
||||
| 154 | 8 | mb_detect_encoding($filename) |
|||
| 155 | ); |
||||
| 156 | |||||
| 157 | 8 | return $filename . ($ext ? '.' . $ext : ''); |
|||
|
0 ignored issues
–
show
Are you sure
$ext of type array|string can be used in concatenation?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 158 | } |
||||
| 159 | |||||
| 160 | /** |
||||
| 161 | * Beautifies a filename to make it better to read |
||||
| 162 | * |
||||
| 163 | * "file name.zip" becomes "file-name.zip" |
||||
| 164 | * "file___name.zip" becomes "file-name.zip" |
||||
| 165 | * "file---name.zip" becomes "file-name.zip" |
||||
| 166 | * "file--.--.-.--name.zip" becomes "file.name.zip" |
||||
| 167 | * "file...name..zip" becomes "file.name.zip" |
||||
| 168 | * ".file-name.-" becomes "file-name" |
||||
| 169 | * |
||||
| 170 | * @link https://stackoverflow.com/questions/2021624/string-sanitizer-for-filename |
||||
| 171 | * @param string $filename Filename |
||||
| 172 | * @return string |
||||
| 173 | */ |
||||
| 174 | 9 | public function beautify(string $filename): string |
|||
| 175 | { |
||||
| 176 | // reduce consecutive characters |
||||
| 177 | 9 | $filename = (string)preg_replace([ |
|||
| 178 | // "file name.zip" becomes "file-name.zip" |
||||
| 179 | 9 | '/ +/', |
|||
| 180 | // "file___name.zip" becomes "file-name.zip" |
||||
| 181 | '/_+/', |
||||
| 182 | // "file---name.zip" becomes "file-name.zip" |
||||
| 183 | '/-+/' |
||||
| 184 | 9 | ], '-', $filename); |
|||
| 185 | |||||
| 186 | 9 | $filename = (string)preg_replace([ |
|||
| 187 | // "file--.--.-.--name.zip" becomes "file.name.zip" |
||||
| 188 | 9 | '/-*\.-*/', |
|||
| 189 | // "file...name..zip" becomes "file.name.zip" |
||||
| 190 | '/\.{2,}/' |
||||
| 191 | 9 | ], '.', $filename); |
|||
| 192 | |||||
| 193 | // lowercase for windows/unix interoperability http://support.microsoft.com/kb/100625 |
||||
| 194 | 9 | $filename = mb_strtolower($filename, mb_detect_encoding($filename)); |
|||
| 195 | |||||
| 196 | // ".file-name.-" becomes "file-name" |
||||
| 197 | 9 | $filename = trim($filename, '.-'); |
|||
| 198 | |||||
| 199 | 9 | return $filename; |
|||
| 200 | } |
||||
| 201 | |||||
| 202 | /** |
||||
| 203 | * @param string $string String |
||||
| 204 | * @return string |
||||
| 205 | */ |
||||
| 206 | 1 | protected function removeAllNonAlphaNumerical(string $string): string |
|||
| 207 | { |
||||
| 208 | 1 | $pathInfo = PathInfo::for($string); |
|||
| 209 | 1 | $string = (string)preg_replace('/[^a-zA-Z0-9]/', '', $pathInfo->filename()); |
|||
| 210 | |||||
| 211 | 1 | if (!$pathInfo->hasExtension()) { |
|||
| 212 | return $string; |
||||
| 213 | } |
||||
| 214 | |||||
| 215 | 1 | return $string . '.' . $pathInfo->extension(); |
|||
| 216 | } |
||||
| 217 | |||||
| 218 | /** |
||||
| 219 | * @param string $string String |
||||
| 220 | * @param string $encoding Encoding |
||||
| 221 | * @return string |
||||
| 222 | */ |
||||
| 223 | 1 | protected function stringToLowerCase( |
|||
| 224 | string $string, |
||||
| 225 | string $encoding = 'UTF-8' |
||||
| 226 | ): string { |
||||
| 227 | 1 | return ((function_exists('mb_strtolower')) ? |
|||
| 228 | 1 | mb_strtolower($string, $encoding) : |
|||
| 229 | 1 | strtolower($string) |
|||
| 230 | ); |
||||
| 231 | } |
||||
| 232 | } |
||||
| 233 |