diff --git src/wp-admin/includes/file.php src/wp-admin/includes/file.php
index 858f828185..a834e5e161 100644
|
|
function wp_tempnam( $filename = '', $dir = '' ) { |
663 | 663 | * @param array $allowed_files Optional. Array of allowed files to edit, $file must match an entry exactly. |
664 | 664 | * @return string|null |
665 | 665 | */ |
666 | | function validate_file_to_edit( $file, $allowed_files = '' ) { |
| 666 | function validate_file_to_edit( $file, $allowed_files = array() ) { |
667 | 667 | $code = validate_file( $file, $allowed_files ); |
668 | 668 | |
669 | 669 | if (!$code ) |
… |
… |
function _unzip_file_ziparchive($file, $to, $needed_dirs = array() ) { |
1119 | 1119 | if ( '__MACOSX/' === substr($info['name'], 0, 9) ) // Skip the OS X-created __MACOSX directory |
1120 | 1120 | continue; |
1121 | 1121 | |
| 1122 | // Don't extract invalid files: |
1122 | 1123 | if ( 0 !== validate_file( $info['name'] ) ) { |
1123 | | return new WP_Error( 'invalid_file_ziparchive', __( 'Could not extract file from archive.' ), $info['name'] ); |
| 1124 | continue; |
1124 | 1125 | } |
1125 | 1126 | |
1126 | 1127 | $uncompressed_size += $info['size']; |
… |
… |
function _unzip_file_ziparchive($file, $to, $needed_dirs = array() ) { |
1180 | 1181 | if ( '__MACOSX/' === substr($info['name'], 0, 9) ) // Don't extract the OS X-created __MACOSX directory files |
1181 | 1182 | continue; |
1182 | 1183 | |
| 1184 | // Don't extract invalid files: |
| 1185 | if ( 0 !== validate_file( $info['name'] ) ) { |
| 1186 | continue; |
| 1187 | } |
| 1188 | |
1183 | 1189 | $contents = $z->getFromIndex($i); |
1184 | 1190 | if ( false === $contents ) |
1185 | 1191 | return new WP_Error( 'extract_failed_ziparchive', __( 'Could not extract file from archive.' ), $info['name'] ); |
… |
… |
function _unzip_file_pclzip($file, $to, $needed_dirs = array()) { |
1283 | 1289 | if ( '__MACOSX/' === substr($file['filename'], 0, 9) ) // Don't extract the OS X-created __MACOSX directory files |
1284 | 1290 | continue; |
1285 | 1291 | |
| 1292 | // Don't extract invalid files: |
1286 | 1293 | if ( 0 !== validate_file( $file['filename'] ) ) { |
1287 | | return new WP_Error( 'invalid_file_pclzip', __( 'Could not extract file from archive.' ), $file['filename'] ); |
| 1294 | continue; |
1288 | 1295 | } |
1289 | 1296 | |
1290 | 1297 | if ( ! $wp_filesystem->put_contents( $to . $file['filename'], $file['content'], FS_CHMOD_FILE) ) |
diff --git src/wp-includes/functions.php src/wp-includes/functions.php
index a9c53d31e4..b5d21285b6 100644
|
|
function iis7_supports_permalinks() { |
4252 | 4252 | * @param array $allowed_files Optional. List of allowed files. |
4253 | 4253 | * @return int 0 means nothing is wrong, greater than 0 means something was wrong. |
4254 | 4254 | */ |
4255 | | function validate_file( $file, $allowed_files = '' ) { |
4256 | | if ( false !== strpos( $file, '..' ) ) |
| 4255 | function validate_file( $file, $allowed_files = array() ) { |
| 4256 | // `../` on its own is not allowed: |
| 4257 | if ( '../' === $file ) { |
4257 | 4258 | return 1; |
| 4259 | } |
4258 | 4260 | |
4259 | | if ( false !== strpos( $file, './' ) ) |
| 4261 | // More than one occurence of `../` is not allowed: |
| 4262 | if ( preg_match_all( '#\.\./#', $file, $matches, PREG_SET_ORDER ) && ( count( $matches ) > 1 ) ) { |
4260 | 4263 | return 1; |
| 4264 | } |
| 4265 | |
| 4266 | // `../` which does not occur at the end of the path is not allowed: |
| 4267 | if ( false !== strpos( $file, '../' ) && '../' !== mb_substr( $file, -3, 3 ) ) { |
| 4268 | return 1; |
| 4269 | } |
4261 | 4270 | |
| 4271 | // Files not in the allowed file list are not allowed: |
4262 | 4272 | if ( ! empty( $allowed_files ) && ! in_array( $file, $allowed_files ) ) |
4263 | 4273 | return 3; |
4264 | 4274 | |
| 4275 | // Absolute Windows drive paths are not allowed: |
4265 | 4276 | if (':' == substr( $file, 1, 1 ) ) |
4266 | 4277 | return 2; |
4267 | 4278 | |
diff --git tests/phpunit/tests/functions.php tests/phpunit/tests/functions.php
index 7c8736ab67..6a2477ef0b 100644
|
|
class Tests_Functions extends WP_UnitTestCase { |
1161 | 1161 | return $data; |
1162 | 1162 | } |
1163 | 1163 | |
| 1164 | /** |
| 1165 | * Test file path validation |
| 1166 | * |
| 1167 | * @ticket 42016 |
| 1168 | * @dataProvider data_test_validate_file() |
| 1169 | * |
| 1170 | * @param string $file File path. |
| 1171 | * @param array $allowed_files List of allowed files. |
| 1172 | * @param int $expected Expected result. |
| 1173 | */ |
| 1174 | public function test_validate_file( $file, $allowed_files, $expected ) { |
| 1175 | $this->assertSame( $expected, validate_file( $file, $allowed_files ) ); |
| 1176 | } |
| 1177 | |
| 1178 | /** |
| 1179 | * Data provider for file validation. |
| 1180 | * |
| 1181 | * @return array { |
| 1182 | * @type array $0... { |
| 1183 | * @type string $0 File path. |
| 1184 | * @type array $1 List of allowed files. |
| 1185 | * @type int $2 Expected result. |
| 1186 | * } |
| 1187 | * } |
| 1188 | */ |
| 1189 | public function data_test_validate_file() { |
| 1190 | return array( |
| 1191 | |
| 1192 | // Allowed files: |
| 1193 | array( |
| 1194 | null, |
| 1195 | array(), |
| 1196 | 0, |
| 1197 | ), |
| 1198 | array( |
| 1199 | '', |
| 1200 | array(), |
| 1201 | 0, |
| 1202 | ), |
| 1203 | array( |
| 1204 | ' ', |
| 1205 | array(), |
| 1206 | 0, |
| 1207 | ), |
| 1208 | array( |
| 1209 | '.', |
| 1210 | array(), |
| 1211 | 0, |
| 1212 | ), |
| 1213 | array( |
| 1214 | '..', |
| 1215 | array(), |
| 1216 | 0, |
| 1217 | ), |
| 1218 | array( |
| 1219 | './', |
| 1220 | array(), |
| 1221 | 0, |
| 1222 | ), |
| 1223 | array( |
| 1224 | 'foo.ext', |
| 1225 | array( 'foo.ext' ), |
| 1226 | 0, |
| 1227 | ), |
| 1228 | array( |
| 1229 | 'dir/foo.ext', |
| 1230 | array(), |
| 1231 | 0, |
| 1232 | ), |
| 1233 | array( |
| 1234 | 'foo..ext', |
| 1235 | array(), |
| 1236 | 0, |
| 1237 | ), |
| 1238 | array( |
| 1239 | 'dir/dir/../', |
| 1240 | array(), |
| 1241 | 0, |
| 1242 | ), |
| 1243 | |
| 1244 | // Directory traversal: |
| 1245 | array( |
| 1246 | '../', |
| 1247 | array(), |
| 1248 | 1, |
| 1249 | ), |
| 1250 | array( |
| 1251 | '../../', |
| 1252 | array(), |
| 1253 | 1, |
| 1254 | ), |
| 1255 | array( |
| 1256 | '../file.ext', |
| 1257 | array(), |
| 1258 | 1, |
| 1259 | ), |
| 1260 | array( |
| 1261 | '../dir/../', |
| 1262 | array(), |
| 1263 | 1, |
| 1264 | ), |
| 1265 | array( |
| 1266 | '/dir/dir/../../', |
| 1267 | array(), |
| 1268 | 1, |
| 1269 | ), |
| 1270 | array( |
| 1271 | '/dir/dir/../../', |
| 1272 | array( '/dir/dir/../../' ), |
| 1273 | 1, |
| 1274 | ), |
| 1275 | |
| 1276 | // Windows drives: |
| 1277 | array( |
| 1278 | 'c:', |
| 1279 | array(), |
| 1280 | 2, |
| 1281 | ), |
| 1282 | array( |
| 1283 | 'C:/WINDOWS/system32', |
| 1284 | array( 'C:/WINDOWS/system32' ), |
| 1285 | 2, |
| 1286 | ), |
| 1287 | |
| 1288 | // Disallowed files: |
| 1289 | array( |
| 1290 | 'foo.ext', |
| 1291 | array( 'bar.ext' ), |
| 1292 | 3, |
| 1293 | ), |
| 1294 | array( |
| 1295 | 'foo.ext', |
| 1296 | array( '.ext' ), |
| 1297 | 3, |
| 1298 | ), |
| 1299 | array( |
| 1300 | 'path/foo.ext', |
| 1301 | array( 'foo.ext' ), |
| 1302 | 3, |
| 1303 | ), |
| 1304 | |
| 1305 | ); |
| 1306 | } |
1164 | 1307 | } |