| @@ -1,34 +1,25 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | namespace Elementor\Core\Files\Assets\Svg; |
| 3 | 3 | |
| 4 | 4 | use Elementor\Core\Files\Assets\Files_Upload_Handler; |
| 5 | -use Elementor\Core\Files\File_Types\Svg; | |
| 6 | -use Elementor\Core\Files\Uploads_Manager; | |
| 7 | -use Elementor\Plugin; | |
| 8 | 5 | |
| 9 | 6 | if ( ! defined( 'ABSPATH' ) ) { |
| 10 | 7 | exit; // Exit if accessed directly. |
| 11 | 8 | } |
| 12 | 9 | |
| 13 | -/** | |
| 14 | - * SVG Handler | |
| 15 | - * | |
| 16 | - * @deprecated 3.5.0 Use `Elementor\Core\Files\File_Types\Svg` instead, accessed by calling: `Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' );` | |
| 17 | - */ | |
| 18 | 10 | class Svg_Handler extends Files_Upload_Handler { |
| 19 | - | |
| 20 | 11 | /** |
| 21 | 12 | * Inline svg attachment meta key |
| 22 | - * | |
| 23 | - * @deprecated 3.5.0 | |
| 24 | 13 | */ |
| 25 | 14 | const META_KEY = '_elementor_inline_svg'; |
| 26 | 15 | |
| 16 | + const SCRIPT_REGEX = '/(?:\w+script|data):/xi'; | |
| 17 | + | |
| 27 | 18 | /** |
| 28 | - * @deprecated 3.5.0 | |
| 19 | + * @var \DOMDocument | |
| 29 | 20 | */ |
| 30 | - const SCRIPT_REGEX = '/(?:\w+script|data):/xi'; | |
| 21 | + private $svg_dom = null; | |
| 31 | 22 | |
| 32 | 23 | /** |
| 33 | 24 | * Attachment ID. |
| 34 | 25 | * |
| @@ -33,146 +24,561 @@ | ||
| 33 | 24 | * Attachment ID. |
| 34 | 25 | * |
| 35 | 26 | * Holds the current attachment ID. |
| 36 | 27 | * |
| 37 | - * @deprecated 3.5.0 | |
| 38 | - * | |
| 39 | 28 | * @var int |
| 40 | 29 | */ |
| 41 | 30 | private $attachment_id; |
| 42 | 31 | |
| 43 | - /** | |
| 44 | - * @deprecated 3.5.0 | |
| 45 | - */ | |
| 46 | 32 | public static function get_name() { |
| 47 | 33 | return 'svg-handler'; |
| 48 | 34 | } |
| 49 | 35 | |
| 50 | 36 | /** |
| 51 | - * Get meta | |
| 52 | - * | |
| 53 | - * @deprecated 3.5.0 | |
| 54 | - * | |
| 37 | + * get_meta | |
| 55 | 38 | * @return mixed |
| 56 | 39 | */ |
| 57 | 40 | protected function get_meta() { |
| 58 | - Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0' ); | |
| 59 | - | |
| 60 | 41 | return get_post_meta( $this->attachment_id, self::META_KEY, true ); |
| 61 | 42 | } |
| 62 | 43 | |
| 63 | 44 | /** |
| 64 | - * Update meta | |
| 65 | - * | |
| 66 | - * @deprecated 3.5.0 | |
| 67 | - * | |
| 45 | + * update_meta | |
| 68 | 46 | * @param $meta |
| 69 | 47 | */ |
| 70 | 48 | protected function update_meta( $meta ) { |
| 71 | - Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0' ); | |
| 72 | - | |
| 73 | 49 | update_post_meta( $this->attachment_id, self::META_KEY, $meta ); |
| 74 | 50 | } |
| 75 | 51 | |
| 76 | 52 | /** |
| 77 | - * Delete meta | |
| 78 | - * | |
| 79 | - * @deprecated 3.5.0 | |
| 53 | + * delete_meta | |
| 80 | 54 | */ |
| 81 | 55 | protected function delete_meta() { |
| 82 | - Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0' ); | |
| 83 | - | |
| 84 | 56 | delete_post_meta( $this->attachment_id, self::META_KEY ); |
| 85 | 57 | } |
| 86 | 58 | |
| 87 | - /** | |
| 88 | - * Get mime type | |
| 89 | - * | |
| 90 | - * @deprecated 3.5.0 | |
| 91 | - */ | |
| 92 | 59 | public function get_mime_type() { |
| 93 | 60 | return 'image/svg+xml'; |
| 94 | 61 | } |
| 95 | 62 | |
| 96 | - /** | |
| 97 | - * Get file type | |
| 98 | - * | |
| 99 | - * @deprecated 3.5.0 | |
| 100 | - */ | |
| 101 | 63 | public function get_file_type() { |
| 102 | 64 | return 'svg'; |
| 103 | 65 | } |
| 104 | 66 | |
| 105 | 67 | /** |
| 106 | - * Delete meta cache | |
| 107 | - * | |
| 108 | - * @deprecated 3.5.0 Use `Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' )->delete_meta_cache()` instead. | |
| 68 | + * delete_meta_cache | |
| 109 | 69 | */ |
| 110 | 70 | public function delete_meta_cache() { |
| 111 | - Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Plugin::$instance->uploads_manager->get_file_type_handlers( \'svg\' )->delete_meta_cache()' ); | |
| 71 | + delete_post_meta_by_key( self::META_KEY ); | |
| 72 | + } | |
| 112 | 73 | |
| 113 | - /** @var Svg $svg_handler */ | |
| 114 | - $svg_handler = Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' ); | |
| 115 | - | |
| 116 | - $svg_handler->delete_meta_cache(); | |
| 74 | + /** | |
| 75 | + * read_from_file | |
| 76 | + * @return bool|string | |
| 77 | + */ | |
| 78 | + public function read_from_file() { | |
| 79 | + return file_get_contents( get_attached_file( $this->attachment_id ) ); | |
| 117 | 80 | } |
| 118 | 81 | |
| 119 | 82 | /** |
| 120 | - * Get inline svg | |
| 121 | - * | |
| 122 | - * @deprecated 3.5.0 Use `Elementor\Core\Files\File_Types\Svg::get_inline_svg()` instead. | |
| 123 | - * | |
| 83 | + * get_inline_svg | |
| 124 | 84 | * @param $attachment_id |
| 125 | 85 | * |
| 126 | 86 | * @return bool|mixed|string |
| 127 | 87 | */ |
| 128 | 88 | public static function get_inline_svg( $attachment_id ) { |
| 129 | - Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Elementor\Core\Files\File_Types\Svg::get_inline_svg()' ); | |
| 89 | + $svg = get_post_meta( $attachment_id, self::META_KEY, true ); | |
| 130 | 90 | |
| 131 | - return Svg::get_inline_svg( $attachment_id ); | |
| 91 | + if ( ! empty( $svg ) ) { | |
| 92 | + return $svg; | |
| 93 | + } | |
| 94 | + | |
| 95 | + $attachment_file = get_attached_file( $attachment_id ); | |
| 96 | + | |
| 97 | + if ( ! $attachment_file ) { | |
| 98 | + return ''; | |
| 99 | + } | |
| 100 | + | |
| 101 | + $svg = file_get_contents( $attachment_file ); | |
| 102 | + | |
| 103 | + if ( ! empty( $svg ) ) { | |
| 104 | + update_post_meta( $attachment_id, self::META_KEY, $svg ); | |
| 105 | + } | |
| 106 | + | |
| 107 | + return $svg; | |
| 132 | 108 | } |
| 133 | 109 | |
| 134 | 110 | /** |
| 135 | - * Sanitize svg | |
| 111 | + * decode_svg | |
| 112 | + * @param $content | |
| 136 | 113 | * |
| 137 | - * @deprecated 3.5.0 Use `Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' )->delete_meta_cache()->sanitize_svg()` instead. | |
| 114 | + * @return string | |
| 115 | + */ | |
| 116 | + private function decode_svg( $content ) { | |
| 117 | + return gzdecode( $content ); | |
| 118 | + } | |
| 119 | + | |
| 120 | + /** | |
| 121 | + * encode_svg | |
| 122 | + * @param $content | |
| 138 | 123 | * |
| 124 | + * @return string | |
| 125 | + */ | |
| 126 | + private function encode_svg( $content ) { | |
| 127 | + return gzencode( $content ); | |
| 128 | + } | |
| 129 | + | |
| 130 | + /** | |
| 131 | + * sanitize_svg | |
| 139 | 132 | * @param $filename |
| 140 | 133 | * |
| 141 | 134 | * @return bool |
| 142 | 135 | */ |
| 143 | 136 | public function sanitize_svg( $filename ) { |
| 144 | - Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Plugin::$instance->uploads_manager->get_file_type_handlers( \'svg\' )->delete_meta_cache()->sanitize_svg()' ); | |
| 137 | + $original_content = file_get_contents( $filename ); | |
| 138 | + $is_encoded = $this->is_encoded( $original_content ); | |
| 145 | 139 | |
| 146 | - /** @var Svg $svg_handler */ | |
| 147 | - $svg_handler = Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' ); | |
| 140 | + if ( $is_encoded ) { | |
| 141 | + $decoded = $this->decode_svg( $original_content ); | |
| 142 | + if ( false === $decoded ) { | |
| 143 | + return false; | |
| 144 | + } | |
| 145 | + $original_content = $decoded; | |
| 146 | + } | |
| 148 | 147 | |
| 149 | - return $svg_handler->sanitize_svg( $filename ); | |
| 148 | + $valid_svg = $this->sanitizer( $original_content ); | |
| 149 | + | |
| 150 | + if ( false === $valid_svg ) { | |
| 151 | + return false; | |
| 152 | + } | |
| 153 | + | |
| 154 | + // If we were gzipped, we need to re-zip | |
| 155 | + if ( $is_encoded ) { | |
| 156 | + $valid_svg = $this->encode_svg( $valid_svg ); | |
| 157 | + } | |
| 158 | + file_put_contents( $filename, $valid_svg ); | |
| 159 | + | |
| 160 | + return true; | |
| 150 | 161 | } |
| 151 | 162 | |
| 152 | 163 | /** |
| 153 | - * Sanitizer | |
| 164 | + * Check if the contents are gzipped | |
| 165 | + * @see http://www.gzip.org/zlib/rfc-gzip.html#member-format | |
| 154 | 166 | * |
| 155 | - * @deprecated 3.5.0 Use `Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' )->sanitizer()` instead. | |
| 167 | + * @param $contents | |
| 168 | + * @return bool | |
| 169 | + */ | |
| 170 | + private function is_encoded( $contents ) { | |
| 171 | + $needle = "\x1f\x8b\x08"; | |
| 172 | + if ( function_exists( 'mb_strpos' ) ) { | |
| 173 | + return 0 === mb_strpos( $contents, $needle ); | |
| 174 | + } else { | |
| 175 | + return 0 === strpos( $contents, $needle ); | |
| 176 | + } | |
| 177 | + } | |
| 178 | + | |
| 179 | + /** | |
| 180 | + * is_allowed_tag | |
| 181 | + * @param $element | |
| 156 | 182 | * |
| 183 | + * @return bool | |
| 184 | + */ | |
| 185 | + private function is_allowed_tag( $element ) { | |
| 186 | + static $allowed_tags = false; | |
| 187 | + if ( false === $allowed_tags ) { | |
| 188 | + $allowed_tags = $this->get_allowed_elements(); | |
| 189 | + } | |
| 190 | + | |
| 191 | + $tag_name = $element->tagName; // phpcs:ignore -- php DomDocument | |
| 192 | + | |
| 193 | + if ( ! in_array( strtolower( $tag_name ), $allowed_tags ) ) { | |
| 194 | + $this->remove_element( $element ); | |
| 195 | + return false; | |
| 196 | + } | |
| 197 | + | |
| 198 | + return true; | |
| 199 | + } | |
| 200 | + | |
| 201 | + private function remove_element( $element ) { | |
| 202 | + $element->parentNode->removeChild( $element ); // phpcs:ignore -- php DomDocument | |
| 203 | + } | |
| 204 | + | |
| 205 | + /** | |
| 206 | + * is_a_attribute | |
| 207 | + * @param $name | |
| 208 | + * @param $check | |
| 209 | + * | |
| 210 | + * @return bool | |
| 211 | + */ | |
| 212 | + private function is_a_attribute( $name, $check ) { | |
| 213 | + return 0 === strpos( $name, $check . '-' ); | |
| 214 | + } | |
| 215 | + | |
| 216 | + /** | |
| 217 | + * is_remote_value | |
| 218 | + * @param $value | |
| 219 | + * | |
| 220 | + * @return string | |
| 221 | + */ | |
| 222 | + private function is_remote_value( $value ) { | |
| 223 | + $value = trim( preg_replace( '/[^ -~]/xu', '', $value ) ); | |
| 224 | + $wrapped_in_url = preg_match( '~^url\(\s*[\'"]\s*(.*)\s*[\'"]\s*\)$~xi', $value, $match ); | |
| 225 | + if ( ! $wrapped_in_url ) { | |
| 226 | + return false; | |
| 227 | + } | |
| 228 | + | |
| 229 | + $value = trim( $match[1], '\'"' ); | |
| 230 | + return preg_match( '~^((https?|ftp|file):)?//~xi', $value ); | |
| 231 | + } | |
| 232 | + | |
| 233 | + /** | |
| 234 | + * has_js_value | |
| 235 | + * @param $value | |
| 236 | + * | |
| 237 | + * @return false|int | |
| 238 | + */ | |
| 239 | + private function has_js_value( $value ) { | |
| 240 | + return preg_match( '/base64|data|(?:java)?script|alert\(|window\.|document/i', $value ); | |
| 241 | + } | |
| 242 | + | |
| 243 | + /** | |
| 244 | + * get_allowed_attributes | |
| 245 | + * @return array | |
| 246 | + */ | |
| 247 | + private function get_allowed_attributes() { | |
| 248 | + $allowed_attributes = [ | |
| 249 | + 'class', | |
| 250 | + 'clip-path', | |
| 251 | + 'clip-rule', | |
| 252 | + 'fill', | |
| 253 | + 'fill-opacity', | |
| 254 | + 'fill-rule', | |
| 255 | + 'filter', | |
| 256 | + 'id', | |
| 257 | + 'mask', | |
| 258 | + 'opacity', | |
| 259 | + 'stroke', | |
| 260 | + 'stroke-dasharray', | |
| 261 | + 'stroke-dashoffset', | |
| 262 | + 'stroke-linecap', | |
| 263 | + 'stroke-linejoin', | |
| 264 | + 'stroke-miterlimit', | |
| 265 | + 'stroke-opacity', | |
| 266 | + 'stroke-width', | |
| 267 | + 'style', | |
| 268 | + 'systemlanguage', | |
| 269 | + 'transform', | |
| 270 | + 'href', | |
| 271 | + 'xlink:href', | |
| 272 | + 'xlink:title', | |
| 273 | + 'cx', | |
| 274 | + 'cy', | |
| 275 | + 'r', | |
| 276 | + 'requiredfeatures', | |
| 277 | + 'clippathunits', | |
| 278 | + 'type', | |
| 279 | + 'rx', | |
| 280 | + 'ry', | |
| 281 | + 'color-interpolation-filters', | |
| 282 | + 'stddeviation', | |
| 283 | + 'filterres', | |
| 284 | + 'filterunits', | |
| 285 | + 'height', | |
| 286 | + 'primitiveunits', | |
| 287 | + 'width', | |
| 288 | + 'x', | |
| 289 | + 'y', | |
| 290 | + 'font-size', | |
| 291 | + 'display', | |
| 292 | + 'font-family', | |
| 293 | + 'font-style', | |
| 294 | + 'font-weight', | |
| 295 | + 'text-anchor', | |
| 296 | + 'marker-end', | |
| 297 | + 'marker-mid', | |
| 298 | + 'marker-start', | |
| 299 | + 'x1', | |
| 300 | + 'x2', | |
| 301 | + 'y1', | |
| 302 | + 'y2', | |
| 303 | + 'gradienttransform', | |
| 304 | + 'gradientunits', | |
| 305 | + 'spreadmethod', | |
| 306 | + 'markerheight', | |
| 307 | + 'markerunits', | |
| 308 | + 'markerwidth', | |
| 309 | + 'orient', | |
| 310 | + 'preserveaspectratio', | |
| 311 | + 'refx', | |
| 312 | + 'refy', | |
| 313 | + 'viewbox', | |
| 314 | + 'maskcontentunits', | |
| 315 | + 'maskunits', | |
| 316 | + 'd', | |
| 317 | + 'patterncontentunits', | |
| 318 | + 'patterntransform', | |
| 319 | + 'patternunits', | |
| 320 | + 'points', | |
| 321 | + 'fx', | |
| 322 | + 'fy', | |
| 323 | + 'offset', | |
| 324 | + 'stop-color', | |
| 325 | + 'stop-opacity', | |
| 326 | + 'xmlns', | |
| 327 | + 'xmlns:se', | |
| 328 | + 'xmlns:xlink', | |
| 329 | + 'xml:space', | |
| 330 | + 'method', | |
| 331 | + 'spacing', | |
| 332 | + 'startoffset', | |
| 333 | + 'dx', | |
| 334 | + 'dy', | |
| 335 | + 'rotate', | |
| 336 | + 'textlength', | |
| 337 | + ]; | |
| 338 | + | |
| 339 | + return apply_filters( 'elementor/files/svg/allowed_attributes', $allowed_attributes ); | |
| 340 | + } | |
| 341 | + | |
| 342 | + /** | |
| 343 | + * get_allowed_elements | |
| 344 | + * @return array | |
| 345 | + */ | |
| 346 | + private function get_allowed_elements() { | |
| 347 | + $allowed_elements = [ | |
| 348 | + 'a', | |
| 349 | + 'circle', | |
| 350 | + 'clippath', | |
| 351 | + 'defs', | |
| 352 | + 'style', | |
| 353 | + 'desc', | |
| 354 | + 'ellipse', | |
| 355 | + 'fegaussianblur', | |
| 356 | + 'filter', | |
| 357 | + 'foreignobject', | |
| 358 | + 'g', | |
| 359 | + 'image', | |
| 360 | + 'line', | |
| 361 | + 'lineargradient', | |
| 362 | + 'marker', | |
| 363 | + 'mask', | |
| 364 | + 'metadata', | |
| 365 | + 'path', | |
| 366 | + 'pattern', | |
| 367 | + 'polygon', | |
| 368 | + 'polyline', | |
| 369 | + 'radialgradient', | |
| 370 | + 'rect', | |
| 371 | + 'stop', | |
| 372 | + 'svg', | |
| 373 | + 'switch', | |
| 374 | + 'symbol', | |
| 375 | + 'text', | |
| 376 | + 'textpath', | |
| 377 | + 'title', | |
| 378 | + 'tspan', | |
| 379 | + 'use', | |
| 380 | + ]; | |
| 381 | + return apply_filters( 'elementor/files/svg/allowed_elements', $allowed_elements ); | |
| 382 | + } | |
| 383 | + | |
| 384 | + /** | |
| 385 | + * validate_allowed_attributes | |
| 386 | + * @param \DOMElement $element | |
| 387 | + */ | |
| 388 | + private function validate_allowed_attributes( $element ) { | |
| 389 | + static $allowed_attributes = false; | |
| 390 | + if ( false === $allowed_attributes ) { | |
| 391 | + $allowed_attributes = $this->get_allowed_attributes(); | |
| 392 | + } | |
| 393 | + | |
| 394 | + for ( $index = $element->attributes->length - 1; $index >= 0; $index-- ) { | |
| 395 | + // get attribute name | |
| 396 | + $attr_name = $element->attributes->item( $index )->name; | |
| 397 | + $attr_name_lowercase = strtolower( $attr_name ); | |
| 398 | + // Remove attribute if not in whitelist | |
| 399 | + if ( ! in_array( $attr_name_lowercase, $allowed_attributes ) && ! $this->is_a_attribute( $attr_name_lowercase, 'aria' ) && ! $this->is_a_attribute( $attr_name_lowercase, 'data' ) ) { | |
| 400 | + $element->removeAttribute( $attr_name ); | |
| 401 | + continue; | |
| 402 | + } | |
| 403 | + | |
| 404 | + $attr_value = $element->attributes->item( $index )->value; | |
| 405 | + | |
| 406 | + // Remove attribute if it has a remote reference or js or data-URI/base64 | |
| 407 | + if ( ! empty( $attr_value ) && ( $this->is_remote_value( $attr_value ) || $this->has_js_value( $attr_value ) ) ) { | |
| 408 | + $element->removeAttribute( $attr_name ); | |
| 409 | + continue; | |
| 410 | + } | |
| 411 | + } | |
| 412 | + } | |
| 413 | + | |
| 414 | + /** | |
| 415 | + * strip_xlinks | |
| 416 | + * @param \DOMElement $element | |
| 417 | + */ | |
| 418 | + private function strip_xlinks( $element ) { | |
| 419 | + $xlinks = $element->getAttributeNS( 'http://www.w3.org/1999/xlink', 'href' ); | |
| 420 | + | |
| 421 | + if ( ! $xlinks ) { | |
| 422 | + return; | |
| 423 | + } | |
| 424 | + | |
| 425 | + $allowed_links = [ | |
| 426 | + 'data:image/png', // PNG | |
| 427 | + 'data:image/gif', // GIF | |
| 428 | + 'data:image/jpg', // JPG | |
| 429 | + 'data:image/jpe', // JPEG | |
| 430 | + 'data:image/pjp', // PJPEG | |
| 431 | + ]; | |
| 432 | + if ( 1 === preg_match( self::SCRIPT_REGEX, $xlinks ) ) { | |
| 433 | + if ( ! in_array( substr( $xlinks, 0, 14 ), $allowed_links ) ) { | |
| 434 | + $element->removeAttributeNS( 'http://www.w3.org/1999/xlink', 'href' ); | |
| 435 | + } | |
| 436 | + } | |
| 437 | + } | |
| 438 | + | |
| 439 | + /** | |
| 440 | + * validate_use_tag | |
| 441 | + * @param $element | |
| 442 | + */ | |
| 443 | + private function validate_use_tag( $element ) { | |
| 444 | + $xlinks = $element->getAttributeNS( 'http://www.w3.org/1999/xlink', 'href' ); | |
| 445 | + if ( $xlinks && '#' !== substr( $xlinks, 0, 1 ) ) { | |
| 446 | + $element->parentNode->removeChild( $element ); // phpcs:ignore -- php DomNode | |
| 447 | + } | |
| 448 | + } | |
| 449 | + | |
| 450 | + /** | |
| 451 | + * strip_docktype | |
| 452 | + */ | |
| 453 | + private function strip_doctype() { | |
| 454 | + foreach ( $this->svg_dom->childNodes as $child ) { | |
| 455 | + if ( XML_DOCUMENT_TYPE_NODE === $child->nodeType ) { // phpcs:ignore -- php DomDocument | |
| 456 | + $child->parentNode->removeChild( $child ); // phpcs:ignore -- php DomDocument | |
| 457 | + } | |
| 458 | + } | |
| 459 | + } | |
| 460 | + | |
| 461 | + /** | |
| 462 | + * sanitize_elements | |
| 463 | + */ | |
| 464 | + private function sanitize_elements() { | |
| 465 | + $elements = $this->svg_dom->getElementsByTagName( '*' ); | |
| 466 | + // loop through all elements | |
| 467 | + // we do this backwards so we don't skip anything if we delete a node | |
| 468 | + // see comments at: http://php.net/manual/en/class.domnamednodemap.php | |
| 469 | + for ( $index = $elements->length - 1; $index >= 0; $index-- ) { | |
| 470 | + /** | |
| 471 | + * @var \DOMElement $current_element | |
| 472 | + */ | |
| 473 | + $current_element = $elements->item( $index ); | |
| 474 | + // If the tag isn't in the whitelist, remove it and continue with next iteration | |
| 475 | + if ( ! $this->is_allowed_tag( $current_element ) ) { | |
| 476 | + continue; | |
| 477 | + } | |
| 478 | + | |
| 479 | + //validate element attributes | |
| 480 | + $this->validate_allowed_attributes( $current_element ); | |
| 481 | + | |
| 482 | + $this->strip_xlinks( $current_element ); | |
| 483 | + | |
| 484 | + if ( 'use' === strtolower( $current_element->tagName ) ) { // phpcs:ignore -- php DomDocument | |
| 485 | + $this->validate_use_tag( $current_element ); | |
| 486 | + } | |
| 487 | + } | |
| 488 | + } | |
| 489 | + | |
| 490 | + /** | |
| 491 | + * sanitizer | |
| 157 | 492 | * @param $content |
| 158 | 493 | * |
| 159 | 494 | * @return bool|string |
| 160 | 495 | */ |
| 161 | 496 | public function sanitizer( $content ) { |
| 162 | - Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Plugin::$instance->uploads_manager->get_file_type_handlers( \'svg\' )->sanitizer()' ); | |
| 497 | + // Strip php tags | |
| 498 | + $content = $this->strip_comments( $content ); | |
| 499 | + $content = $this->strip_php_tags( $content ); | |
| 163 | 500 | |
| 164 | - /** @var Svg $svg_handler */ | |
| 165 | - $svg_handler = Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' ); | |
| 501 | + // Find the start and end tags so we can cut out miscellaneous garbage. | |
| 502 | + $start = strpos( $content, '<svg' ); | |
| 503 | + $end = strrpos( $content, '</svg>' ); | |
| 504 | + if ( false === $start || false === $end ) { | |
| 505 | + return false; | |
| 506 | + } | |
| 166 | 507 | |
| 167 | - return $svg_handler->sanitizer( $content ); | |
| 508 | + $content = substr( $content, $start, ( $end - $start + 6 ) ); | |
| 509 | + | |
| 510 | + // If the server's PHP version is 8 or up, make sure to Disable the ability to load external entities | |
| 511 | + $php_version_under_eight = version_compare( PHP_VERSION, '8.0.0', '<' ); | |
| 512 | + if ( $php_version_under_eight ) { | |
| 513 | + $libxml_disable_entity_loader = libxml_disable_entity_loader( true ); // phpcs:ignore Generic.PHP.DeprecatedFunctions.Deprecated | |
| 514 | + } | |
| 515 | + // Suppress the errors | |
| 516 | + $libxml_use_internal_errors = libxml_use_internal_errors( true ); | |
| 517 | + | |
| 518 | + // Create DomDocument instance | |
| 519 | + $this->svg_dom = new \DOMDocument(); | |
| 520 | + $this->svg_dom->formatOutput = false; | |
| 521 | + $this->svg_dom->preserveWhiteSpace = false; | |
| 522 | + $this->svg_dom->strictErrorChecking = false; | |
| 523 | + | |
| 524 | + $open_svg = $this->svg_dom->loadXML( $content ); | |
| 525 | + if ( ! $open_svg ) { | |
| 526 | + return false; | |
| 527 | + } | |
| 528 | + | |
| 529 | + $this->strip_doctype(); | |
| 530 | + $this->sanitize_elements(); | |
| 531 | + | |
| 532 | + // Export sanitized svg to string | |
| 533 | + // Using documentElement to strip out <?xml version="1.0" encoding="UTF-8"... | |
| 534 | + $sanitized = $this->svg_dom->saveXML( $this->svg_dom->documentElement, LIBXML_NOEMPTYTAG ); | |
| 535 | + | |
| 536 | + // Restore defaults | |
| 537 | + if ( $php_version_under_eight ) { | |
| 538 | + libxml_disable_entity_loader( $libxml_disable_entity_loader ); // phpcs:ignore Generic.PHP.DeprecatedFunctions.Deprecated | |
| 539 | + } | |
| 540 | + libxml_use_internal_errors( $libxml_use_internal_errors ); | |
| 541 | + | |
| 542 | + return $sanitized; | |
| 168 | 543 | } |
| 169 | 544 | |
| 170 | 545 | /** |
| 171 | - * Prepare attachment for js | |
| 546 | + * strip_php_tags | |
| 547 | + * @param $string | |
| 172 | 548 | * |
| 173 | - * @deprecated 3.5.0 Use `Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' )->wp_prepare_attachment_for_js()` instead. | |
| 549 | + * @return string | |
| 550 | + */ | |
| 551 | + private function strip_php_tags( $string ) { | |
| 552 | + $string = preg_replace( '/<\?(=|php)(.+?)\?>/i', '', $string ); | |
| 553 | + // Remove XML, ASP, etc. | |
| 554 | + $string = preg_replace( '/<\?(.*)\?>/Us', '', $string ); | |
| 555 | + $string = preg_replace( '/<\%(.*)\%>/Us', '', $string ); | |
| 556 | + | |
| 557 | + if ( ( false !== strpos( $string, '<?' ) ) || ( false !== strpos( $string, '<%' ) ) ) { | |
| 558 | + return ''; | |
| 559 | + } | |
| 560 | + return $string; | |
| 561 | + } | |
| 562 | + | |
| 563 | + /** | |
| 564 | + * strip_comments | |
| 565 | + * @param $string | |
| 174 | 566 | * |
| 567 | + * @return string | |
| 568 | + */ | |
| 569 | + private function strip_comments( $string ) { | |
| 570 | + // Remove comments. | |
| 571 | + $string = preg_replace( '/<!--(.*)-->/Us', '', $string ); | |
| 572 | + $string = preg_replace( '/\/\*(.*)\*\//Us', '', $string ); | |
| 573 | + if ( ( false !== strpos( $string, '<!--' ) ) || ( false !== strpos( $string, '/*' ) ) ) { | |
| 574 | + return ''; | |
| 575 | + } | |
| 576 | + return $string; | |
| 577 | + } | |
| 578 | + | |
| 579 | + /** | |
| 580 | + * wp_prepare_attachment_for_js | |
| 175 | 581 | * @param $attachment_data |
| 176 | 582 | * @param $attachment |
| 177 | 583 | * @param $meta |
| 178 | 584 | * |
| @@ -178,72 +584,126 @@ | ||
| 178 | 584 | * |
| 179 | 585 | * @return mixed |
| 180 | 586 | */ |
| 181 | 587 | public function wp_prepare_attachment_for_js( $attachment_data, $attachment, $meta ) { |
| 182 | - Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Plugin::$instance->uploads_manager->get_file_type_handlers( \'svg\' )->wp_prepare_attachment_for_js()' ); | |
| 588 | + if ( 'image' !== $attachment_data['type'] || 'svg+xml' !== $attachment_data['subtype'] || ! class_exists( 'SimpleXMLElement' ) ) { | |
| 589 | + return $attachment_data; | |
| 590 | + } | |
| 183 | 591 | |
| 184 | - /** @var Svg $svg_handler */ | |
| 185 | - $svg_handler = Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' ); | |
| 592 | + $svg = self::get_inline_svg( $attachment->ID ); | |
| 186 | 593 | |
| 187 | - return $svg_handler->wp_prepare_attachment_for_js( $attachment_data, $attachment, $meta ); | |
| 594 | + if ( ! $svg ) { | |
| 595 | + return $attachment_data; | |
| 596 | + } | |
| 597 | + | |
| 598 | + try { | |
| 599 | + $svg = new \SimpleXMLElement( $svg ); | |
| 600 | + } catch ( \Exception $e ) { | |
| 601 | + return $attachment_data; | |
| 602 | + } | |
| 603 | + | |
| 604 | + $src = $attachment_data['url']; | |
| 605 | + $width = (int) $svg['width']; | |
| 606 | + $height = (int) $svg['height']; | |
| 607 | + | |
| 608 | + // Media Gallery | |
| 609 | + $attachment_data['image'] = compact( 'src', 'width', 'height' ); | |
| 610 | + $attachment_data['thumb'] = compact( 'src', 'width', 'height' ); | |
| 611 | + | |
| 612 | + // Single Details of Image | |
| 613 | + $attachment_data['sizes']['full'] = [ | |
| 614 | + 'height' => $height, | |
| 615 | + 'width' => $width, | |
| 616 | + 'url' => $src, | |
| 617 | + 'orientation' => $height > $width ? 'portrait' : 'landscape', | |
| 618 | + ]; | |
| 619 | + return $attachment_data; | |
| 188 | 620 | } |
| 189 | 621 | |
| 190 | 622 | /** |
| 191 | - * Set attachment id | |
| 192 | - * | |
| 193 | - * @deprecated 3.5.0 | |
| 194 | - * | |
| 623 | + * set_attachment_id | |
| 195 | 624 | * @param $attachment_id |
| 196 | 625 | * |
| 197 | 626 | * @return int |
| 198 | 627 | */ |
| 199 | 628 | public function set_attachment_id( $attachment_id ) { |
| 200 | - Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0' ); | |
| 201 | - | |
| 202 | 629 | $this->attachment_id = $attachment_id; |
| 203 | 630 | return $this->attachment_id; |
| 204 | 631 | } |
| 205 | 632 | |
| 206 | 633 | /** |
| 207 | - * Get attachment id | |
| 208 | - * | |
| 209 | - * @deprecated 3.5.0 | |
| 210 | - * | |
| 634 | + * get_attachment_id | |
| 211 | 635 | * @return int |
| 212 | 636 | */ |
| 213 | 637 | public function get_attachment_id() { |
| 214 | - Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0' ); | |
| 215 | - | |
| 216 | 638 | return $this->attachment_id; |
| 217 | 639 | } |
| 218 | 640 | |
| 219 | 641 | /** |
| 220 | - * Set svg meta data | |
| 642 | + * handle_upload_prefilter | |
| 643 | + * @param $file | |
| 221 | 644 | * |
| 222 | - * @deprecated 3.5.0 Use `Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' )->set_svg_meta_data()` instead. | |
| 223 | - * | |
| 224 | 645 | * @return mixed |
| 225 | 646 | */ |
| 226 | - public function set_svg_meta_data( $data, $id ) { | |
| 227 | - Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Plugin::$instance->uploads_manager->get_file_type_handlers( \'svg\' )->set_svg_meta_data()' ); | |
| 647 | + public function handle_upload_prefilter( $file ) { | |
| 648 | + if ( ! $this->is_file_should_handled( $file ) ) { | |
| 649 | + return $file; | |
| 650 | + } | |
| 228 | 651 | |
| 229 | - /** @var Svg $svg_handler */ | |
| 230 | - $svg_handler = Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' ); | |
| 652 | + $file = parent::handle_upload_prefilter( $file ); | |
| 231 | 653 | |
| 232 | - return $svg_handler->set_svg_meta_data( $data, $id ); | |
| 654 | + if ( ! $file['error'] && self::file_sanitizer_can_run() && ! $this->sanitize_svg( $file['tmp_name'] ) ) { | |
| 655 | + $display_type = strtoupper( $this->get_file_type() ); | |
| 656 | + | |
| 657 | + $file['error'] = sprintf( __( 'Invalid %1$s Format, file not uploaded for security reasons', 'elementor' ), $display_type ); | |
| 658 | + } | |
| 659 | + | |
| 660 | + return $file; | |
| 233 | 661 | } |
| 234 | 662 | |
| 235 | 663 | /** |
| 236 | - * Handle upload prefilter | |
| 237 | - * | |
| 238 | - * @deprecated 3.5.0 Use `Elementor\Plugin::$instance->uploads_manager->handle_elementor_wp_media_upload()` instead. | |
| 239 | - * | |
| 240 | - * @param $file | |
| 241 | - * | |
| 242 | - * @return mixed | |
| 664 | + * @since 3.0.0 | |
| 665 | + * @deprecated 3.0.0 Use Files_Upload_Handler::file_sanitizer_can_run() instead. | |
| 243 | 666 | */ |
| 244 | - public function handle_upload_prefilter( $file ) { | |
| 245 | - Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Elementor\Plugin::$instance->uploads_manager->handle_elementor_wp_media_upload()' ); | |
| 667 | + public function svg_sanitizer_can_run() { | |
| 668 | + _deprecated_function( __METHOD__, '3.0.0', 'Files_Upload_Handler::file_sanitizer_can_run()' ); | |
| 246 | 669 | |
| 247 | - return Plugin::$instance->uploads_manager->handle_elementor_wp_media_upload( $file ); | |
| 670 | + return Files_Upload_Handler::file_sanitizer_can_run(); | |
| 671 | + } | |
| 672 | + | |
| 673 | + /** | |
| 674 | + * @since 3.0.0 | |
| 675 | + * @deprecated 3.0.0 | |
| 676 | + */ | |
| 677 | + public function upload_mimes() { | |
| 678 | + _deprecated_function( __METHOD__, '3.0.0' ); | |
| 679 | + } | |
| 680 | + | |
| 681 | + /** | |
| 682 | + * @since 3.0.0 | |
| 683 | + * @deprecated 3.0.0 | |
| 684 | + */ | |
| 685 | + public function wp_handle_upload_prefilter() { | |
| 686 | + _deprecated_function( __METHOD__, '3.0.0' ); | |
| 687 | + } | |
| 688 | + | |
| 689 | + /** | |
| 690 | + * @since 3.0.0 | |
| 691 | + * @deprecated 3.0.0 Use Files_Upload_Handler::is_enabled() instead. | |
| 692 | + * @see is_enabled() | |
| 693 | + */ | |
| 694 | + public function is_svg_uploads_enabled() { | |
| 695 | + _deprecated_function( __METHOD__, '3.0.0', 'Files_Upload_Handler::is_enabled()' ); | |
| 696 | + | |
| 697 | + return Files_Upload_Handler::is_enabled(); | |
| 698 | + } | |
| 699 | + | |
| 700 | + /** | |
| 701 | + * Svg_Handler constructor. | |
| 702 | + */ | |
| 703 | + public function __construct() { | |
| 704 | + parent::__construct(); | |
| 705 | + | |
| 706 | + add_filter( 'wp_prepare_attachment_for_js', [ $this, 'wp_prepare_attachment_for_js' ], 10, 3 ); | |
| 707 | + add_action( 'elementor/core/files/clear_cache', [ $this, 'delete_meta_cache' ] ); | |
| 248 | 708 | } |
| 249 | 709 | } |