Make WordPress Core

Opened 8 years ago

Last modified 4 weeks ago

#46544 accepted defect (bug)

mp3 file with different file mime type and content type when uploading should not be rejected

Reported by: soleo Owned by: joedolson
Priority: normal Milestone: 7.2
Component: Media Version: 5.1
Severity: major Keywords: has-screenshots has-test-info has-patch has-unit-tests
Cc: Focuses:

Description

<?php
$files = [
    'recording6021333337014136895.mp3',
    'SampleAudio_0.7mb.mp3',
    'to_test_on_timby_voice_note.m4a'
];
if (  extension_loaded( 'fileinfo' ) ) {
    foreach ($files as $file ) {
        $finfo     = finfo_open( FILEINFO_MIME_TYPE );
        $real_mime = finfo_file( $finfo, $file );
        finfo_close( $finfo );
        echo "$file $real_mime\r\n";
    }
}

Results:

recording6021333337014136895.mp3 video/mp4
SampleAudio_0.7mb.mp3 audio/mpeg
to_test_on_timby_voice_note.m4a application/octet-stream

All the files are valid, but recording6021333337014136895.mp3 and to_test_on_timby_voice_note.m4a because of the mime type.

https://github.com/WordPress/WordPress/commit/3af00578e46efa3b59fb8ed9f4177d632a123a2e is the commit which introduces this issue

Attachments (5)

recording6021333337014136895.mp3 (37.0 KB ) - added by soleo 8 years ago.
Recording from phone
to_test_on_timby_voice_note.m4a (130.1 KB ) - added by soleo 8 years ago.
Another recording
SampleAudio_0.7mb.mp3 (708.2 KB ) - added by soleo 8 years ago.
Regular test sample audio
xj.m4a (295.9 KB ) - added by soleo 7 years ago.
Another example for m4a audio file
trac-46544-pr-13156-playground.png (127.0 KB ) - added by arkaprabhachowdhury 4 weeks ago.
PR #13156 Playground result: the MP4 container renamed to .m4a uploads successfully and appears as an audio attachment.

Download all attachments as: .zip

Change History (15)

@soleo
8 years ago

Recording from phone

@soleo
8 years ago

Another recording

@soleo
8 years ago

Regular test sample audio

#1 follow-up: @subrataemfluence
7 years ago

Welcome to trac and thanks for the report!

recording6021333337014136895.mp3 video/mp4
to_test_on_timby_voice_note.m4a application/octet-stream

The issue with the above files should ideally be fixed by adding right associations in wp_get_mime_types function inside wp_includes/functions.php file.

'mp3'   => 'video/mp4',
'm4a'   => 'application/octet-stream',

The issue with recording6021333337014136895.mp3 video/mp4 has actually resolved by the above approach but the m4a one is not responding to it!

I downloaded few sample m4a sample files and they uploaded correctly since the mime-types of those are all audio/x-m4a, and this is the value returned by get_real_type function.

Although adding a correct association should work ('m4a' => 'application/octet-stream') and the mime-type of your file is indeed application/octet-stream I am not sure why it is being rejected! However, none of the m4a files I downloaded is of application/octet-stream.

Can you send another m4a of application/octet-stream type file for further testing please?

@soleo
7 years ago

Another example for m4a audio file

#2 in reply to: ↑ 1 @soleo
7 years ago

Thanks for taking care of it.

I just attached another m4a file (xj.m4a). It was recorded using Vocienote app built in SAMSUNG S8.

Could you explain how you are going to fix it? The fix should work for the following types

  • an mp3 file could have a set of mime types such as audio/mpeg, video/mp4
  • similarly, an m4a file could have mime type audio/mpeg, audio/x-m4a or application/octet-stream

This is not a complete list of all variations of possible mime types, because every recording app handles it differently. Adding another set of mime type for mp3,m4a might solve this current bug, but it is very likely that there are other cases we may miss.

Is there a reason that we have to do mime type checking that strict?

Replying to subrataemfluence:

Welcome to trac and thanks for the report!

recording6021333337014136895.mp3 video/mp4
to_test_on_timby_voice_note.m4a application/octet-stream

The issue with the above files should ideally be fixed by adding right associations in wp_get_mime_types function inside wp_includes/functions.php file.

'mp3'   => 'video/mp4',
'm4a'   => 'application/octet-stream',

The issue with recording6021333337014136895.mp3 video/mp4 has actually resolved by the above approach but the m4a one is not responding to it!

I downloaded few sample m4a sample files and they uploaded correctly since the mime-types of those are all audio/x-m4a, and this is the value returned by get_real_type function.

