Changeset 3464033
- Timestamp:
- 02/18/2026 07:58:27 AM (6 weeks ago)
- Location:
- visual-portfolio
- Files:
-
- 12 edited
- 1 copied
-
tags/3.5.2 (copied) (copied from visual-portfolio/trunk)
-
tags/3.5.2/CHANGELOG.md (modified) (1 diff)
-
tags/3.5.2/class-visual-portfolio.php (modified) (2 diffs)
-
tags/3.5.2/classes/class-security.php (modified) (2 diffs)
-
tags/3.5.2/classes/class-templates.php (modified) (3 diffs)
-
tags/3.5.2/languages/visual-portfolio.pot (modified) (2 diffs)
-
tags/3.5.2/readme.txt (modified) (2 diffs)
-
trunk/CHANGELOG.md (modified) (1 diff)
-
trunk/class-visual-portfolio.php (modified) (2 diffs)
-
trunk/classes/class-security.php (modified) (2 diffs)
-
trunk/classes/class-templates.php (modified) (3 diffs)
-
trunk/languages/visual-portfolio.pot (modified) (2 diffs)
-
trunk/readme.txt (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
visual-portfolio/tags/3.5.2/CHANGELOG.md
r3459565 r3464033 2 2 3 3 All 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 4 8 5 9 = 3.5.1 - Feb 12, 2026 = -
visual-portfolio/tags/3.5.2/class-visual-portfolio.php
r3459565 r3464033 3 3 * Plugin Name: Visual Portfolio, Posts & Image Gallery 4 4 * 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. 15 * Version: 3.5.2 6 6 * Plugin URI: https://www.visualportfolio.com/?utm_source=wordpress.org&utm_medium=readme&utm_campaign=byline 7 7 * Author: Visual Portfolio Team … … 19 19 20 20 if ( ! defined( 'VISUAL_PORTFOLIO_VERSION' ) ) { 21 define( 'VISUAL_PORTFOLIO_VERSION', '3.5. 1' );21 define( 'VISUAL_PORTFOLIO_VERSION', '3.5.2' ); 22 22 } 23 23 -
visual-portfolio/tags/3.5.2/classes/class-security.php
r3318088 r3464033 233 233 ) 234 234 ) { 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 ) ) { 235 284 $attribute = self::reset_control_attribute_to_default( $attribute, $control ); 236 285 } … … 474 523 break; 475 524 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; 476 528 case 'text': 477 529 case 'radio': -
visual-portfolio/tags/3.5.2/classes/class-templates.php
r3001683 r3464033 17 17 */ 18 18 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 19 24 // Allow 3rd party plugin filter template args from their plugin. 20 25 $args = apply_filters( 'vpf_include_template_args', $args, $template_name ); … … 42 47 43 48 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 } 45 55 } 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; 46 110 } 47 111 … … 53 117 */ 54 118 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 55 127 $template = ''; 56 128 $template_version = ''; -
visual-portfolio/tags/3.5.2/languages/visual-portfolio.pot
r3459565 r3464033 3 3 msgid "" 4 4 msgstr "" 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" 6 6 "Report-Msgid-Bugs-To: https://github.com/nk-crew/visual-portfolio/issues\n" 7 7 "Last-Translator: Visual Portfolio\n" … … 10 10 "Content-Type: text/plain; charset=UTF-8\n" 11 11 "Content-Transfer-Encoding: 8bit\n" 12 "POT-Creation-Date: 2026-02-1 2T06:11:19+00:00\n"12 "POT-Creation-Date: 2026-02-18T07:57:00+00:00\n" 13 13 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 14 "X-Generator: WP-CLI 2.12.0\n" -
visual-portfolio/tags/3.5.2/readme.txt
r3459565 r3464033 7 7 * Tested up to: 6.9 8 8 * Requires PHP: 7.2 9 * Stable tag: 3.5. 19 * Stable tag: 3.5.2 10 10 * License: GPLv2 or later 11 11 * License URI: <http://www.gnu.org/licenses/gpl-2.0.html> … … 324 324 325 325 ## Changelog ## 326 327 = 3.5.2 - Feb 18, 2026 = 328 329 * security: fixed local file inclusion via path traversal 326 330 327 331 = 3.5.1 - Feb 12, 2026 = -
visual-portfolio/trunk/CHANGELOG.md
r3459565 r3464033 2 2 3 3 All 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 4 8 5 9 = 3.5.1 - Feb 12, 2026 = -
visual-portfolio/trunk/class-visual-portfolio.php
r3459565 r3464033 3 3 * Plugin Name: Visual Portfolio, Posts & Image Gallery 4 4 * 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. 15 * Version: 3.5.2 6 6 * Plugin URI: https://www.visualportfolio.com/?utm_source=wordpress.org&utm_medium=readme&utm_campaign=byline 7 7 * Author: Visual Portfolio Team … … 19 19 20 20 if ( ! defined( 'VISUAL_PORTFOLIO_VERSION' ) ) { 21 define( 'VISUAL_PORTFOLIO_VERSION', '3.5. 1' );21 define( 'VISUAL_PORTFOLIO_VERSION', '3.5.2' ); 22 22 } 23 23 -
visual-portfolio/trunk/classes/class-security.php
r3318088 r3464033 233 233 ) 234 234 ) { 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 ) ) { 235 284 $attribute = self::reset_control_attribute_to_default( $attribute, $control ); 236 285 } … … 474 523 break; 475 524 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; 476 528 case 'text': 477 529 case 'radio': -
visual-portfolio/trunk/classes/class-templates.php
r3001683 r3464033 17 17 */ 18 18 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 19 24 // Allow 3rd party plugin filter template args from their plugin. 20 25 $args = apply_filters( 'vpf_include_template_args', $args, $template_name ); … … 42 47 43 48 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 } 45 55 } 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; 46 110 } 47 111 … … 53 117 */ 54 118 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 55 127 $template = ''; 56 128 $template_version = ''; -
visual-portfolio/trunk/languages/visual-portfolio.pot
r3459565 r3464033 3 3 msgid "" 4 4 msgstr "" 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" 6 6 "Report-Msgid-Bugs-To: https://github.com/nk-crew/visual-portfolio/issues\n" 7 7 "Last-Translator: Visual Portfolio\n" … … 10 10 "Content-Type: text/plain; charset=UTF-8\n" 11 11 "Content-Transfer-Encoding: 8bit\n" 12 "POT-Creation-Date: 2026-02-1 2T06:11:19+00:00\n"12 "POT-Creation-Date: 2026-02-18T07:57:00+00:00\n" 13 13 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 14 "X-Generator: WP-CLI 2.12.0\n" -
visual-portfolio/trunk/readme.txt
r3459565 r3464033 7 7 * Tested up to: 6.9 8 8 * Requires PHP: 7.2 9 * Stable tag: 3.5. 19 * Stable tag: 3.5.2 10 10 * License: GPLv2 or later 11 11 * License URI: <http://www.gnu.org/licenses/gpl-2.0.html> … … 324 324 325 325 ## Changelog ## 326 327 = 3.5.2 - Feb 18, 2026 = 328 329 * security: fixed local file inclusion via path traversal 326 330 327 331 = 3.5.1 - Feb 12, 2026 =
Note: See TracChangeset
for help on using the changeset viewer.