@@ -2324,14 +2324,59 @@ function wp_check_filetype_and_ext( $file, $filename, $mimes = null ) {
23242324 $ type = $ ext = false ;
23252325 }
23262326 }
2327- } elseif ( function_exists ( 'finfo_file ' ) ) {
2328- // Use finfo_file if available to validate non-image files.
2327+ }
2328+
2329+ // Validate files that didn't get validated during previous checks.
2330+ if ( $ type && ! $ real_mime && extension_loaded ( 'fileinfo ' ) ) {
23292331 $ finfo = finfo_open ( FILEINFO_MIME_TYPE );
23302332 $ real_mime = finfo_file ( $ finfo , $ file );
23312333 finfo_close ( $ finfo );
23322334
2333- // If the extension does not match the file's real type, return false.
2334- if ( $ real_mime !== $ type ) {
2335+ // fileinfo often misidentifies obscure files as one of these types
2336+ $ nonspecific_types = array (
2337+ 'application/octet-stream ' ,
2338+ 'application/encrypted ' ,
2339+ 'application/CDFV2-encrypted ' ,
2340+ 'application/zip ' ,
2341+ );
2342+
2343+ /*
2344+ * If $real_mime doesn't match the content type we're expecting from the file's extension,
2345+ * we need to do some additional vetting. Media types and those listed in $nonspecific_types are
2346+ * allowed some leeway, but anything else must exactly match the real content type.
2347+ */
2348+ if ( in_array ( $ real_mime , $ nonspecific_types , true ) ) {
2349+ // File is a non-specific binary type. That's ok if it's a type that generally tends to be binary.
2350+ if ( !in_array ( substr ( $ type , 0 , strcspn ( $ type , '/ ' ) ), array ( 'application ' , 'video ' , 'audio ' ) ) ) {
2351+ $ type = $ ext = false ;
2352+ }
2353+ } elseif ( 0 === strpos ( $ real_mime , 'video/ ' ) || 0 === strpos ( $ real_mime , 'audio/ ' ) ) {
2354+ /*
2355+ * For these types, only the major type must match the real value.
2356+ * This means that common mismatches are forgiven: application/vnd.apple.numbers is often misidentified as application/zip,
2357+ * and some media files are commonly named with the wrong extension (.mov instead of .mp4)
2358+ */
2359+
2360+ if ( substr ( $ real_mime , 0 , strcspn ( $ real_mime , '/ ' ) ) !== substr ( $ type , 0 , strcspn ( $ type , '/ ' ) ) ) {
2361+ $ type = $ ext = false ;
2362+ }
2363+ } else {
2364+ if ( $ type !== $ real_mime ) {
2365+ /*
2366+ * Everything else including image/* and application/*:
2367+ * If the real content type doesn't match the file extension, assume it's dangerous.
2368+ */
2369+ $ type = $ ext = false ;
2370+ }
2371+
2372+ }
2373+ }
2374+
2375+ // The mime type must be allowed
2376+ if ( $ type ) {
2377+ $ allowed = get_allowed_mime_types ();
2378+
2379+ if ( ! in_array ( $ type , $ allowed ) ) {
23352380 $ type = $ ext = false ;
23362381 }
23372382 }
0 commit comments