Although adding a correct association should work ('m4a' => 'application/octet-stream') and the mime-type of your file is indeed application/octet-stream I am not sure why it is being rejected! However, none of the m4a files I downloaded is of application/octet-stream.

Can you send another m4a of application/octet-stream type file for further testing please?

This ticket was mentioned in Slack in #core-media by desrosj. View the logs.


7 years ago

#4 @ironprogrammer
4 years ago

#57530 was marked as a duplicate.

#5 @ironprogrammer
4 years ago

  • Keywords has-testing-info added

Reproduction Report

I'm providing an updated test report to confirm this issue still exists.

Steps to Reproduce

  1. In your test WordPress site, navigate to Media.
  2. Attempt to upload an audio file that has a MIME type of video/mp4 (e.g. Monday%20at%202-57%20AM.m4a).
  3. 🐞 The uploader responds with the error: "Sorry, you are not allowed to upload this file type."

Environment

  • Hardware: MacBook Pro Apple M1 Pro
  • OS: macOS 12.6.2
  • Browser: Safari 16.2
  • Server: nginx/1.23.3
  • PHP: 7.4.33
  • WordPress: 6.2-alpha-54642-src

Expected Results

  • ✅ The uploader should allow audio files with a MIME type of video/mp4, which is common for Android.

Actual Results

  • ❌ The uploader does not allow upload of an audio file with MIME type video/mp4 and responds with an error. This occurred for a sample .mp3 and .m4a file.

Additional Notes

When using macOS Safari, .m4a files may be converted to .mp3 when using Trac's attachment download link. However, this does not change the MIME type, so the resultant converted .mp3 file should still validate the error. To avoid ambiguity, when downloading contributor-supplied samples, I recommend using a different browser, or curl -O <url-to-file> instead.

The sample audio file's MIME type was verified using (includes Safari's .mp3 conversion):

$ file -I Monday\ at\ 2-57\ AM* 
Monday at 2-57 AM.m4a:     video/mp4; charset=binary
Monday at 2-57 AM.m4a.mp3: video/mp4; charset=binary

A sample iOS-generated .m4a was verified using:

$ file -I iPhone-sample.m4a
iPhone-sample.m4a: audio/x-m4a; charset=binary

Supplemental Artifacts

The sample Android recorder audio file was supplied by @pontifier in #57530.

#6 @wordpressdotorg
16 months ago

  • Keywords has-test-info added; has-testing-info removed

#7 @joedolson
4 weeks ago

  • Milestone Awaiting Review7.2
  • Owner set to joedolson
  • Status newaccepted

The problem occurs because some m4a or mp4 files return a real mime that is a video but an extension that maps to being audio.

At https://github.com/WordPress/wordpress-develop/blob/a554ae14aeef4b9f1b7c2f494d932754d706e445/src/wp-includes/functions.php#L3258, the check verifies that the mime type starts with the same media type as the application maps to the type. But when an audio file (like m4a) is wrapping an mp4, then those two types don't match.

This problems needs similar fuzzy matching like the same function uses for some other types, so that these pass checks.

First step is to identify a thorough list of cases where the mimetype and extension will map to different types, so that we can appropriately map these in the type checking.

This ticket was mentioned in PR #13156 on WordPress/wordpress-develop by @sanket.parmar.


4 weeks ago
#8

  • Keywords has-patch has-unit-tests added; needs-patch removed

## Problem

Uploading an audio file whose real MIME type doesn't share the same major type as its extension is rejected with "Sorry, you are not allowed to upload this file type." The common real-world case is audio stored in an MP4/ISO-BMFF container — .m4a/.m4b, and some .mp3 files produced by mobile recorders — which fileinfo detects as video/mp4, while the extension maps to audio/mpeg.

