| @@ -1,8 +1,14 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | |
| 3 | 3 | namespace UltimateStoreKit\Builder; |
| 4 | 4 | |
| 5 | +if (! defined('ABSPATH')) { | |
| 6 | + exit; // Exit if accessed directly | |
| 7 | +} | |
| 8 | + | |
| 9 | +// phpcs:disable WordPress.NamingConventions.PrefixAllGlobals -- BDTUSK_ / ultimate_store_kit_ / ultimate-store-kit- are this plugin's established public prefixes. | |
| 10 | + | |
| 5 | 11 | if (! defined('WPINC')) { |
| 6 | 12 | die; |
| 7 | 13 | } |
| 8 | 14 | |
| @@ -29,38 +35,16 @@ | ||
| 29 | 35 | add_filter("elementor/document/urls/wp_preview", [$this, 'change_preview_editor_url'], 999, 2); |
| 30 | 36 | |
| 31 | 37 | add_action('elementor/documents/register_controls', [$this, 'register_document_controls']); |
| 32 | 38 | |
| 33 | - // Add demo bypass filter for template preview | |
| 34 | - add_filter('ultimate_store_kit/preview/verified_bypass', function ($verify) { | |
| 35 | - // Check if we're in a demo environment or development site | |
| 36 | - // For demo sites, you might want to check domain names or other indicators | |
| 37 | - $demo_hosts = apply_filters('ultimate_store_kit/demo_hosts', [ | |
| 38 | - 'storekit.pro', | |
| 39 | - 'demo.storekit.pro', | |
| 40 | - 'localhost', | |
| 41 | - '127.0.0.1' | |
| 42 | - ]); | |
| 43 | - | |
| 44 | - $current_host = isset($_SERVER['HTTP_HOST']) ? sanitize_text_field($_SERVER['HTTP_HOST']) : ''; | |
| 45 | - | |
| 46 | - foreach ($demo_hosts as $host) { | |
| 47 | - if (strpos($current_host, $host) !== false) { | |
| 48 | - return true; | |
| 49 | - } | |
| 50 | - } | |
| 51 | - | |
| 52 | - return $verify; | |
| 53 | - }); | |
| 54 | - | |
| 55 | - // Add filter to enable demo mode for preview URLs | |
| 56 | - add_filter('ultimate_store_kit/preview/use_demo_bypass', function ($use_demo) { | |
| 57 | - // Enable demo bypass in Elementor editor | |
| 58 | - if (isset($_GET['action']) && $_GET['action'] === 'elementor') { | |
| 59 | - return true; | |
| 60 | - } | |
| 61 | - return $use_demo; | |
| 62 | - }); | |
| 39 | + // The template preview used to accept the literal string "verified" in place | |
| 40 | + // of a nonce whenever $_SERVER['HTTP_HOST'] contained one of a list of demo | |
| 41 | + // hostnames. Because Host is attacker-controlled and the check was a substring | |
| 42 | + // match, any unauthenticated visitor could render the Elementor content of an | |
| 43 | + // arbitrary post id -- including drafts and private posts -- on any site whose | |
| 44 | + // Host reached PHP as localhost/127.0.0.1 (common behind a reverse proxy) or | |
| 45 | + // whose domain merely contained one of those strings. Preview is now gated on | |
| 46 | + // the real per-post nonce plus an edit_post capability check; see get_template_id(). | |
| 63 | 47 | } |
| 64 | 48 | |
| 65 | 49 | public function change_preview_editor_url($url, $document) { |
| 66 | 50 | $post_id = $document->get_main_id(); |
| @@ -137,15 +121,12 @@ | ||
| 137 | 121 | if (empty($template_url)) { |
| 138 | 122 | return $url; |
| 139 | 123 | } |
| 140 | 124 | |
| 141 | - $is_demo = apply_filters('ultimate_store_kit/preview/use_demo_bypass', false); | |
| 142 | - $nonce_value = $is_demo ? 'verified' : wp_create_nonce('template_preview_' . $post_id); | |
| 143 | - | |
| 144 | 125 | $param = [ |
| 145 | - 'usk_template_id' => $post_id, | |
| 146 | - 'preview_nonce' => $nonce_value, | |
| 147 | - 'preview' => true | |
| 126 | + 'ultimate_store_kit_template_id' => $post_id, | |
| 127 | + 'ultimate_store_kit_preview_nonce' => wp_create_nonce('ultimate_store_kit_template_preview_' . $post_id), | |
| 128 | + 'preview' => true | |
| 148 | 129 | ]; |
| 149 | 130 | |
| 150 | 131 | // Add parameters two URL |
| 151 | 132 | $url = add_query_arg($param, $template_url); |
| @@ -154,8 +135,9 @@ | ||
| 154 | 135 | } |
| 155 | 136 | |
| 156 | 137 | public function my_custom_fonts() { |
| 157 | 138 | if (is_admin() && Plugin::instance()->editor->is_edit_mode()) { |
| 139 | + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only check on whether a builder template is open; only enqueues a style. | |
| 158 | 140 | if (isset($_REQUEST['usk-template'])) { |
| 159 | 141 | wp_register_style('usk-template-builder-hide-preview-btn-inline', false); // phpcs:ignore |
| 160 | 142 | wp_enqueue_style('usk-template-builder-hide-preview-btn-inline'); |
| 161 | 143 | wp_add_inline_style( |
| @@ -193,9 +175,9 @@ | ||
| 193 | 175 | return; |
| 194 | 176 | } |
| 195 | 177 | $meta = get_post_meta($post->ID); |
| 196 | 178 | |
| 197 | - $templateMeta = optional($meta)[Meta::TEMPLATE_TYPE]; | |
| 179 | + $templateMeta = ultimate_store_kit_optional($meta)[Meta::TEMPLATE_TYPE]; | |
| 198 | 180 | if (! isset($templateMeta[0])) { |
| 199 | 181 | return; |
| 200 | 182 | } |
| 201 | 183 | $postMeta = $templateMeta[0]; |
| @@ -396,9 +378,9 @@ | ||
| 396 | 378 | return $this->getTemplatePath('home', $template); |
| 397 | 379 | } |
| 398 | 380 | } |
| 399 | 381 | |
| 400 | - if ($page_Id = intval(get_option('bdt_usk_compare_products_page_id'))) { | |
| 382 | + if ($page_Id = ultimate_store_kit_get_compare_page_option()) { | |
| 401 | 383 | if (is_page($page_Id)) { |
| 402 | 384 | if ($custom_template = $this->get_template_id('compare-products', 'product')) { |
| 403 | 385 | $this->current_template_id = $custom_template; |
| 404 | 386 | return $this->getTemplatePath('home', $template); |
| @@ -440,32 +422,26 @@ | ||
| 440 | 422 | if (null !== $this->current_template_id) { |
| 441 | 423 | return $this->current_template_id; |
| 442 | 424 | } |
| 443 | 425 | |
| 444 | - // Handle template preview from URL parameters | |
| 445 | - if (!empty($_GET['preview']) && !empty($_GET['usk_template_id']) && !empty($_GET['preview_nonce'])) { | |
| 446 | - $usk_template_id = sanitize_text_field(wp_unslash($_GET['usk_template_id'])); | |
| 447 | - $nonce = sanitize_text_field(wp_unslash($_GET['preview_nonce'])); | |
| 426 | + // Handle template preview from URL parameters. | |
| 427 | + if (!empty($_GET['preview']) && !empty($_GET['ultimate_store_kit_template_id']) && !empty($_GET['ultimate_store_kit_preview_nonce'])) { | |
| 428 | + $usk_template_id = absint(wp_unslash($_GET['ultimate_store_kit_template_id'])); | |
| 429 | + $nonce = sanitize_text_field(wp_unslash($_GET['ultimate_store_kit_preview_nonce'])); | |
| 448 | 430 | |
| 449 | - // Special handling for demo bypass | |
| 450 | - $is_demo_bypass = ($nonce === 'verified'); | |
| 451 | - $nonce_verified = $is_demo_bypass ? | |
| 452 | - apply_filters('ultimate_store_kit/preview/verified_bypass', false) : | |
| 453 | - wp_verify_nonce($nonce, 'template_preview_' . $usk_template_id); | |
| 431 | + // The nonce is bound to the specific template and to the user who | |
| 432 | + // generated it, and the capability check makes sure a leaked preview | |
| 433 | + // URL cannot be replayed by someone who may not edit the template. | |
| 434 | + $nonce_verified = $usk_template_id | |
| 435 | + && wp_verify_nonce($nonce, 'ultimate_store_kit_template_preview_' . $usk_template_id) | |
| 436 | + && current_user_can('edit_post', $usk_template_id); | |
| 454 | 437 | |
| 455 | - if ($nonce_verified) { | |
| 456 | - // For demo bypass mode, we don't need to check template type | |
| 457 | - if ($is_demo_bypass) { | |
| 458 | - $this->current_template_id = (int)$usk_template_id; | |
| 459 | - return $this->current_template_id; | |
| 460 | - } | |
| 461 | - | |
| 462 | - // For normal preview, check template type | |
| 438 | + if ($nonce_verified && get_post_type($usk_template_id) === Meta::POST_TYPE) { | |
| 463 | 439 | $template_type = get_post_meta($usk_template_id, Meta::TEMPLATE_TYPE, true); |
| 464 | 440 | if (!empty($template_type)) { |
| 465 | 441 | $template_data = explode(Builder_Template_Helper::separator(), $template_type); |
| 466 | 442 | if (count($template_data) >= 2 && $template_data[1] === $slug && ($postType === false || $template_data[0] === $postType)) { |
| 467 | - $this->current_template_id = (int)$usk_template_id; | |
| 443 | + $this->current_template_id = $usk_template_id; | |
| 468 | 444 | return $this->current_template_id; |
| 469 | 445 | } |
| 470 | 446 | } |
| 471 | 447 | } |