← All changes
|
includes/setup-wizard/ultimate-post-kit-others-plugin.php
+89
-83
4.1.2
→
4.5.6
View file →
| @@ -21,11 +21,11 @@ | ||
| 21 | 21 | /** |
| 22 | 22 | * Constructor |
| 23 | 23 | */ |
| 24 | 24 | public function __construct() { |
| 25 | - // Add AJAX handlers | |
| 25 | + // Add AJAX handlers. This is an admin-only, plugin-install screen; the | |
| 26 | + // handler must never be exposed to unauthenticated visitors. | |
| 26 | 27 | add_action('wp_ajax_upk_get_plugins', [$this, 'ajax_get_plugins']); |
| 27 | - add_action('wp_ajax_nopriv_upk_get_plugins', [$this, 'ajax_get_plugins']); | |
| 28 | 28 | add_action('wp_ajax_upk_install_plugin', [$this, 'install_plugin_ajax']); |
| 29 | 29 | } |
| 30 | 30 | |
| 31 | 31 | /** |
| @@ -54,8 +54,9 @@ | ||
| 54 | 54 | ); |
| 55 | 55 | |
| 56 | 56 | // Helper function for time formatting |
| 57 | 57 | if (!function_exists('format_last_updated_usk')) { |
| 58 | + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- established function name relied on across the plugin family / feedback SDK; renaming would break integration. | |
| 58 | 59 | function format_last_updated_usk($date_string) { |
| 59 | 60 | if (empty($date_string)) { |
| 60 | 61 | return __('Unknown', 'ultimate-post-kit'); |
| 61 | 62 | } |
| @@ -70,58 +71,30 @@ | ||
| 70 | 71 | if ($diff < 60) { |
| 71 | 72 | return __('Just now', 'ultimate-post-kit'); |
| 72 | 73 | } elseif ($diff < 3600) { |
| 73 | 74 | $minutes = floor($diff / 60); |
| 75 | + /* translators: %d: number of minutes */ | |
| 74 | 76 | return sprintf(_n('%d minute ago', '%d minutes ago', $minutes, 'ultimate-post-kit'), $minutes); |
| 75 | 77 | } elseif ($diff < 86400) { |
| 76 | 78 | $hours = floor($diff / 3600); |
| 79 | + /* translators: %d: number of hours */ | |
| 77 | 80 | return sprintf(_n('%d hour ago', '%d hours ago', $hours, 'ultimate-post-kit'), $hours); |
| 78 | 81 | } elseif ($diff < 2592000) { // 30 days |
| 79 | 82 | $days = floor($diff / 86400); |
| 83 | + /* translators: %d: number of days */ | |
| 80 | 84 | return sprintf(_n('%d day ago', '%d days ago', $days, 'ultimate-post-kit'), $days); |
| 81 | 85 | } elseif ($diff < 31536000) { // 1 year |
| 82 | 86 | $months = floor($diff / 2592000); |
| 87 | + /* translators: %d: number of months */ | |
| 83 | 88 | return sprintf(_n('%d month ago', '%d months ago', $months, 'ultimate-post-kit'), $months); |
| 84 | 89 | } else { |
| 85 | 90 | $years = floor($diff / 31536000); |
| 91 | + /* translators: %d: number of years */ | |
| 86 | 92 | return sprintf(_n('%d year ago', '%d years ago', $years, 'ultimate-post-kit'), $years); |
| 87 | 93 | } |
| 88 | 94 | } |
| 89 | 95 | } |
| 90 | 96 | |
| 91 | - // Helper function for fallback URLs | |
| 92 | - if (!function_exists('get_plugin_fallback_urls_usk')) { | |
| 93 | - function get_plugin_fallback_urls_usk($plugin_slug) { | |
| 94 | - // Handle different plugin slug formats | |
| 95 | - if (strpos($plugin_slug, '/') !== false) { | |
| 96 | - // If it's a file path like 'plugin-name/plugin-name.php', extract directory | |
| 97 | - $plugin_slug_clean = dirname($plugin_slug); | |
| 98 | - } else { | |
| 99 | - // If it's just the plugin directory name, use it directly | |
| 100 | - $plugin_slug_clean = $plugin_slug; | |
| 101 | - } | |
| 102 | - | |
| 103 | - // Custom icon URLs for specific plugins that might not be on WordPress.org | |
| 104 | - $custom_icons = [ | |
| 105 | - 'ar-viewer' => [ | |
| 106 | - 'https://ps.w.org/ar-viewer/assets/icon-256x256.gif', | |
| 107 | - 'https://ps.w.org/ar-viewer/assets/icon-128x128.gif', | |
| 108 | - ], | |
| 109 | - ]; | |
| 110 | - | |
| 111 | - // Return custom icons if available, otherwise use default WordPress.org URLs | |
| 112 | - if (isset($custom_icons[$plugin_slug_clean])) { | |
| 113 | - return $custom_icons[$plugin_slug_clean]; | |
| 114 | - } | |
| 115 | - | |
| 116 | - return [ | |
| 117 | - "https://ps.w.org/{$plugin_slug_clean}/assets/icon-256x256.png", // Then PNG | |
| 118 | - "https://ps.w.org/{$plugin_slug_clean}/assets/icon-128x128.png", // Medium PNG | |
| 119 | - "https://ps.w.org/{$plugin_slug_clean}/assets/icon-256x256.gif", // Try GIF first | |
| 120 | - "https://ps.w.org/{$plugin_slug_clean}/assets/icon-128x128.gif", // Medium GIF | |
| 121 | - ]; | |
| 122 | - } | |
| 123 | - } | |
| 124 | 97 | ?> |
| 125 | 98 | |
| 126 | 99 | <div class="upk-dashboard-panel" |
| 127 | 100 | bdt-scrollspy="target: > div > div > .bdt-card; cls: bdt-animation-slide-bottom-small; delay: 300"> |
| @@ -218,9 +191,9 @@ | ||
| 218 | 191 | url: ajaxurl, |
| 219 | 192 | type: 'POST', |
| 220 | 193 | data: { |
| 221 | 194 | action: 'upk_get_plugins', |
| 222 | - nonce: '<?php echo wp_create_nonce("upk_get_plugins_nonce"); ?>' | |
| 195 | + nonce: '<?php echo esc_attr( wp_create_nonce("upk_get_plugins_nonce") ); ?>' | |
| 223 | 196 | }, |
| 224 | 197 | success: function(response) { |
| 225 | 198 | if (response.success && response.data) { |
| 226 | 199 | if (response.data.loading) { |
| @@ -239,14 +212,28 @@ | ||
| 239 | 212 | } |
| 240 | 213 | }); |
| 241 | 214 | } |
| 242 | 215 | |
| 216 | + // Escape remote-sourced strings before they are concatenated into | |
| 217 | + // markup. The plugin catalog comes from a remote endpoint; treat it | |
| 218 | + // as untrusted so a poisoned/compromised feed cannot inject HTML/JS | |
| 219 | + // into the admin dashboard (the 2026 notification-feed incident). | |
| 220 | + function upkEsc(s) { | |
| 221 | + return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) { | |
| 222 | + return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]; | |
| 223 | + }); | |
| 224 | + } | |
| 225 | + function upkSafeUrl(u) { | |
| 226 | + u = String(u == null ? '' : u); | |
| 227 | + return /^https?:\/\//i.test(u) ? u : ''; | |
| 228 | + } | |
| 229 | + | |
| 243 | 230 | // Function to render plugins |
| 244 | 231 | function renderPlugins(plugins) { |
| 245 | 232 | var html = ''; |
| 246 | 233 | |
| 247 | 234 | if (plugins.length === 0) { |
| 248 | - html = '<div class="bdt-text-center bdt-padding-large"><p><?php esc_html_e('No plugins available.', 'ultimate-post-kit'); ?></p></div>'; | |
| 235 | + html = '<div class="bdt-text-center bdt-padding-large"><p><?php echo esc_js(__('No plugins available.', 'ultimate-post-kit')); ?></p></div>'; | |
| 249 | 236 | } else { |
| 250 | 237 | plugins.forEach(function(plugin) { |
| 251 | 238 | // Skip own plugin (Ultimate Post Kit) when printing only; data still includes it for other plugins |
| 252 | 239 | if (plugin.slug === 'ultimate-post-kit') return; |
| @@ -254,39 +241,37 @@ | ||
| 254 | 241 | var logoUrl = plugin.logo || ''; |
| 255 | 242 | var pluginName = plugin.name || ''; |
| 256 | 243 | var pluginSlug = plugin.slug || ''; |
| 257 | 244 | |
| 258 | - // Generate fallback logo URL if needed | |
| 259 | - if (!logoUrl) { | |
| 260 | - var actualSlug = pluginSlug.replace('.php', '').split('/')[0]; | |
| 261 | - logoUrl = 'https://ps.w.org/' + actualSlug + '/assets/icon-256x256.png'; | |
| 262 | - } | |
| 263 | - | |
| 245 | + // The logo URL comes from the WordPress.org API response. When it is | |
| 246 | + // missing we show the local placeholder rather than guessing a remote | |
| 247 | + // asset URL. | |
| 248 | + var logoMarkup = upkSafeUrl(logoUrl) | |
| 249 | + ? '<img src="' + upkEsc(upkSafeUrl(logoUrl)) + '" alt="' + upkEsc(pluginName) + '" class="bdt-plugin-logo" ' + | |
| 250 | + 'onerror="this.style.display=\'none\'; this.nextElementSibling.style.display=\'flex\';">' + | |
| 251 | + '<div class="default-plugin-icon" style="display:none;">📦</div>' | |
| 252 | + : '<div class="default-plugin-icon" style="display:flex;">📦</div>'; | |
| 253 | + | |
| 264 | 254 | html += '<div class="bdt-card bdt-card-body bdt-flex bdt-flex-middle bdt-flex-between">' + |
| 265 | 255 | '<div class="bdt-others-plugin-content">' + |
| 266 | 256 | '<div class="bdt-plugin-logo-wrap bdt-flex bdt-flex-middle">' + |
| 267 | 257 | '<div class="bdt-plugin-logo-container">' + |
| 268 | - '<img src="' + logoUrl + '" alt="' + pluginName + '" class="bdt-plugin-logo" ' + | |
| 269 | - 'onerror="this.style.display=\'none\'; this.nextElementSibling.style.display=\'flex\';">' + | |
| 270 | - '<div class="default-plugin-icon" style="display:none;">📦</div>' + | |
| 258 | + logoMarkup + | |
| 271 | 259 | '</div>' + |
| 272 | 260 | '<div class="bdt-others-plugin-user-wrap bdt-flex bdt-flex-middle">' + |
| 273 | - '<h1 class="upk-feature-title">' + pluginName + '</h1>' + | |
| 261 | + '<h1 class="upk-feature-title">' + upkEsc(pluginName) + '</h1>' + | |
| 274 | 262 | '</div>' + |
| 275 | 263 | '</div>' + |
| 276 | 264 | '<div class="bdt-others-plugin-content-text bdt-margin-top">'; |
| 277 | - | |
| 278 | - if (plugin.description) { | |
| 279 | - html += '<p>' + plugin.description + '</p>'; | |
| 280 | - } | |
| 281 | - | |
| 265 | + | |
| 282 | 266 | // Active installs |
| 267 | + var installsCount = Number(plugin.active_installs_count) || 0; | |
| 283 | 268 | html += '<span class="active-installs bdt-margin-small-top">' + |
| 284 | - '<?php esc_html_e("Active Installs: ", "ultimate-post-kit"); ?> '; | |
| 285 | - if (plugin.active_installs_count > 0) { | |
| 286 | - html += '<span class="installs-count">' + plugin.active_installs_count.toLocaleString() + '+</span>'; | |
| 269 | + '<?php echo esc_js(__('Active Installs: ', 'ultimate-post-kit')); ?> '; | |
| 270 | + if (installsCount > 0) { | |
| 271 | + html += '<span class="installs-count">' + upkEsc(installsCount.toLocaleString()) + '+</span>'; | |
| 287 | 272 | } else { |
| 288 | - html += '<span class="installs-count">Fewer than 10</span>'; | |
| 273 | + html += '<span class="installs-count"><?php echo esc_js(__('Fewer than 10', 'ultimate-post-kit')); ?></span>'; | |
| 289 | 274 | } |
| 290 | 275 | html += '</span>'; |
| 291 | 276 | |
| 292 | 277 | // Rating |
| @@ -292,29 +277,32 @@ | ||
| 292 | 277 | // Rating |
| 293 | 278 | html += '<div class="bdt-others-plugin-rating bdt-margin-small-top bdt-flex bdt-flex-middle">' + |
| 294 | 279 | '<span class="bdt-others-plugin-rating-stars">'; |
| 295 | 280 | |
| 296 | - var rating = parseFloat(plugin.rating) || 0; | |
| 281 | + // Clamp to 0-5 so malformed data cannot emit a runaway number of stars. | |
| 282 | + var rating = Math.min(5, Math.max(0, parseFloat(plugin.rating) || 0)); | |
| 297 | 283 | var fullStars = Math.floor(rating); |
| 298 | 284 | var hasHalfStar = (rating - fullStars) >= 0.5; |
| 299 | 285 | var emptyStars = 5 - fullStars - (hasHalfStar ? 1 : 0); |
| 300 | - | |
| 301 | - for (var i = 0; i < fullStars; i++) { | |
| 286 | + var i; | |
| 287 | + | |
| 288 | + for (i = 0; i < fullStars; i++) { | |
| 302 | 289 | html += '<i class="dashicons dashicons-star-filled"></i>'; |
| 303 | 290 | } |
| 304 | 291 | if (hasHalfStar) { |
| 305 | 292 | html += '<i class="dashicons dashicons-star-half"></i>'; |
| 306 | 293 | } |
| 307 | - for (var i = 0; i < emptyStars; i++) { | |
| 294 | + for (i = 0; i < emptyStars; i++) { | |
| 308 | 295 | html += '<i class="dashicons dashicons-star-empty"></i>'; |
| 309 | 296 | } |
| 310 | 297 | |
| 311 | 298 | html += '</span>' + |
| 312 | 299 | '<span class="bdt-others-plugin-rating-text bdt-margin-small-left">' + |
| 313 | - rating + ' <?php esc_html_e("out of 5 stars.", "ultimate-post-kit"); ?>'; | |
| 300 | + rating + ' <?php echo esc_js(__('out of 5 stars.', 'ultimate-post-kit')); ?>'; | |
| 314 | 301 | |
| 315 | - if (plugin.num_ratings > 0) { | |
| 316 | - html += '<span class="rating-count">(' + plugin.num_ratings.toLocaleString() + ' <?php esc_html_e("ratings", "ultimate-post-kit"); ?>)</span>'; | |
| 302 | + var numRatings = Number(plugin.num_ratings) || 0; | |
| 303 | + if (numRatings > 0) { | |
| 304 | + html += '<span class="rating-count">(' + upkEsc(numRatings.toLocaleString()) + ' <?php echo esc_js(__('ratings', 'ultimate-post-kit')); ?>)</span>'; | |
| 317 | 305 | } |
| 318 | 306 | |
| 319 | 307 | html += '</span></div>'; |
| 320 | 308 | |
| @@ -320,9 +308,9 @@ | ||
| 320 | 308 | |
| 321 | 309 | // Downloads |
| 322 | 310 | if (plugin.downloaded_formatted) { |
| 323 | 311 | html += '<div class="bdt-others-plugin-downloads bdt-margin-small-top">' + |
| 324 | - '<span><?php esc_html_e("Downloads: ", "ultimate-post-kit"); ?>' + plugin.downloaded_formatted + '</span>' + | |
| 312 | + '<span><?php echo esc_js(__('Downloads: ', 'ultimate-post-kit')); ?>' + upkEsc(plugin.downloaded_formatted) + '</span>' + | |
| 325 | 313 | '</div>'; |
| 326 | 314 | } |
| 327 | 315 | |
| 328 | 316 | // Last updated |
| @@ -327,9 +315,9 @@ | ||
| 327 | 315 | |
| 328 | 316 | // Last updated |
| 329 | 317 | if (plugin.last_updated_formatted) { |
| 330 | 318 | html += '<div class="bdt-others-plugin-updated bdt-margin-small-top">' + |
| 331 | - '<span><?php esc_html_e("Last Updated: ", "ultimate-post-kit"); ?>' + plugin.last_updated_formatted + '</span>' + | |
| 319 | + '<span><?php echo esc_js(__('Last Updated: ', 'ultimate-post-kit')); ?>' + upkEsc(plugin.last_updated_formatted) + '</span>' + | |
| 332 | 320 | '</div>'; |
| 333 | 321 | } |
| 334 | 322 | |
| 335 | 323 | html += '</div></div>' + |
| @@ -338,24 +326,28 @@ | ||
| 338 | 326 | // Show different buttons based on plugin status |
| 339 | 327 | if (plugin.status === 'active') { |
| 340 | 328 | html += '<span class="bdt-button bdt-button-success bdt-disabled">' + |
| 341 | 329 | '<span class="dashicons dashicons-yes"></span> ' + |
| 342 | - '<?php esc_html_e("Active", "ultimate-post-kit"); ?>' + | |
| 330 | + '<?php echo esc_js(__('Active', 'ultimate-post-kit')); ?>' + | |
| 343 | 331 | '</span>'; |
| 344 | 332 | } else if (plugin.status === 'installed') { |
| 345 | - var activateUrl = '<?php echo admin_url("plugins.php?action=activate&plugin="); ?>' + plugin.plugin_file + '&_wpnonce=' + plugin.activate_nonce; | |
| 346 | - html += '<a class="bdt-button bdt-welcome-button" href="' + activateUrl + '">' + | |
| 347 | - '<?php esc_html_e("Activate", "ultimate-post-kit"); ?>' + | |
| 333 | + // URL-encode the query values: plugin_file contains slashes and | |
| 334 | + // both parts land inside an href attribute. | |
| 335 | + var activateUrl = <?php echo wp_json_encode( esc_url_raw( admin_url( 'plugins.php?action=activate&plugin=' ) ) ); ?> + | |
| 336 | + encodeURIComponent(plugin.plugin_file || '') + | |
| 337 | + '&_wpnonce=' + encodeURIComponent(plugin.activate_nonce || ''); | |
| 338 | + html += '<a class="bdt-button bdt-welcome-button" href="' + upkEsc(activateUrl) + '">' + | |
| 339 | + '<?php echo esc_js(__('Activate', 'ultimate-post-kit')); ?>' + | |
| 348 | 340 | '</a>'; |
| 349 | 341 | } else { |
| 350 | - html += '<button class="bdt-button bdt-welcome-button upk-install-plugin" data-plugin-slug="' + pluginSlug + '" data-nonce="<?php echo wp_create_nonce('upk_install_plugin_nonce'); ?>">' + | |
| 351 | - '<?php esc_html_e("Install", "ultimate-post-kit"); ?>' + | |
| 342 | + html += '<button type="button" class="bdt-button bdt-welcome-button upk-install-plugin" data-plugin-slug="' + upkEsc(pluginSlug) + '" data-nonce="<?php echo esc_attr( wp_create_nonce('upk_install_plugin_nonce') ); ?>">' + | |
| 343 | + '<?php echo esc_js(__('Install', 'ultimate-post-kit')); ?>' + | |
| 352 | 344 | '</button>'; |
| 353 | 345 | } |
| 354 | 346 | |
| 355 | - if (plugin.homepage) { | |
| 356 | - html += '<a class="bdt-button bdt-dashboard-sec-btn" target="_blank" href="' + plugin.homepage + '">' + | |
| 357 | - '<?php esc_html_e("Learn More", "ultimate-post-kit"); ?>' + | |
| 347 | + if (plugin.homepage && upkSafeUrl(plugin.homepage)) { | |
| 348 | + html += '<a class="bdt-button bdt-dashboard-sec-btn" target="_blank" rel="noopener noreferrer" href="' + upkEsc(upkSafeUrl(plugin.homepage)) + '">' + | |
| 349 | + '<?php echo esc_js(__('Learn More', 'ultimate-post-kit')); ?>' + | |
| 358 | 350 | '</a>'; |
| 359 | 351 | } |
| 360 | 352 | |
| 361 | 353 | html += '</div></div>'; |
| @@ -363,10 +355,13 @@ | ||
| 363 | 355 | } |
| 364 | 356 | |
| 365 | 357 | $list.html(html); |
| 366 | 358 | |
| 367 | - // Handle plugin action buttons | |
| 368 | - $('.upk-install-plugin').on('click', function(e) { | |
| 359 | + // Handle plugin action buttons. Delegated from the list and | |
| 360 | + // namespaced+unbound first: renderPlugins() runs again on every | |
| 361 | + // retry, and a plain global bind stacked one handler per render, | |
| 362 | + // firing duplicate install requests for a single click. | |
| 363 | + $list.off('click.upkInstall').on('click.upkInstall', '.upk-install-plugin', function(e) { | |
| 369 | 364 | e.preventDefault(); |
| 370 | 365 | |
| 371 | 366 | var $button = $(this); |
| 372 | 367 | var pluginSlug = $button.data('plugin-slug'); |
| @@ -379,9 +374,9 @@ | ||
| 379 | 374 | .addClass('bdt-installing'); |
| 380 | 375 | |
| 381 | 376 | // Perform AJAX request |
| 382 | 377 | $.ajax({ |
| 383 | - url: '<?php echo admin_url('admin-ajax.php'); ?>', | |
| 378 | + url: '<?php echo esc_url( admin_url('admin-ajax.php') ); ?>', | |
| 384 | 379 | type: 'POST', |
| 385 | 380 | data: { |
| 386 | 381 | action: 'upk_install_plugin', |
| 387 | 382 | plugin_slug: pluginSlug, |
| @@ -454,12 +449,15 @@ | ||
| 454 | 449 | '<div class="upk-loading-dot"></div>' + |
| 455 | 450 | '<div class="upk-loading-dot"></div>' + |
| 456 | 451 | '</div>' + |
| 457 | 452 | '</div>' + |
| 458 | - '<p class="bdt-margin-small-top bdt-text-muted"><?php esc_html_e("Loading plugin data...", "ultimate-post-kit"); ?></p>' + | |
| 453 | + '<p class="bdt-margin-small-top bdt-text-muted"><?php echo esc_js(__('Loading plugin data...', 'ultimate-post-kit')); ?></p>' + | |
| 459 | 454 | '</div>' |
| 460 | 455 | ); |
| 461 | - $list.show(); | |
| 456 | + // Set the display explicitly: the list is a CSS grid, and jQuery's | |
| 457 | + // .show() can resolve the inline display:none to "block", which would | |
| 458 | + // flatten the card grid into a single stacked column. | |
| 459 | + $list.css('display', 'grid'); | |
| 462 | 460 | } |
| 463 | 461 | |
| 464 | 462 | // Function to show error |
| 465 | 463 | function showError() { |
| @@ -482,13 +480,20 @@ | ||
| 482 | 480 | /** |
| 483 | 481 | * AJAX handler for getting plugins data |
| 484 | 482 | */ |
| 485 | 483 | public function ajax_get_plugins() { |
| 486 | - // Verify nonce | |
| 484 | + // Verify nonce. Respond with JSON -- the caller parses the response as | |
| 485 | + // JSON, so wp_die() here would surface as a generic "unable to load". | |
| 487 | 486 | if (!check_ajax_referer('upk_get_plugins_nonce', 'nonce', false)) { |
| 488 | - wp_die(__('Security check failed.', 'ultimate-post-kit')); | |
| 487 | + wp_send_json_error(['message' => __('Security check failed.', 'ultimate-post-kit')], 403); | |
| 489 | 488 | } |
| 490 | 489 | |
| 490 | + // This data is only ever used on the plugin-install screen; gate it to | |
| 491 | + // users who could act on it rather than exposing it to any visitor. | |
| 492 | + if (!current_user_can('install_plugins')) { | |
| 493 | + wp_send_json_error(['message' => __('You do not have permission to do this.', 'ultimate-post-kit')], 403); | |
| 494 | + } | |
| 495 | + | |
| 491 | 496 | // Get cached data |
| 492 | 497 | $plugins_data = \UltimatePostKit\SetupWizard\Remote_Data_Handler::get_remote_plugins(); |
| 493 | 498 | |
| 494 | 499 | // If cache is empty, try to fetch immediately (but don't block) |
| @@ -516,9 +521,10 @@ | ||
| 516 | 521 | * AJAX handler for plugin installation |
| 517 | 522 | */ |
| 518 | 523 | public function install_plugin_ajax() { |
| 519 | 524 | // Check nonce |
| 520 | - if (!wp_verify_nonce($_POST['nonce'], 'upk_install_plugin_nonce')) { | |
| 525 | + $nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : ''; | |
| 526 | + if (!wp_verify_nonce( $nonce, 'upk_install_plugin_nonce')) { | |
| 521 | 527 | wp_send_json_error(['message' => __('Security check failed', 'ultimate-post-kit')]); |
| 522 | 528 | } |
| 523 | 529 | |
| 524 | 530 | // Check user capability |
| @@ -525,9 +531,9 @@ | ||
| 525 | 531 | if (!current_user_can('install_plugins')) { |
| 526 | 532 | wp_send_json_error(['message' => __('You do not have permission to install plugins', 'ultimate-post-kit')]); |
| 527 | 533 | } |
| 528 | 534 | |
| 529 | - $plugin_slug = sanitize_text_field($_POST['plugin_slug']); | |
| 535 | + $plugin_slug = isset($_POST['plugin_slug']) ? sanitize_text_field(wp_unslash($_POST['plugin_slug'])) : ''; | |
| 530 | 536 | |
| 531 | 537 | if (empty($plugin_slug)) { |
| 532 | 538 | wp_send_json_error(['message' => __('Plugin slug is required', 'ultimate-post-kit')]); |
| 533 | 539 | } |