In wp_check_filetype_and_ext(), the audio/* / video/* branch forgives sub-type mismatches but still requires the *major* type to match, so audio/mpeg (from the extension) vs video/mp4 (from finfo) fails and the file is rejected.

## Change

Add a small, curated allow-list for known audio-in-MP4-container confusions inside that branch, mirroring the existing special-casing for text/csv and text/rtf. All three affected extensions (m4a, m4b, mp3) map to audio/mpeg, so the case collapses to a single entry:

$cross_container_types = array(
    'audio/mpeg' => array( 'video/mp4' ),
);

The check stays strict for every other combination. The application/octet-stream variant from the original report already passes today via the $nonspecific_types branch, so this targets only the remaining video/mp4 case.

Security: the worst case is a genuine MP4 video uploaded as .m4a being treated as audio/mpeg — both are allowed media types served as downloads, so no new upload vector is opened.

## Testing instructions

  1. Attempt to upload an audio file that fileinfo reports as video/mp4 (e.g. an .m4a from a phone recorder, or any MP4 container renamed to .m4a) via Media → Add New.
  2. Before this change: the upload fails with "Sorry, you are not allowed to upload this file type."
  3. After this change: the file uploads and appears in the Media Library as audio (audio/mpeg).

Automated coverage: a new case in the data_wp_check_filetype_and_ext provider uploads a fixture detected as video/mp4 and asserts it resolves to ext = m4a, type = audio/mpeg. The full Tests_Functions suite passes on single and multisite.

## Use of AI Tools

  • AI assistance: Yes
  • Tool(s): Claude Code
  • Model(s): Claude Opus 4.8
  • Used for: investigating the root cause, drafting the fix and unit test, and verifying via automated tests and a live Media Library upload.

#9 @sanket.parmar
4 weeks ago

Thanks for the clear diagnosis, @joedolson. I dug into wp_check_filetype_and_ext() and can confirm the exact behaviour, along with a first pass at the "thorough list" of cross-type cases.

Where it breaks

In the audio/* / video/* branch, the leniency forgives sub-type mismatches but still requires the major type to match:

} elseif ( str_starts_with( $real_mime, 'video/' ) || str_starts_with( $real_mime, 'audio/' ) ) {
    if ( substr( $real_mime, 0, strcspn( $real_mime, '/' ) ) !== substr, '/' ) ) ) {
        $type = false;
        $ext  = false;
    }
}

An .m4a / .m4b (and some .mp3) is audio wrapped in an ISO/MP4 container, so finfo reports the container as video/mp4, while the extension maps to audio/mpeg (mp3|m4a|m4b => audio/mpeg). Major types differ (audio vs video)

What already passes

Worth noting the original application/octet-stream case from the repo $nonspecific_types branch allows application, video, and audiomajor types, so an .m4a detected as application/octet-stream uploads fine today. The only remaining failure is an audio extension whose real mime is video/mp4.

Cases where extension and real mime map to different major types

Extension Maps to ($type) Real mime from finfo Result today
m4a, m4b, mp3 audio/mpeg video/mp4 rejected

All three extensions map to audio/mpeg, so the audio-in-MP4-containere confusion: audio/mpeg detected as video/mp4. (A raw .aac is ADTSand detects as audio/aac, so it isn't affected — only AAC inside an MP4 container, i.e. .m4a, is, and that already maps to audio/mpeg.)

Proposed approach
Add a small curated allow-list of known audio-in-MP4-container confusions inside that branch, mirroring the existing special-casing for text/csv and text/rtf:

    'audio/mpeg' => array( 'video/mp4' ),                                                                                                                                );

This keeps the check strict for everything else. Security-wise the worst case is a genuine MP4 video uploaded as .m4a being treated as audio/mpeg — both are allowed media types, so no new vector is opened. The map is trivially extensiblonfusions surface.

Verification

I confirmed the behaviour both ways:

  • A new unit test in the data_wp_check_filetype_and_ext provider — an audio file detected as video/mp4 — fails on trunk (ext/type come back false) and passes with the fix, resolving to m4a / audio/mpeg. Full Tests_Functions multisite.
  • A live upload via Media → Add New: trunk rejects the file with "Sorry, you are not allowed to upload this file type", and with the patch it uploads and appears in the Library as audio.

PR: https://github.com/WordPress/wordpress-develop/pull/13156

Happy to adjust the mapping if you'd prefer a broader or narrower list.

@arkaprabhachowdhury
4 weeks ago

PR #13156 Playground result: the MP4 container renamed to .m4a uploads successfully and appears as an audio attachment.

#10 @arkaprabhachowdhury
4 weeks ago

Test Report

Tested PR: #13156

Environment: WordPress Playground using ?core-pr=13156, WordPress 7.2-alpha-20260819.063144, Chrome, 2026-08-19.

Test case:

  1. Started a fresh Playground with PR #13156.
  2. Opened Media -> Add New.
  3. Uploaded an MP4/ISO-BMFF sample renamed to issue-video-container.m4a, matching the documented reproduction for a file detected by fileinfo as video/mp4.
  4. Opened the uploaded attachment.

Expected result: The file should be accepted instead of showing "Sorry, you are not allowed to upload this file type."

Actual result: PASS. In a separate trunk Playground (?wp=trunk), the same file failed with that error. With PR #13156, the upload succeeded; the attachment editor showed issue-video-container.m4a, M4A (video/mp4), 10 seconds, and an Audio Player.

Screenshot: PR #13156 Playground result: the MP4 container renamed to .m4a uploads successfully and appears as an audio attachment.

Note: See TracTickets for help on using tickets.