Plugin Directory

Changeset 3407949


Ignore:
Timestamp:
12/02/2025 12:02:09 PM (4 months ago)
Author:
wpchill
Message:

Update to version 2.13.3 from GitHub

Location:
modula-best-grid-gallery
Files:
8 edited
1 copied

Legend:

Unmodified
Added
Removed
  • modula-best-grid-gallery/tags/2.13.3/Modula.php

    r3398654 r3407949  
    55* Description:              Modula is the most powerful, user-friendly WordPress gallery plugin. Add galleries, masonry grids and more in a few clicks.
    66* Author:                   WPChill
    7 * Version:                  2.13.2
     7* Version:                  2.13.3
    88* Author URI:               https://www.wpchill.com/
    99* License:                  GPLv3 or later
     
    4848 */
    4949
    50 define( 'MODULA_LITE_VERSION', '2.13.0' );
     50define( 'MODULA_LITE_VERSION', '2.13.3' );
    5151define( 'MODULA_PATH', plugin_dir_path( __FILE__ ) );
    5252define( 'MODULA_URL', plugin_dir_url( __FILE__ ) );
  • modula-best-grid-gallery/tags/2.13.3/changelog.txt

    r3398654 r3407949  
     1= 2.13.3 - 02.12.2025 =
     2Fixed: Vulnerability in zip import.
     3
    14= 2.13.2 - 19.11.2025 =
    25Updated: Performance improvements.
  • modula-best-grid-gallery/tags/2.13.3/includes/admin/class-modula-gallery-upload.php

    r3395701 r3407949  
    10481048        $zip        = new ZipArchive();
    10491049        $zip_opened = $zip->open( $file );
    1050         if ( $zip_opened !== true ) {
     1050        if ( true !== $zip_opened ) {
    10511051            $this->delete_atachment( $file_id, true );
    10521052            wp_send_json_error( __( 'Could not open ZIP file.', 'modula-best-grid-gallery' ) );
    10531053        }
    10541054
    1055         // Get allowed mime types
    10561055        $allowed_mime_types = $this->define_allowed_mime_types();
    10571056
    1058         $has_valid_files = false;
    1059         $invalid_files   = array();
    1060         for ( $i = 0; $i < $zip->numFiles; $i++ ) {
    1061             $stat      = $zip->statIndex( $i );
    1062             $full_path = $stat['name'];
    1063             if ( substr( $full_path, -1 ) === '/' ) {
    1064                 continue;
    1065             }
    1066 
    1067             $file_name = basename( $full_path );
    1068             if ( empty( $file_name ) ) {
    1069                 continue;
    1070             }
    1071 
    1072             if ( substr( $file_name, 0, 2 ) === '._' ) {
    1073                 $invalid_files[] = $full_path;
    1074                 continue;
    1075             }
    1076 
    1077             $file_type = wp_check_filetype( $file_name, $allowed_mime_types );
    1078             if ( empty( $file_type['type'] ) ) {
    1079                 $invalid_files[] = $full_path;
    1080             } else {
    1081                 $has_valid_files = true;
    1082             }
    1083         }
    1084 
    1085         $zip->close();
    1086 
    1087         // If no valid image files found, reject the ZIP
    1088         if ( ! $has_valid_files ) {
    1089             $this->delete_atachment( $file_id, true );
    1090             wp_send_json_error( __( 'ZIP file does not contain any valid image files. Only image files are permitted.', 'modula-best-grid-gallery' ) );
    1091         }
    1092 
    1093         // Get the base path.
    10941057        $base       = pathinfo( $file, PATHINFO_DIRNAME );
    10951058        $file_name  = pathinfo( $file, PATHINFO_FILENAME );
     
    10971060        $unzip_path = $base . '/' . $file_name . $timestamp;
    10981061
    1099         // Set the WP_Filesystem.
    11001062        require_once ABSPATH . '/wp-admin/includes/file.php';
    11011063        WP_Filesystem();
    1102 
    1103         // Unzip the file.
    1104         $response = unzip_file( $file, $unzip_path );
    1105         if ( is_wp_error( $response ) ) {
     1064        global $wp_filesystem;
     1065
     1066        if ( ! $wp_filesystem->mkdir( $unzip_path, FS_CHMOD_DIR ) ) {
     1067            $zip->close();
    11061068            $this->delete_atachment( $file_id, true );
    1107             wp_send_json_error( $response->get_error_message() );
    1108         }
    1109 
    1110         // Delete the original zip file
     1069            wp_send_json_error( __( 'Could not create extraction directory.', 'modula-best-grid-gallery' ) );
     1070        }
     1071
     1072        $unzip_path = realpath( $unzip_path );
     1073        if ( false === $unzip_path ) {
     1074            $zip->close();
     1075            $this->delete_atachment( $file_id, true );
     1076            wp_send_json_error( __( 'Invalid extraction path.', 'modula-best-grid-gallery' ) );
     1077        }
     1078
     1079        $has_valid_files = false;
     1080        $valid_files     = array();
     1081
     1082        for ( $i = 0; $i < $zip->numFiles; $i++ ) {
     1083            $stat      = $zip->statIndex( $i );
     1084            $full_path = $stat['name'];
     1085
     1086            if ( substr( $full_path, -1 ) === '/' ) {
     1087                continue;
     1088            }
     1089
     1090            $file_name = basename( $full_path );
     1091            if ( empty( $file_name ) ) {
     1092                continue;
     1093            }
     1094
     1095            if ( substr( $file_name, 0, 2 ) === '._' ) {
     1096                continue;
     1097            }
     1098
     1099            if ( strpos( $full_path, '__MACOSX/' ) === 0 ) {
     1100                continue;
     1101            }
     1102
     1103            $file_type = wp_check_filetype( $file_name, $allowed_mime_types );
     1104            if ( empty( $file_type['type'] ) ) {
     1105                continue;
     1106            }
     1107
     1108            $sanitized_path = $this->sanitize_zip_path( $full_path, $unzip_path );
     1109            if ( false === $sanitized_path ) {
     1110                continue;
     1111            }
     1112
     1113            $has_valid_files = true;
     1114            $valid_files[]   = array(
     1115                'index' => $i,
     1116                'path'  => $sanitized_path,
     1117            );
     1118        }
     1119
     1120        if ( ! $has_valid_files ) {
     1121            $zip->close();
     1122            $wp_filesystem->rmdir( $unzip_path, true );
     1123            $this->delete_atachment( $file_id, true );
     1124            wp_send_json_error( __( 'ZIP file does not contain any valid image files. Only image files are permitted.', 'modula-best-grid-gallery' ) );
     1125        }
     1126
     1127        foreach ( $valid_files as $file_data ) {
     1128            $content = $zip->getFromIndex( $file_data['index'] );
     1129            if ( false === $content ) {
     1130                continue;
     1131            }
     1132
     1133            $target_file = $file_data['path'];
     1134            $target_dir  = dirname( $target_file );
     1135
     1136            if ( ! $wp_filesystem->is_dir( $target_dir ) ) {
     1137                if ( ! $wp_filesystem->mkdir( $target_dir, FS_CHMOD_DIR, true ) ) {
     1138                    continue;
     1139                }
     1140            }
     1141
     1142            if ( ! $wp_filesystem->put_contents( $target_file, $content, FS_CHMOD_FILE ) ) {
     1143                continue;
     1144            }
     1145        }
     1146
     1147        $zip->close();
     1148
    11111149        $this->delete_atachment( $file_id, true );
    1112 
    1113         // Delete invalid files after extraction
    1114         global $wp_filesystem;
    1115         if ( ! empty( $invalid_files ) && isset( $wp_filesystem ) ) {
    1116             foreach ( $invalid_files as $invalid_path ) {
    1117                 $file_to_delete = $unzip_path . '/' . $invalid_path;
    1118                 if ( $wp_filesystem->exists( $file_to_delete ) ) {
    1119                     $wp_filesystem->delete( $file_to_delete, false, 'f' );
    1120                 }
    1121             }
    1122 
    1123             $macosx_root = $unzip_path . '/__MACOSX';
    1124             if ( $wp_filesystem->exists( $macosx_root ) && $wp_filesystem->is_dir( $macosx_root ) ) {
    1125                 $wp_filesystem->rmdir( $macosx_root, true );
    1126             }
    1127 
    1128             $this->remove_empty_folders( $unzip_path, $unzip_path );
    1129         }
    1130 
    1131         $folders = array( $unzip_path );
    1132         // Check if the folder has subfolders.
     1150        $this->remove_empty_folders( $unzip_path, $unzip_path );
     1151
     1152        $folders    = array( $unzip_path );
    11331153        $subfolders = $this->list_folders( $unzip_path, true );
    11341154        if ( ! empty( $subfolders ) ) {
    11351155            foreach ( $subfolders as $subfolder ) {
    1136                 // Add the subfolder path to the folders array.
    11371156                $folders[] = $subfolder['path'];
    11381157            }
     
    11411160        // Send the unzip path.
    11421161        wp_send_json_success( $folders );
     1162    }
     1163
     1164    /**
     1165     * Sanitize ZIP file path to prevent path traversal attacks
     1166     *
     1167     * @param string $zip_path The path from the ZIP archive
     1168     * @param string $base_path The base extraction directory
     1169     * @return string|false The sanitized absolute path, or false if path traversal detected
     1170     *
     1171     * @since 2.11.0
     1172     */
     1173    private function sanitize_zip_path( $zip_path, $base_path ) {
     1174        $zip_path = str_replace( "\0", '', $zip_path );
     1175        $zip_path = str_replace( '\\', '/', $zip_path );
     1176        $zip_path = ltrim( $zip_path, './' );
     1177        $parts    = explode( '/', $zip_path );
     1178
     1179        $sanitized_parts = array();
     1180        foreach ( $parts as $part ) {
     1181            if ( '' === $part ) {
     1182                continue;
     1183            }
     1184
     1185            if ( '.' === $part ) {
     1186                continue;
     1187            }
     1188
     1189            if ( '..' === $part ) {
     1190                return false;
     1191            }
     1192
     1193            if ( preg_match( '/^[a-zA-Z]:$/', $part ) ) {
     1194                return false;
     1195            }
     1196
     1197            $sanitized_parts[] = $part;
     1198        }
     1199
     1200        $sanitized_relative = implode( '/', $sanitized_parts );
     1201        $full_path          = $base_path . '/' . $sanitized_relative;
     1202
     1203        $normalized = realpath( dirname( $full_path ) );
     1204        if ( false === $normalized ) {
     1205            return false;
     1206        }
     1207
     1208        $final_path = $normalized . '/' . basename( $full_path );
     1209        $base_real  = realpath( $base_path );
     1210        if ( false === $base_real ) {
     1211            return false;
     1212        }
     1213
     1214        if ( 0 !== strpos( $final_path, $base_real . '/' ) && $final_path !== $base_real ) {
     1215            return false;
     1216        }
     1217
     1218        return $final_path;
    11431219    }
    11441220
  • modula-best-grid-gallery/tags/2.13.3/readme.txt

    r3407694 r3407949  
    22Contributors: wpchill, silkalns 
    33Tags: gallery plugin, image gallery, video gallery, responsive gallery, WordPress gallery plugin
    4 Requires at least: 5.3 
     4Requires at least: 5.3
    55Tested up to: 6.9
    6 Requires PHP: 5.6 
    7 Stable tag: 2.13.2
     6Requires PHP: 5.6
     7Stable tag: 2.13.3
    88
    99License: GNU General Public License v3.0 or later 
     
    323323
    324324== Changelog ==
     325= 2.13.3 - 02.12.2025 =
     326Fixed: Vulnerability in zip import.
     327
    325328= 2.13.2 - 19.11.2025 =
    326329Updated: Performance improvements.
  • modula-best-grid-gallery/trunk/Modula.php

    r3398654 r3407949  
    55* Description:              Modula is the most powerful, user-friendly WordPress gallery plugin. Add galleries, masonry grids and more in a few clicks.
    66* Author:                   WPChill
    7 * Version:                  2.13.2
     7* Version:                  2.13.3
    88* Author URI:               https://www.wpchill.com/
    99* License:                  GPLv3 or later
     
    4848 */
    4949
    50 define( 'MODULA_LITE_VERSION', '2.13.0' );
     50define( 'MODULA_LITE_VERSION', '2.13.3' );
    5151define( 'MODULA_PATH', plugin_dir_path( __FILE__ ) );
    5252define( 'MODULA_URL', plugin_dir_url( __FILE__ ) );
  • modula-best-grid-gallery/trunk/changelog.txt

    r3398654 r3407949  
     1= 2.13.3 - 02.12.2025 =
     2Fixed: Vulnerability in zip import.
     3
    14= 2.13.2 - 19.11.2025 =
    25Updated: Performance improvements.
  • modula-best-grid-gallery/trunk/includes/admin/class-modula-gallery-upload.php

    r3395701 r3407949  
    10481048        $zip        = new ZipArchive();
    10491049        $zip_opened = $zip->open( $file );
    1050         if ( $zip_opened !== true ) {
     1050        if ( true !== $zip_opened ) {
    10511051            $this->delete_atachment( $file_id, true );
    10521052            wp_send_json_error( __( 'Could not open ZIP file.', 'modula-best-grid-gallery' ) );
    10531053        }
    10541054
    1055         // Get allowed mime types
    10561055        $allowed_mime_types = $this->define_allowed_mime_types();
    10571056
    1058         $has_valid_files = false;
    1059         $invalid_files   = array();
    1060         for ( $i = 0; $i < $zip->numFiles; $i++ ) {
    1061             $stat      = $zip->statIndex( $i );
    1062             $full_path = $stat['name'];
    1063             if ( substr( $full_path, -1 ) === '/' ) {
    1064                 continue;
    1065             }
    1066 
    1067             $file_name = basename( $full_path );
    1068             if ( empty( $file_name ) ) {
    1069                 continue;
    1070             }
    1071 
    1072             if ( substr( $file_name, 0, 2 ) === '._' ) {
    1073                 $invalid_files[] = $full_path;
    1074                 continue;
    1075             }
    1076 
    1077             $file_type = wp_check_filetype( $file_name, $allowed_mime_types );
    1078             if ( empty( $file_type['type'] ) ) {
    1079                 $invalid_files[] = $full_path;
    1080             } else {
    1081                 $has_valid_files = true;
    1082             }
    1083         }
    1084 
    1085         $zip->close();
    1086 
    1087         // If no valid image files found, reject the ZIP
    1088         if ( ! $has_valid_files ) {
    1089             $this->delete_atachment( $file_id, true );
    1090             wp_send_json_error( __( 'ZIP file does not contain any valid image files. Only image files are permitted.', 'modula-best-grid-gallery' ) );
    1091         }
    1092 
    1093         // Get the base path.
    10941057        $base       = pathinfo( $file, PATHINFO_DIRNAME );
    10951058        $file_name  = pathinfo( $file, PATHINFO_FILENAME );
     
    10971060        $unzip_path = $base . '/' . $file_name . $timestamp;
    10981061
    1099         // Set the WP_Filesystem.
    11001062        require_once ABSPATH . '/wp-admin/includes/file.php';
    11011063        WP_Filesystem();
    1102 
    1103         // Unzip the file.
    1104         $response = unzip_file( $file, $unzip_path );
    1105         if ( is_wp_error( $response ) ) {
     1064        global $wp_filesystem;
     1065
     1066        if ( ! $wp_filesystem->mkdir( $unzip_path, FS_CHMOD_DIR ) ) {
     1067            $zip->close();
    11061068            $this->delete_atachment( $file_id, true );
    1107             wp_send_json_error( $response->get_error_message() );
    1108         }
    1109 
    1110         // Delete the original zip file
     1069            wp_send_json_error( __( 'Could not create extraction directory.', 'modula-best-grid-gallery' ) );
     1070        }
     1071
     1072        $unzip_path = realpath( $unzip_path );
     1073        if ( false === $unzip_path ) {
     1074            $zip->close();
     1075            $this->delete_atachment( $file_id, true );
     1076            wp_send_json_error( __( 'Invalid extraction path.', 'modula-best-grid-gallery' ) );
     1077        }
     1078
     1079        $has_valid_files = false;
     1080        $valid_files     = array();
     1081
     1082        for ( $i = 0; $i < $zip->numFiles; $i++ ) {
     1083            $stat      = $zip->statIndex( $i );
     1084            $full_path = $stat['name'];
     1085
     1086            if ( substr( $full_path, -1 ) === '/' ) {
     1087                continue;
     1088            }
     1089
     1090            $file_name = basename( $full_path );
     1091            if ( empty( $file_name ) ) {
     1092                continue;
     1093            }
     1094
     1095            if ( substr( $file_name, 0, 2 ) === '._' ) {
     1096                continue;
     1097            }
     1098
     1099            if ( strpos( $full_path, '__MACOSX/' ) === 0 ) {
     1100                continue;
     1101            }
     1102
     1103            $file_type = wp_check_filetype( $file_name, $allowed_mime_types );
     1104            if ( empty( $file_type['type'] ) ) {
     1105                continue;
     1106            }
     1107
     1108            $sanitized_path = $this->sanitize_zip_path( $full_path, $unzip_path );
     1109            if ( false === $sanitized_path ) {
     1110                continue;
     1111            }
     1112
     1113            $has_valid_files = true;
     1114            $valid_files[]   = array(
     1115                'index' => $i,
     1116                'path'  => $sanitized_path,
     1117            );
     1118        }
     1119
     1120        if ( ! $has_valid_files ) {
     1121            $zip->close();
     1122            $wp_filesystem->rmdir( $unzip_path, true );
     1123            $this->delete_atachment( $file_id, true );
     1124            wp_send_json_error( __( 'ZIP file does not contain any valid image files. Only image files are permitted.', 'modula-best-grid-gallery' ) );
     1125        }
     1126
     1127        foreach ( $valid_files as $file_data ) {
     1128            $content = $zip->getFromIndex( $file_data['index'] );
     1129            if ( false === $content ) {
     1130                continue;
     1131            }
     1132
     1133            $target_file = $file_data['path'];
     1134            $target_dir  = dirname( $target_file );
     1135
     1136            if ( ! $wp_filesystem->is_dir( $target_dir ) ) {
     1137                if ( ! $wp_filesystem->mkdir( $target_dir, FS_CHMOD_DIR, true ) ) {
     1138                    continue;
     1139                }
     1140            }
     1141
     1142            if ( ! $wp_filesystem->put_contents( $target_file, $content, FS_CHMOD_FILE ) ) {
     1143                continue;
     1144            }
     1145        }
     1146
     1147        $zip->close();
     1148
    11111149        $this->delete_atachment( $file_id, true );
    1112 
    1113         // Delete invalid files after extraction
    1114         global $wp_filesystem;
    1115         if ( ! empty( $invalid_files ) && isset( $wp_filesystem ) ) {
    1116             foreach ( $invalid_files as $invalid_path ) {
    1117                 $file_to_delete = $unzip_path . '/' . $invalid_path;
    1118                 if ( $wp_filesystem->exists( $file_to_delete ) ) {
    1119                     $wp_filesystem->delete( $file_to_delete, false, 'f' );
    1120                 }
    1121             }
    1122 
    1123             $macosx_root = $unzip_path . '/__MACOSX';
    1124             if ( $wp_filesystem->exists( $macosx_root ) && $wp_filesystem->is_dir( $macosx_root ) ) {
    1125                 $wp_filesystem->rmdir( $macosx_root, true );
    1126             }
    1127 
    1128             $this->remove_empty_folders( $unzip_path, $unzip_path );
    1129         }
    1130 
    1131         $folders = array( $unzip_path );
    1132         // Check if the folder has subfolders.
     1150        $this->remove_empty_folders( $unzip_path, $unzip_path );
     1151
     1152        $folders    = array( $unzip_path );
    11331153        $subfolders = $this->list_folders( $unzip_path, true );
    11341154        if ( ! empty( $subfolders ) ) {
    11351155            foreach ( $subfolders as $subfolder ) {
    1136                 // Add the subfolder path to the folders array.
    11371156                $folders[] = $subfolder['path'];
    11381157            }
     
    11411160        // Send the unzip path.
    11421161        wp_send_json_success( $folders );
     1162    }
     1163
     1164    /**
     1165     * Sanitize ZIP file path to prevent path traversal attacks
     1166     *
     1167     * @param string $zip_path The path from the ZIP archive
     1168     * @param string $base_path The base extraction directory
     1169     * @return string|false The sanitized absolute path, or false if path traversal detected
     1170     *
     1171     * @since 2.11.0
     1172     */
     1173    private function sanitize_zip_path( $zip_path, $base_path ) {
     1174        $zip_path = str_replace( "\0", '', $zip_path );
     1175        $zip_path = str_replace( '\\', '/', $zip_path );
     1176        $zip_path = ltrim( $zip_path, './' );
     1177        $parts    = explode( '/', $zip_path );
     1178
     1179        $sanitized_parts = array();
     1180        foreach ( $parts as $part ) {
     1181            if ( '' === $part ) {
     1182                continue;
     1183            }
     1184
     1185            if ( '.' === $part ) {
     1186                continue;
     1187            }
     1188
     1189            if ( '..' === $part ) {
     1190                return false;
     1191            }
     1192
     1193            if ( preg_match( '/^[a-zA-Z]:$/', $part ) ) {
     1194                return false;
     1195            }
     1196
     1197            $sanitized_parts[] = $part;
     1198        }
     1199
     1200        $sanitized_relative = implode( '/', $sanitized_parts );
     1201        $full_path          = $base_path . '/' . $sanitized_relative;
     1202
     1203        $normalized = realpath( dirname( $full_path ) );
     1204        if ( false === $normalized ) {
     1205            return false;
     1206        }
     1207
     1208        $final_path = $normalized . '/' . basename( $full_path );
     1209        $base_real  = realpath( $base_path );
     1210        if ( false === $base_real ) {
     1211            return false;
     1212        }
     1213
     1214        if ( 0 !== strpos( $final_path, $base_real . '/' ) && $final_path !== $base_real ) {
     1215            return false;
     1216        }
     1217
     1218        return $final_path;
    11431219    }
    11441220
  • modula-best-grid-gallery/trunk/readme.txt

    r3407694 r3407949  
    22Contributors: wpchill, silkalns 
    33Tags: gallery plugin, image gallery, video gallery, responsive gallery, WordPress gallery plugin
    4 Requires at least: 5.3 
     4Requires at least: 5.3
    55Tested up to: 6.9
    6 Requires PHP: 5.6 
    7 Stable tag: 2.13.2
     6Requires PHP: 5.6
     7Stable tag: 2.13.3
    88
    99License: GNU General Public License v3.0 or later 
     
    323323
    324324== Changelog ==
     325= 2.13.3 - 02.12.2025 =
     326Fixed: Vulnerability in zip import.
     327
    325328= 2.13.2 - 19.11.2025 =
    326329Updated: Performance improvements.
Note: See TracChangeset for help on using the changeset viewer.