Changeset 3407949
- Timestamp:
- 12/02/2025 12:02:09 PM (4 months ago)
- Location:
- modula-best-grid-gallery
- Files:
-
- 8 edited
- 1 copied
-
tags/2.13.3 (copied) (copied from modula-best-grid-gallery/trunk)
-
tags/2.13.3/Modula.php (modified) (2 diffs)
-
tags/2.13.3/changelog.txt (modified) (1 diff)
-
tags/2.13.3/includes/admin/class-modula-gallery-upload.php (modified) (3 diffs)
-
tags/2.13.3/readme.txt (modified) (2 diffs)
-
trunk/Modula.php (modified) (2 diffs)
-
trunk/changelog.txt (modified) (1 diff)
-
trunk/includes/admin/class-modula-gallery-upload.php (modified) (3 diffs)
-
trunk/readme.txt (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
modula-best-grid-gallery/tags/2.13.3/Modula.php
r3398654 r3407949 5 5 * Description: Modula is the most powerful, user-friendly WordPress gallery plugin. Add galleries, masonry grids and more in a few clicks. 6 6 * Author: WPChill 7 * Version: 2.13. 27 * Version: 2.13.3 8 8 * Author URI: https://www.wpchill.com/ 9 9 * License: GPLv3 or later … … 48 48 */ 49 49 50 define( 'MODULA_LITE_VERSION', '2.13. 0' );50 define( 'MODULA_LITE_VERSION', '2.13.3' ); 51 51 define( 'MODULA_PATH', plugin_dir_path( __FILE__ ) ); 52 52 define( '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 = 2 Fixed: Vulnerability in zip import. 3 1 4 = 2.13.2 - 19.11.2025 = 2 5 Updated: Performance improvements. -
modula-best-grid-gallery/tags/2.13.3/includes/admin/class-modula-gallery-upload.php
r3395701 r3407949 1048 1048 $zip = new ZipArchive(); 1049 1049 $zip_opened = $zip->open( $file ); 1050 if ( $zip_opened !== true) {1050 if ( true !== $zip_opened ) { 1051 1051 $this->delete_atachment( $file_id, true ); 1052 1052 wp_send_json_error( __( 'Could not open ZIP file.', 'modula-best-grid-gallery' ) ); 1053 1053 } 1054 1054 1055 // Get allowed mime types1056 1055 $allowed_mime_types = $this->define_allowed_mime_types(); 1057 1056 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 ZIP1088 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.1094 1057 $base = pathinfo( $file, PATHINFO_DIRNAME ); 1095 1058 $file_name = pathinfo( $file, PATHINFO_FILENAME ); … … 1097 1060 $unzip_path = $base . '/' . $file_name . $timestamp; 1098 1061 1099 // Set the WP_Filesystem.1100 1062 require_once ABSPATH . '/wp-admin/includes/file.php'; 1101 1063 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(); 1106 1068 $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 1111 1149 $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 ); 1133 1153 $subfolders = $this->list_folders( $unzip_path, true ); 1134 1154 if ( ! empty( $subfolders ) ) { 1135 1155 foreach ( $subfolders as $subfolder ) { 1136 // Add the subfolder path to the folders array.1137 1156 $folders[] = $subfolder['path']; 1138 1157 } … … 1141 1160 // Send the unzip path. 1142 1161 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; 1143 1219 } 1144 1220 -
modula-best-grid-gallery/tags/2.13.3/readme.txt
r3407694 r3407949 2 2 Contributors: wpchill, silkalns 3 3 Tags: gallery plugin, image gallery, video gallery, responsive gallery, WordPress gallery plugin 4 Requires at least: 5.3 4 Requires at least: 5.3 5 5 Tested up to: 6.9 6 Requires PHP: 5.6 7 Stable tag: 2.13. 26 Requires PHP: 5.6 7 Stable tag: 2.13.3 8 8 9 9 License: GNU General Public License v3.0 or later … … 323 323 324 324 == Changelog == 325 = 2.13.3 - 02.12.2025 = 326 Fixed: Vulnerability in zip import. 327 325 328 = 2.13.2 - 19.11.2025 = 326 329 Updated: Performance improvements. -
modula-best-grid-gallery/trunk/Modula.php
r3398654 r3407949 5 5 * Description: Modula is the most powerful, user-friendly WordPress gallery plugin. Add galleries, masonry grids and more in a few clicks. 6 6 * Author: WPChill 7 * Version: 2.13. 27 * Version: 2.13.3 8 8 * Author URI: https://www.wpchill.com/ 9 9 * License: GPLv3 or later … … 48 48 */ 49 49 50 define( 'MODULA_LITE_VERSION', '2.13. 0' );50 define( 'MODULA_LITE_VERSION', '2.13.3' ); 51 51 define( 'MODULA_PATH', plugin_dir_path( __FILE__ ) ); 52 52 define( 'MODULA_URL', plugin_dir_url( __FILE__ ) ); -
modula-best-grid-gallery/trunk/changelog.txt
r3398654 r3407949 1 = 2.13.3 - 02.12.2025 = 2 Fixed: Vulnerability in zip import. 3 1 4 = 2.13.2 - 19.11.2025 = 2 5 Updated: Performance improvements. -
modula-best-grid-gallery/trunk/includes/admin/class-modula-gallery-upload.php
r3395701 r3407949 1048 1048 $zip = new ZipArchive(); 1049 1049 $zip_opened = $zip->open( $file ); 1050 if ( $zip_opened !== true) {1050 if ( true !== $zip_opened ) { 1051 1051 $this->delete_atachment( $file_id, true ); 1052 1052 wp_send_json_error( __( 'Could not open ZIP file.', 'modula-best-grid-gallery' ) ); 1053 1053 } 1054 1054 1055 // Get allowed mime types1056 1055 $allowed_mime_types = $this->define_allowed_mime_types(); 1057 1056 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 ZIP1088 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.1094 1057 $base = pathinfo( $file, PATHINFO_DIRNAME ); 1095 1058 $file_name = pathinfo( $file, PATHINFO_FILENAME ); … … 1097 1060 $unzip_path = $base . '/' . $file_name . $timestamp; 1098 1061 1099 // Set the WP_Filesystem.1100 1062 require_once ABSPATH . '/wp-admin/includes/file.php'; 1101 1063 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(); 1106 1068 $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 1111 1149 $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 ); 1133 1153 $subfolders = $this->list_folders( $unzip_path, true ); 1134 1154 if ( ! empty( $subfolders ) ) { 1135 1155 foreach ( $subfolders as $subfolder ) { 1136 // Add the subfolder path to the folders array.1137 1156 $folders[] = $subfolder['path']; 1138 1157 } … … 1141 1160 // Send the unzip path. 1142 1161 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; 1143 1219 } 1144 1220 -
modula-best-grid-gallery/trunk/readme.txt
r3407694 r3407949 2 2 Contributors: wpchill, silkalns 3 3 Tags: gallery plugin, image gallery, video gallery, responsive gallery, WordPress gallery plugin 4 Requires at least: 5.3 4 Requires at least: 5.3 5 5 Tested up to: 6.9 6 Requires PHP: 5.6 7 Stable tag: 2.13. 26 Requires PHP: 5.6 7 Stable tag: 2.13.3 8 8 9 9 License: GNU General Public License v3.0 or later … … 323 323 324 324 == Changelog == 325 = 2.13.3 - 02.12.2025 = 326 Fixed: Vulnerability in zip import. 327 325 328 = 2.13.2 - 19.11.2025 = 326 329 Updated: Performance improvements.
Note: See TracChangeset
for help on using the changeset viewer.