PluginProbe
Elementor Website Builder – more than just a page builder / 3.6.7
Elementor Website Builder – more than just a page builder v3.6.7
4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 4.0.7 All 451 releases
← All changes | core/files/uploads-manager.php +14 -158 4.1.0-dev13.6.7 View file →
@@ -6,12 +6,9 @@
6 6 use Elementor\Core\Files\File_Types\Base as File_Type_Base;
7 7 use Elementor\Core\Files\File_Types\Json;
8 8 use Elementor\Core\Files\File_Types\Svg;
9 9 use Elementor\Core\Files\File_Types\Zip;
10 -use Elementor\Core\Files\Fonts\Google_Font;
11 10 use Elementor\Core\Utils\Exceptions;
12 -use Elementor\Fonts;
13 -use Elementor\User;
14 11
15 12 if ( ! defined( 'ABSPATH' ) ) {
16 13 exit; // Exit if accessed directly.
17 14 }
@@ -26,9 +23,8 @@
26 23 class Uploads_Manager extends Base_Object {
27 24
28 25 const UNFILTERED_FILE_UPLOADS_KEY = 'elementor_unfiltered_files_upload';
29 26 const INVALID_FILE_CONTENT = 'Invalid Content In File';
30 - const ELEMENTOR_UPLOAD_DIR = 'elementor';
31 27
32 28 /**
33 29 * @var File_Type_Base[]
34 30 */
@@ -75,9 +71,9 @@
75 71 * @since 3.3.0
76 72 * @access public
77 73 *
78 74 * @param string $file_path
79 - * @param array $allowed_file_types
75 + * @param array $allowed_file_types
80 76 * @return array|\WP_Error
81 77 */
82 78 public function extract_and_validate_zip( $file_path, $allowed_file_types = null ) {
83 79 $result = [];
@@ -87,12 +83,8 @@
87 83
88 84 // Returns an array of file paths.
89 85 $extracted = $zip_handler->extract( $file_path, $allowed_file_types );
90 86
91 - if ( is_wp_error( $extracted ) ) {
92 - return $extracted;
93 - }
94 -
95 87 // If there are no extracted file names, no files passed the extraction validation.
96 88 if ( empty( $extracted['files'] ) ) {
97 89 // TODO: Decide what to do if no files passed the extraction validation
98 90 return new \WP_Error( 'file_error', self::INVALID_FILE_CONTENT );
@@ -122,53 +114,31 @@
122 114 *
123 115 * @since 3.3.0
124 116 * @access public
125 117 *
126 - * @param array $data
118 + * @param array $file
127 119 * @param array $allowed_file_extensions Optional. an array of file types that are allowed to pass validation for each
128 120 * upload.
129 121 * @return array|\WP_Error
130 122 */
131 - public function handle_elementor_upload( array $data, $allowed_file_extensions = null ) {
123 + public function handle_elementor_upload( array $file, $allowed_file_extensions = null ) {
132 124 // If $file['fileData'] is set, it signals that the passed file is a Base64 string that needs to be decoded and
133 125 // saved to a temporary file.
134 - if ( isset( $data['fileData'] ) ) {
135 - $data = $this->save_base64_to_tmp_file( $data, $allowed_file_extensions );
126 + if ( isset( $file['fileData'] ) ) {
127 + $file = $this->save_base64_to_tmp_file( $file );
136 128 }
137 129
138 - if ( is_wp_error( $data ) ) {
139 - return $data;
140 - }
130 + $validation_result = $this->validate_file( $file, $allowed_file_extensions );
141 131
142 - if ( ! isset( $data['fileData'] ) ) {
143 - if ( empty( $data['tmp_name'] ) ) {
144 - return new \WP_Error( 'file_error', esc_html__( 'Invalid temporary file path.', 'elementor' ) );
145 - }
146 -
147 - // Path validation only applies to direct calls (e.g. import_template) where
148 - // tmp_name originates from user input. When is_elementor_upload is true, this
149 - // method is used as a WordPress filter (wp_handle_sideload_prefilter) and
150 - // tmp_name is set by WordPress core.
151 - if ( ! $this->is_elementor_upload && ! $this->is_path_in_allowed_dir( $data['tmp_name'] ) ) {
152 - return new \WP_Error( 'file_error', esc_html__( 'Invalid temporary file path.', 'elementor' ) );
153 - }
154 - }
155 -
156 - $validation_result = $this->validate_file( $data, $allowed_file_extensions );
157 -
158 132 if ( is_wp_error( $validation_result ) ) {
159 - if ( ! empty( $data['tmp_name'] ) ) {
160 - $this->remove_file_or_dir( dirname( $data['tmp_name'] ) );
161 - }
162 -
163 133 return $validation_result;
164 134 }
165 135
166 - return $data;
136 + return $file;
167 137 }
168 138
169 139 /**
170 - * Is Unfiltered Uploads Enabled
140 + * are Unfiltered Uploads Enabled
171 141 *
172 142 * @since 3.5.0
173 143 * @access public
174 144 *
@@ -174,11 +144,9 @@
174 144 *
175 145 * @return bool
176 146 */
177 147 final public static function are_unfiltered_uploads_enabled() {
178 - $enabled = (bool) get_option( self::UNFILTERED_FILE_UPLOADS_KEY )
179 - && Svg::file_sanitizer_can_run()
180 - && User::is_current_user_can_upload_json();
148 + $enabled = ! ! get_option( self::UNFILTERED_FILE_UPLOADS_KEY ) && Svg::file_sanitizer_can_run();
181 149
182 150 /**
183 151 * Allow Unfiltered Files Upload.
184 152 *
@@ -272,58 +240,11 @@
272 240 return $data;
273 241 }
274 242
275 243 /**
276 - * Check if path is within the allowed Elementor uploads directory.
277 - *
278 - * Prevents path traversal and arbitrary directory deletion by ensuring the path
279 - * resolves under wp-content/uploads/elementor/ or under the configured temp dir
280 - * (elementor/files/temp-dir filter), so that cleanup works when temp dir is customized.
281 - *
282 - * @since 3.35.4
283 - * @access private
284 - *
285 - * @param string $path
286 - * @return bool
287 - */
288 - private function is_path_in_allowed_dir( $path ) {
289 - if ( ! is_string( $path ) || '' === $path ) {
290 - return false;
291 - }
292 -
293 - $real_path = realpath( $path );
294 -
295 - if ( false === $real_path ) {
296 - $real_path = realpath( dirname( $path ) );
297 - if ( false === $real_path ) {
298 - return false;
299 - }
300 - }
301 -
302 - $wp_upload_dir = wp_upload_dir();
303 - $elementor_base = realpath( $wp_upload_dir['basedir'] . DIRECTORY_SEPARATOR . self::ELEMENTOR_UPLOAD_DIR );
304 -
305 - if ( false !== $elementor_base ) {
306 - $allowed = $real_path === $elementor_base || 0 === strpos( $real_path, $elementor_base . DIRECTORY_SEPARATOR );
307 - if ( $allowed ) {
308 - return true;
309 - }
310 - }
311 -
312 - $temp_dir = realpath( $this->get_temp_dir() );
313 - if ( false !== $temp_dir ) {
314 - $temp_dir = rtrim( $temp_dir, DIRECTORY_SEPARATOR ) . DIRECTORY_SEPARATOR;
315 - return 0 === strpos( $real_path, $temp_dir );
316 - }
317 -
318 - return false;
319 - }
320 -
321 - /**
322 244 * Remove File Or Directory
323 245 *
324 246 * Directory is deleted recursively with all of its contents (subdirectories and files).
325 - * Only paths under wp-content/uploads/elementor/ are allowed (security: prevents arbitrary directory deletion).
326 247 *
327 248 * @since 3.3.0
328 249 * @access public
329 250 *
@@ -329,15 +250,11 @@
329 250 *
330 251 * @param string $path
331 252 */
332 253 public function remove_file_or_dir( $path ) {
333 - if ( ! $this->is_path_in_allowed_dir( $path ) ) {
334 - return;
335 - }
336 -
337 254 if ( is_dir( $path ) ) {
338 255 $this->remove_directory_with_files( $path );
339 - } elseif ( is_file( $path ) ) {
256 + } else {
340 257 unlink( $path );
341 258 }
342 259 }
343 260
@@ -353,27 +270,10 @@
353 270 * @param string $file_name
354 271 * @return string|\WP_Error
355 272 */
356 273 public function create_temp_file( $file_content, $file_name ) {
357 - $file_name = str_replace( ' ', '', sanitize_file_name( $file_name ) );
358 -
359 - if ( empty( $file_name ) ) {
360 - return new \WP_Error( 'invalid_file_name', esc_html__( 'Invalid file name.', 'elementor' ) );
361 - }
362 -
363 274 $temp_filename = $this->create_unique_dir() . $file_name;
364 275
365 - /**
366 - * Temp File Path
367 - *
368 - * Allows modifying the full path of the temporary file.
369 - *
370 - * @since 3.7.0
371 - *
372 - * @param string full path to file
373 - */
374 - $temp_filename = apply_filters( 'elementor/files/temp-file-path', $temp_filename );
375 -
376 276 file_put_contents( $temp_filename, $file_content ); // phpcs:ignore
377 277
378 278 return $temp_filename;
379 279 }
@@ -391,21 +291,10 @@
391 291 public function get_temp_dir() {
392 292 if ( ! $this->temp_dir ) {
393 293 $wp_upload_dir = wp_upload_dir();
394 294
395 - $temp_dir = implode( DIRECTORY_SEPARATOR, [ $wp_upload_dir['basedir'], self::ELEMENTOR_UPLOAD_DIR, 'tmp' ] ) . DIRECTORY_SEPARATOR;
295 + $this->temp_dir = implode( DIRECTORY_SEPARATOR, [ $wp_upload_dir['basedir'], 'elementor', 'tmp' ] ) . DIRECTORY_SEPARATOR;
396 296
397 - /**
398 - * Temp File Path
399 - *
400 - * Allows modifying the full path of the temporary file.
401 - *
402 - * @since 3.7.0
403 - *
404 - * @param string temporary directory
405 - */
406 - $this->temp_dir = apply_filters( 'elementor/files/temp-dir', $temp_dir );
407 -
408 297 if ( ! is_dir( $this->temp_dir ) ) {
409 298 wp_mkdir_p( $this->temp_dir );
410 299 }
411 300 }
@@ -443,9 +332,8 @@
443 332 * @param Ajax $ajax
444 333 */
445 334 public function register_ajax_actions( Ajax $ajax ) {
446 335 $ajax->register_ajax_action( 'enable_unfiltered_files_upload', [ $this, 'enable_unfiltered_files_upload' ] );
447 - $ajax->register_ajax_action( 'enqueue_google_fonts', [ $this, 'ajax_enqueue_google_fonts' ] );
448 336 }
449 337
450 338 /**
451 339 * Set Unfiltered Files Upload
@@ -460,24 +348,8 @@
460 348
461 349 update_option( self::UNFILTERED_FILE_UPLOADS_KEY, 1 );
462 350 }
463 351
464 - public function ajax_enqueue_google_fonts( $data ): bool {
465 - if ( empty( $data['font_name'] ) ) {
466 - return false;
467 - }
468 -
469 - $font_type = Fonts::get_font_type( $data['font_name'] );
470 -
471 - if ( Fonts::GOOGLE !== $font_type ) {
472 - return false;
473 - }
474 -
475 - Google_Font::enqueue( $data['font_name'] );
476 -
477 - return true;
478 - }
479 -
480 352 /**
481 353 * Support Unfiltered File Uploads
482 354 *
483 355 * When uploading a file within Elementor, this method adds the registered
@@ -584,24 +456,11 @@
584 456 * @since 3.3.0
585 457 * @access private
586 458 *
587 459 * @param $file
588 - * @param array|null $allowed_file_extensions
589 - *
590 460 * @return array|\WP_Error
591 461 */
592 - private function save_base64_to_tmp_file( $file, $allowed_file_extensions = null ) {
593 - if ( empty( $file['fileName'] ) || empty( $file['fileData'] ) ) {
594 - return new \WP_Error( 'file_error', self::INVALID_FILE_CONTENT );
595 - }
596 -
597 - $file_extension = pathinfo( $file['fileName'], PATHINFO_EXTENSION );
598 - $is_file_type_allowed = $this->is_file_type_allowed( $file_extension, $allowed_file_extensions );
599 -
600 - if ( is_wp_error( $is_file_type_allowed ) ) {
601 - return $is_file_type_allowed;
602 - }
603 -
462 + private function save_base64_to_tmp_file( $file ) {
604 463 $file_content = base64_decode( $file['fileData'] ); // phpcs:ignore
605 464
606 465 // If the decode fails
607 466 if ( ! $file_content ) {
@@ -654,12 +513,9 @@
654 513
655 514 // If there is a File Type Handler for the uploaded file, it means it is a non-standard file type. In this case,
656 515 // we check if unfiltered file uploads are enabled or not before allowing it.
657 516 if ( ! self::are_unfiltered_uploads_enabled() ) {
658 - $error = 'json' === $file_extension
659 - ? esc_html__( 'You do not have permission to upload JSON files.', 'elementor' )
660 - : esc_html__( 'This file is not allowed for security reasons.', 'elementor' );
661 - return new \WP_Error( Exceptions::FORBIDDEN, $error );
517 + return new \WP_Error( Exceptions::FORBIDDEN, esc_html__( 'This file is not allowed for security reasons.', 'elementor' ) );
662 518 }
663 519
664 520 // Here is each file type handler's chance to run its own specific validations
665 521 return $file_type_handler->validate_file( $file );
@@ -727,9 +583,9 @@
727 583
728 584 foreach ( new \RecursiveIteratorIterator( $dir_iterator, \RecursiveIteratorIterator::CHILD_FIRST ) as $name => $item ) {
729 585 if ( is_dir( $name ) ) {
730 586 rmdir( $name );
731 - } elseif ( is_file( $name ) ) {
587 + } else {
732 588 unlink( $name );
733 589 }
734 590 }
735 591