Plugin Directory

Changeset 3464033


Ignore:
Timestamp:
02/18/2026 07:58:27 AM (6 weeks ago)
Author:
nko
Message:

Update to version 3.5.2 from GitHub

Location:
visual-portfolio
Files:
12 edited
1 copied

Legend:

Unmodified
Added
Removed
  • visual-portfolio/tags/3.5.2/CHANGELOG.md

    r3459565 r3464033  
    22
    33All notable changes to this project will be documented in this file.
     4
     5= 3.5.2 - Feb 18, 2026 =
     6
     7* security: fixed local file inclusion via path traversal
    48
    59= 3.5.1 - Feb 12, 2026 =
  • visual-portfolio/tags/3.5.2/class-visual-portfolio.php

    r3459565 r3464033  
    33 * Plugin Name:  Visual Portfolio, Posts & Image Gallery
    44 * Description:  Modern gallery and portfolio plugin with advanced layouts editor. Clean and powerful gallery styles with enormous settings in the Gutenberg block.
    5  * Version:      3.5.1
     5 * Version:      3.5.2
    66 * Plugin URI:   https://www.visualportfolio.com/?utm_source=wordpress.org&utm_medium=readme&utm_campaign=byline
    77 * Author:       Visual Portfolio Team
     
    1919
    2020if ( ! defined( 'VISUAL_PORTFOLIO_VERSION' ) ) {
    21     define( 'VISUAL_PORTFOLIO_VERSION', '3.5.1' );
     21    define( 'VISUAL_PORTFOLIO_VERSION', '3.5.2' );
    2222}
    2323
  • visual-portfolio/tags/3.5.2/classes/class-security.php

    r3318088 r3464033  
    233233            )
    234234        ) {
     235            $attribute = self::reset_control_attribute_to_default( $attribute, $control );
     236        }
     237
     238        if ( is_numeric( $attribute ) ) {
     239            if ( false === strpos( $attribute, '.' ) ) {
     240                $attribute = intval( $attribute );
     241            } else {
     242                $attribute = (float) $attribute;
     243            }
     244        } else {
     245            $attribute = sanitize_text_field( wp_unslash( $attribute ) );
     246        }
     247
     248        return $attribute;
     249    }
     250
     251    /**
     252     * Sanitize icons selector attribute.
     253     *
     254     * Icons selector options are usually provided as an indexed array where each
     255     * option contains a `value` key, unlike regular select controls that may use
     256     * associative options by key.
     257     *
     258     * @param int|float|string $attribute - Unclear Selector Attribute.
     259     * @param array            $control - Array of control parameters.
     260     * @return int|float|string
     261     */
     262    public static function sanitize_icons_selector( $attribute, $control ) {
     263        $valid_options = array();
     264
     265        if ( isset( $control['options'] ) && is_array( $control['options'] ) ) {
     266            foreach ( $control['options'] as $option_key => $option_data ) {
     267                if ( is_array( $option_data ) && isset( $option_data['value'] ) ) {
     268                    $valid_options[] = (string) $option_data['value'];
     269                } else {
     270                    $valid_options[] = (string) $option_key;
     271                }
     272            }
     273        }
     274
     275        $attribute_string = is_bool( $attribute ) ? ( $attribute ? 'true' : 'false' ) : (string) $attribute;
     276
     277        // Reject path traversal sequences regardless of control options state.
     278        if ( validate_file( $attribute_string ) !== 0 ) {
     279            $attribute = self::reset_control_attribute_to_default( $attribute, $control );
     280        }
     281
     282        // Apply strict allowlist only when options are available.
     283        if ( ! empty( $valid_options ) && ! in_array( $attribute_string, $valid_options, true ) ) {
    235284            $attribute = self::reset_control_attribute_to_default( $attribute, $control );
    236285        }
     
    474523                            break;
    475524                        case 'icons_selector':
     525                            // Layer 2: Validate against allowed options (same as 'select' type).
     526                            $attributes[ $key ] = self::sanitize_icons_selector( $attributes[ $key ], $controls[ $key ] );
     527                            break;
    476528                        case 'text':
    477529                        case 'radio':
  • visual-portfolio/tags/3.5.2/classes/class-templates.php

    r3001683 r3464033  
    1717     */
    1818    public static function include_template( $template_name, $args = array() ) {
     19        // Layer 1: Reject template names containing path traversal sequences.
     20        if ( validate_file( $template_name ) !== 0 ) {
     21            return;
     22        }
     23
    1924        // Allow 3rd party plugin filter template args from their plugin.
    2025        $args = apply_filters( 'vpf_include_template_args', $args, $template_name );
     
    4247
    4348        if ( file_exists( $template ) ) {
    44             include $template;
     49            // Layer 3: Verify the resolved path is within allowed directories.
     50            $real_path = realpath( $template );
     51
     52            if ( $real_path && self::is_allowed_template_path( $real_path ) ) {
     53                include $template;
     54            }
    4555        }
     56    }
     57
     58    /**
     59     * Check if a resolved file path is within allowed template directories.
     60     *
     61     * Layer 3: Prevents inclusion of files outside expected template directories,
     62     * even if path traversal bypasses other checks.
     63     *
     64     * @param string $real_path The resolved (realpath) file path to check.
     65     * @return bool True if the path is within an allowed directory.
     66     */
     67    public static function is_allowed_template_path( $real_path ) {
     68        $normalized_real_path = wp_normalize_path( $real_path );
     69
     70        if ( ! $normalized_real_path ) {
     71            return false;
     72        }
     73
     74        $allowed_dirs = array(
     75            visual_portfolio()->plugin_path . 'templates/',
     76            get_stylesheet_directory() . '/visual-portfolio/',
     77            get_template_directory() . '/visual-portfolio/',
     78        );
     79
     80        if ( visual_portfolio()->pro_plugin_path ) {
     81            $allowed_dirs[] = visual_portfolio()->pro_plugin_path . 'templates/';
     82        }
     83
     84        /**
     85         * Filters the list of allowed template directories.
     86         *
     87         * This is used by the Layer 3 realpath() inclusion guard.
     88         * Add your plugin directory here if you return a custom absolute template
     89         * path via the `vpf_include_template` filter.
     90         *
     91         * @since 3.5.2
     92         *
     93         * @param array  $allowed_dirs Allowed directories (absolute paths).
     94         * @param string $real_path    Resolved real path to the included template.
     95         */
     96        $allowed_dirs = (array) apply_filters( 'vpf_allowed_template_dirs', $allowed_dirs, $real_path );
     97
     98        // Resolve all allowed directories to their real paths.
     99        $allowed_dirs = array_filter( array_map( 'realpath', $allowed_dirs ) );
     100
     101        foreach ( $allowed_dirs as $dir ) {
     102            $normalized_dir = trailingslashit( wp_normalize_path( $dir ) );
     103
     104            if ( strpos( $normalized_real_path, $normalized_dir ) === 0 ) {
     105                return true;
     106            }
     107        }
     108
     109        return false;
    46110    }
    47111
     
    53117     */
    54118    public static function find_template_styles( $template_name ) {
     119        // Layer 1: Reject template names containing path traversal sequences.
     120        if ( validate_file( $template_name ) !== 0 ) {
     121            return array(
     122                'path'    => '',
     123                'version' => '',
     124            );
     125        }
     126
    55127        $template         = '';
    56128        $template_version = '';
  • visual-portfolio/tags/3.5.2/languages/visual-portfolio.pot

    r3459565 r3464033  
    33msgid ""
    44msgstr ""
    5 "Project-Id-Version: Visual Portfolio, Posts & Image Gallery 3.5.1\n"
     5"Project-Id-Version: Visual Portfolio, Posts & Image Gallery 3.5.2\n"
    66"Report-Msgid-Bugs-To: https://github.com/nk-crew/visual-portfolio/issues\n"
    77"Last-Translator: Visual Portfolio\n"
     
    1010"Content-Type: text/plain; charset=UTF-8\n"
    1111"Content-Transfer-Encoding: 8bit\n"
    12 "POT-Creation-Date: 2026-02-12T06:11:19+00:00\n"
     12"POT-Creation-Date: 2026-02-18T07:57:00+00:00\n"
    1313"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
    1414"X-Generator: WP-CLI 2.12.0\n"
  • visual-portfolio/tags/3.5.2/readme.txt

    r3459565 r3464033  
    77* Tested up to: 6.9
    88* Requires PHP: 7.2
    9 * Stable tag: 3.5.1
     9* Stable tag: 3.5.2
    1010* License: GPLv2 or later
    1111* License URI: <http://www.gnu.org/licenses/gpl-2.0.html>
     
    324324
    325325## Changelog ##
     326
     327= 3.5.2 - Feb 18, 2026 =
     328
     329* security: fixed local file inclusion via path traversal
    326330
    327331= 3.5.1 - Feb 12, 2026 =
  • visual-portfolio/trunk/CHANGELOG.md

    r3459565 r3464033  
    22
    33All notable changes to this project will be documented in this file.
     4
     5= 3.5.2 - Feb 18, 2026 =
     6
     7* security: fixed local file inclusion via path traversal
    48
    59= 3.5.1 - Feb 12, 2026 =
  • visual-portfolio/trunk/class-visual-portfolio.php

    r3459565 r3464033  
    33 * Plugin Name:  Visual Portfolio, Posts & Image Gallery
    44 * Description:  Modern gallery and portfolio plugin with advanced layouts editor. Clean and powerful gallery styles with enormous settings in the Gutenberg block.
    5  * Version:      3.5.1
     5 * Version:      3.5.2
    66 * Plugin URI:   https://www.visualportfolio.com/?utm_source=wordpress.org&utm_medium=readme&utm_campaign=byline
    77 * Author:       Visual Portfolio Team
     
    1919
    2020if ( ! defined( 'VISUAL_PORTFOLIO_VERSION' ) ) {
    21     define( 'VISUAL_PORTFOLIO_VERSION', '3.5.1' );
     21    define( 'VISUAL_PORTFOLIO_VERSION', '3.5.2' );
    2222}
    2323
  • visual-portfolio/trunk/classes/class-security.php

    r3318088 r3464033  
    233233            )
    234234        ) {
     235            $attribute = self::reset_control_attribute_to_default( $attribute, $control );
     236        }
     237
     238        if ( is_numeric( $attribute ) ) {
     239            if ( false === strpos( $attribute, '.' ) ) {
     240                $attribute = intval( $attribute );
     241            } else {
     242                $attribute = (float) $attribute;
     243            }
     244        } else {
     245            $attribute = sanitize_text_field( wp_unslash( $attribute ) );
     246        }
     247
     248        return $attribute;
     249    }
     250
     251    /**
     252     * Sanitize icons selector attribute.
     253     *
     254     * Icons selector options are usually provided as an indexed array where each
     255     * option contains a `value` key, unlike regular select controls that may use
     256     * associative options by key.
     257     *
     258     * @param int|float|string $attribute - Unclear Selector Attribute.
     259     * @param array            $control - Array of control parameters.
     260     * @return int|float|string
     261     */
     262    public static function sanitize_icons_selector( $attribute, $control ) {
     263        $valid_options = array();
     264
     265        if ( isset( $control['options'] ) && is_array( $control['options'] ) ) {
     266            foreach ( $control['options'] as $option_key => $option_data ) {
     267                if ( is_array( $option_data ) && isset( $option_data['value'] ) ) {
     268                    $valid_options[] = (string) $option_data['value'];
     269                } else {
     270                    $valid_options[] = (string) $option_key;
     271                }
     272            }
     273        }
     274
     275        $attribute_string = is_bool( $attribute ) ? ( $attribute ? 'true' : 'false' ) : (string) $attribute;
     276
     277        // Reject path traversal sequences regardless of control options state.
     278        if ( validate_file( $attribute_string ) !== 0 ) {
     279            $attribute = self::reset_control_attribute_to_default( $attribute, $control );
     280        }
     281
     282        // Apply strict allowlist only when options are available.
     283        if ( ! empty( $valid_options ) && ! in_array( $attribute_string, $valid_options, true ) ) {
    235284            $attribute = self::reset_control_attribute_to_default( $attribute, $control );
    236285        }
     
    474523                            break;
    475524                        case 'icons_selector':
     525                            // Layer 2: Validate against allowed options (same as 'select' type).
     526                            $attributes[ $key ] = self::sanitize_icons_selector( $attributes[ $key ], $controls[ $key ] );
     527                            break;
    476528                        case 'text':
    477529                        case 'radio':
  • visual-portfolio/trunk/classes/class-templates.php

    r3001683 r3464033  
    1717     */
    1818    public static function include_template( $template_name, $args = array() ) {
     19        // Layer 1: Reject template names containing path traversal sequences.
     20        if ( validate_file( $template_name ) !== 0 ) {
     21            return;
     22        }
     23
    1924        // Allow 3rd party plugin filter template args from their plugin.
    2025        $args = apply_filters( 'vpf_include_template_args', $args, $template_name );
     
    4247
    4348        if ( file_exists( $template ) ) {
    44             include $template;
     49            // Layer 3: Verify the resolved path is within allowed directories.
     50            $real_path = realpath( $template );
     51
     52            if ( $real_path && self::is_allowed_template_path( $real_path ) ) {
     53                include $template;
     54            }
    4555        }
     56    }
     57
     58    /**
     59     * Check if a resolved file path is within allowed template directories.
     60     *
     61     * Layer 3: Prevents inclusion of files outside expected template directories,
     62     * even if path traversal bypasses other checks.
     63     *
     64     * @param string $real_path The resolved (realpath) file path to check.
     65     * @return bool True if the path is within an allowed directory.
     66     */
     67    public static function is_allowed_template_path( $real_path ) {
     68        $normalized_real_path = wp_normalize_path( $real_path );
     69
     70        if ( ! $normalized_real_path ) {
     71            return false;
     72        }
     73
     74        $allowed_dirs = array(
     75            visual_portfolio()->plugin_path . 'templates/',
     76            get_stylesheet_directory() . '/visual-portfolio/',
     77            get_template_directory() . '/visual-portfolio/',
     78        );
     79
     80        if ( visual_portfolio()->pro_plugin_path ) {
     81            $allowed_dirs[] = visual_portfolio()->pro_plugin_path . 'templates/';
     82        }
     83
     84        /**
     85         * Filters the list of allowed template directories.
     86         *
     87         * This is used by the Layer 3 realpath() inclusion guard.
     88         * Add your plugin directory here if you return a custom absolute template
     89         * path via the `vpf_include_template` filter.
     90         *
     91         * @since 3.5.2
     92         *
     93         * @param array  $allowed_dirs Allowed directories (absolute paths).
     94         * @param string $real_path    Resolved real path to the included template.
     95         */
     96        $allowed_dirs = (array) apply_filters( 'vpf_allowed_template_dirs', $allowed_dirs, $real_path );
     97
     98        // Resolve all allowed directories to their real paths.
     99        $allowed_dirs = array_filter( array_map( 'realpath', $allowed_dirs ) );
     100
     101        foreach ( $allowed_dirs as $dir ) {
     102            $normalized_dir = trailingslashit( wp_normalize_path( $dir ) );
     103
     104            if ( strpos( $normalized_real_path, $normalized_dir ) === 0 ) {
     105                return true;
     106            }
     107        }
     108
     109        return false;
    46110    }
    47111
     
    53117     */
    54118    public static function find_template_styles( $template_name ) {
     119        // Layer 1: Reject template names containing path traversal sequences.
     120        if ( validate_file( $template_name ) !== 0 ) {
     121            return array(
     122                'path'    => '',
     123                'version' => '',
     124            );
     125        }
     126
    55127        $template         = '';
    56128        $template_version = '';
  • visual-portfolio/trunk/languages/visual-portfolio.pot

    r3459565 r3464033  
    33msgid ""
    44msgstr ""
    5 "Project-Id-Version: Visual Portfolio, Posts & Image Gallery 3.5.1\n"
     5"Project-Id-Version: Visual Portfolio, Posts & Image Gallery 3.5.2\n"
    66"Report-Msgid-Bugs-To: https://github.com/nk-crew/visual-portfolio/issues\n"
    77"Last-Translator: Visual Portfolio\n"
     
    1010"Content-Type: text/plain; charset=UTF-8\n"
    1111"Content-Transfer-Encoding: 8bit\n"
    12 "POT-Creation-Date: 2026-02-12T06:11:19+00:00\n"
     12"POT-Creation-Date: 2026-02-18T07:57:00+00:00\n"
    1313"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
    1414"X-Generator: WP-CLI 2.12.0\n"
  • visual-portfolio/trunk/readme.txt

    r3459565 r3464033  
    77* Tested up to: 6.9
    88* Requires PHP: 7.2
    9 * Stable tag: 3.5.1
     9* Stable tag: 3.5.2
    1010* License: GPLv2 or later
    1111* License URI: <http://www.gnu.org/licenses/gpl-2.0.html>
     
    324324
    325325## Changelog ##
     326
     327= 3.5.2 - Feb 18, 2026 =
     328
     329* security: fixed local file inclusion via path traversal
    326330
    327331= 3.5.1 - Feb 12, 2026 =
Note: See TracChangeset for help on using the changeset viewer.