PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.5
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.5
1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 trunk All 48 releases
← All changes | app/Services/FileSystem/Drivers/Local/LocalDriver.php +20 -8 1.3.19 → 1.6.5 View file →
@@ -6,8 +6,9 @@
6 6 use FluentCart\App\Helpers\Helper;
7 7 use FluentCart\App\Modules\StorageDrivers\BaseStorageDriver;
8 8 use FluentCart\App\Modules\StorageDrivers\Local\Local as LocalStorageDriver;
9 9 use FluentCart\App\Services\FileSystem\Drivers\BaseDriver;
10 +use FluentCart\App\Services\FileSystem\StoragePath;
10 11 use FluentCart\Framework\Support\Arr;
11 12 use FluentCart\Framework\Support\Str;
12 13
13 14 class LocalDriver extends BaseDriver
@@ -186,17 +187,19 @@
186 187 }
187 188
188 189 public function downloadFile(string $filePath, $fileName = null)
189 190 {
190 - $filePath = wp_normalize_path( $filePath );
191 191 $fileName = sanitize_file_name($fileName);
192 192
193 - $file = "{$this->dirPath}/{$filePath}";
193 + $file = $this->getFilePath($filePath);
194 194 if (ob_get_level()) {
195 195 ob_end_clean();
196 196 }
197 197
198 - if(!file_exists($file)) {
198 + // A path that escapes the storage directory reports as missing rather
199 + // than as rejected, so this never answers whether a file outside the
200 + // directory exists.
201 + if (!$file || !file_exists($file)) {
199 202 return new \WP_Error('file_not_found', __('File not found', 'fluent-cart'));
200 203 }
201 204 $fileSize = filesize($file);
202 205 $fileName = $fileName ?? basename($filePath);
@@ -211,11 +214,19 @@
211 214 }
212 215 exit;
213 216 }
214 217
218 + /**
219 + * Absolute path for a stored file, or '' when $filePath escapes the
220 + * storage directory.
221 + *
222 + * `..` survives both sanitize_text_field() and wp_normalize_path(), so
223 + * composing the path by concatenation alone let a relative path address any
224 + * file the web user could reach.
225 + */
215 226 public function getFilePath(string $filePath, $fileName = null): string
216 227 {
217 - return "{$this->dirPath}/{$filePath}";
228 + return StoragePath::contain($this->dirPath, $filePath);
218 229 }
219 230
220 231 protected function retrieveFileForDownload(string $downloadableFilePath)
221 232 {
@@ -228,15 +239,16 @@
228 239 if ( !current_user_can('manage_options') ) {
229 240 return new \WP_Error('permission_error', __('You are not allowed to delete file', 'fluent-cart'));
230 241 }
231 242
232 - $filePath = wp_normalize_path($filePath);
233 243 $fullPath = $this->getFilePath($filePath);
234 -
235 - if (!file_exists($fullPath)) {
244 +
245 + // Same as the download path: an escaping path reports as missing so the
246 + // response cannot be used to probe for files outside the directory.
247 + if (!$fullPath || !file_exists($fullPath)) {
236 248 return new \WP_Error('file_not_found', __('File not found', 'fluent-cart'));
237 249 }
238 -
250 +
239 251 if (!is_file($fullPath)) {
240 252 return new \WP_Error('not_a_file', __('Path is not a file', 'fluent-cart'));
241 253 }
242 254