← All changes
|
includes/core/rest-api/Controllers/class-gutenberg-css-controller.php
+119
-3
2.8.16
→
3.13.3
View file →
| @@ -111,9 +111,9 @@ | ||
| 111 | 111 | if ( ! $wp_filesystem ) { |
| 112 | 112 | require_once ABSPATH . 'wp-admin/includes/file.php'; |
| 113 | 113 | } |
| 114 | 114 | $params = $request->get_params(); |
| 115 | - $css = $params['css']; | |
| 115 | + $css = $this->sanitize_css_content( $params['css'] ); | |
| 116 | 116 | $post_id = (int) sanitize_text_field( $params['post_id'] ); |
| 117 | 117 | if ( $post_id ) { |
| 118 | 118 | $filename = "wpfnl-gb-css-{$post_id}.css"; |
| 119 | 119 | $upload_dir = wp_upload_dir(); |
| @@ -163,12 +163,12 @@ | ||
| 163 | 163 | $post_id = (int) sanitize_text_field($params['post_id']); |
| 164 | 164 | $is_previewing = $params['isPreviewing']; |
| 165 | 165 | |
| 166 | 166 | if ($params['is_remain']) { |
| 167 | - $qubely_block_css = $params['block_css']; | |
| 167 | + $qubely_block_css = $this->sanitize_css_content($params['block_css']); | |
| 168 | 168 | $filename = "wpfnl-css-{$post_id}.css"; |
| 169 | 169 | |
| 170 | - $qubely_block_json = $params['interaction']; | |
| 170 | + $qubely_block_json = $this->sanitize_json_content($params['interaction']); | |
| 171 | 171 | $jsonfilename = "wpfnl-json-{$post_id}.json"; |
| 172 | 172 | |
| 173 | 173 | $upload_dir = wp_upload_dir(); |
| 174 | 174 | $dir = trailingslashit($upload_dir['basedir']) . 'wpfunnels/css/'; |
| @@ -280,6 +280,122 @@ | ||
| 280 | 280 | $get_css = implode( '', $fonts ) . $get_css; |
| 281 | 281 | } |
| 282 | 282 | } |
| 283 | 283 | return $get_css; |
| 284 | + } | |
| 285 | + | |
| 286 | + /** | |
| 287 | + * Sanitize CSS content to prevent XSS attacks | |
| 288 | + * | |
| 289 | + * @param string $css The CSS content to sanitize | |
| 290 | + * @return string Sanitized CSS content | |
| 291 | + * @since 3.5.27 | |
| 292 | + */ | |
| 293 | + private function sanitize_css_content($css){ | |
| 294 | + if (!is_string($css)) { | |
| 295 | + return ''; | |
| 296 | + } | |
| 297 | + | |
| 298 | + // Remove potentially dangerous CSS functions and properties | |
| 299 | + $dangerous_patterns = array( | |
| 300 | + '/javascript\s*:/i', // Remove javascript: URLs | |
| 301 | + '/expression\s*\(/i', // Remove CSS expressions | |
| 302 | + '/behavior\s*:/i', // Remove IE behavior property | |
| 303 | + '/@import\s+(?!url\()/i', // Remove @import except url() imports | |
| 304 | + '/binding\s*:/i', // Remove binding property | |
| 305 | + '/-moz-binding\s*:/i', // Remove -moz-binding | |
| 306 | + '/data\s*:\s*text\/html/i', // Remove data:text/html | |
| 307 | + '/vbscript\s*:/i', // Remove vbscript: URLs | |
| 308 | + '/on\w+\s*=/i', // Remove event handlers | |
| 309 | + '/<script/i', // Remove script tags | |
| 310 | + '/<\/script/i', // Remove closing script tags | |
| 311 | + '/<script/i', // Remove encoded script tags | |
| 312 | + '/<\/script/i', // Remove encoded closing script tags | |
| 313 | + ); | |
| 314 | + | |
| 315 | + // Apply sanitization patterns | |
| 316 | + $sanitized_css = preg_replace($dangerous_patterns, '', $css); | |
| 317 | + | |
| 318 | + // Strip any remaining HTML/XML tags that might have sneaked in | |
| 319 | + $sanitized_css = strip_tags($sanitized_css); | |
| 320 | + | |
| 321 | + // Additional validation for CSS structure | |
| 322 | + $sanitized_css = $this->validate_css_structure($sanitized_css); | |
| 323 | + | |
| 324 | + return $sanitized_css; | |
| 325 | + } | |
| 326 | + | |
| 327 | + /** | |
| 328 | + * Validate CSS structure and remove potentially malicious content | |
| 329 | + * | |
| 330 | + * @param string $css The CSS content to validate | |
| 331 | + * @return string Validated CSS content | |
| 332 | + * @since 3.5.27 | |
| 333 | + */ | |
| 334 | + private function validate_css_structure($css){ | |
| 335 | + // Remove any content that looks like HTML/XML | |
| 336 | + $css = preg_replace('/<[^>]*>/', '', $css); | |
| 337 | + | |
| 338 | + // Remove any remaining HTML entities that could be used for XSS | |
| 339 | + $css = html_entity_decode($css, ENT_QUOTES | ENT_HTML5, 'UTF-8'); | |
| 340 | + $css = preg_replace('/&[#\w]+;/', '', $css); | |
| 341 | + | |
| 342 | + return $css; | |
| 343 | + } | |
| 344 | + | |
| 345 | + /** | |
| 346 | + * Sanitize JSON content to prevent XSS attacks | |
| 347 | + * | |
| 348 | + * @param mixed $json The JSON content to sanitize | |
| 349 | + * @return string Sanitized JSON content | |
| 350 | + * @since 3.5.27 | |
| 351 | + */ | |
| 352 | + private function sanitize_json_content($json){ | |
| 353 | + if (is_string($json)) { | |
| 354 | + // Decode JSON to validate structure | |
| 355 | + $decoded = json_decode($json, true); | |
| 356 | + if (json_last_error() === JSON_ERROR_NONE) { | |
| 357 | + // Recursively sanitize the decoded data | |
| 358 | + $sanitized = $this->sanitize_json_data($decoded); | |
| 359 | + return wp_json_encode($sanitized); | |
| 360 | + } | |
| 361 | + } elseif (is_array($json) || is_object($json)) { | |
| 362 | + // Sanitize array/object data | |
| 363 | + $sanitized = $this->sanitize_json_data($json); | |
| 364 | + return wp_json_encode($sanitized); | |
| 365 | + } | |
| 366 | + | |
| 367 | + return '{}'; // Return empty JSON object if invalid | |
| 368 | + } | |
| 369 | + | |
| 370 | + /** | |
| 371 | + * Recursively sanitize JSON data | |
| 372 | + * | |
| 373 | + * @param mixed $data The data to sanitize | |
| 374 | + * @return mixed Sanitized data | |
| 375 | + * @since 3.5.27 | |
| 376 | + */ | |
| 377 | + private function sanitize_json_data($data){ | |
| 378 | + if (is_array($data)) { | |
| 379 | + $sanitized = array(); | |
| 380 | + foreach ($data as $key => $value) { | |
| 381 | + $sanitized_key = sanitize_text_field($key); | |
| 382 | + $sanitized[$sanitized_key] = $this->sanitize_json_data($value); | |
| 383 | + } | |
| 384 | + return $sanitized; | |
| 385 | + } elseif (is_object($data)) { | |
| 386 | + $sanitized = new \stdClass(); | |
| 387 | + foreach ($data as $key => $value) { | |
| 388 | + $sanitized_key = sanitize_text_field($key); | |
| 389 | + $sanitized->$sanitized_key = $this->sanitize_json_data($value); | |
| 390 | + } | |
| 391 | + return $sanitized; | |
| 392 | + } elseif (is_string($data)) { | |
| 393 | + // For strings, remove potential XSS vectors | |
| 394 | + $data = wp_kses($data, array()); | |
| 395 | + $data = sanitize_text_field($data); | |
| 396 | + return $data; | |
| 397 | + } | |
| 398 | + | |
| 399 | + return $data; | |
| 284 | 400 | } |
| 285 | 401 | } |