Handling email piping attachments and detecting unsupported file types

I have a plugin that I am working on which uses Email Piping. When an email is processed I want all the attachments from the email to be added to the WordPress Media Library.

Currently I am using wp_insert_attachment, wp_generate_attachment_metadata and wp_update_attachment_metadata which is working fine, however, using these functions allows for any file which is attached to an email to be added to the WP Media Library. The thing that worries me about this is that it could open up the ability for people to attach malicious files which could then be readily accessed and used for nasty purposes.

As such, what I would like to do is when handling attachments be able to identify if the attachment is a supported file type. I have investigated wp_check_filetype, wp_check_filetype_and_ext and get_allowed_mime_types which I can use to confirm the security of a file, but unless I have missed something I cannot see how to pass a file through a function to confirm if it is a supported file type.

If I can identify the files that aren’t supported then I can create a zip file of these files to be uploaded rather that the raw file, which in my book should close the security hole that I can see. I am doing this for files uploaded via a form, supported files upload with media_handle_upload and unsupported are processed via PHP, added to a zip file and then attached using wp_insert_attachment, wp_generate_attachment_metadata and wp_update_attachment_metadata.

Any help would be greatly appreciated.

$upload_dir = wp_upload_dir();
$tmp_pathfile="/home/user/public_html/wp-content/uploads/tmp_file.js";
$tmp_filename="tmp_file.js";
$filetype = wp_check_filetype( $tmp_pathfile );
if ( is file allowed for WordPress Media Library ) { // This is where I want to check if the file is supported by the WordPress Media Library
    $data = array(
        'guid' => $upload_dir['url'] . "https://wordpress.stackexchange.com/" . $tmp_filename,
        'post_title' => $tmp_filename,
        'post_content' => '',
        'post_status' => 'inherit',
        'post_mime_type' => $filetype['type']
    );
    $theID = wp_insert_attachment( $data, $tmp_pathfile );
    $attach_data = wp_generate_attachment_metadata( $theID, $tmp_pathfile );
    wp_update_attachment_metadata( $theID, $attach_data );
} else { // File not supported by the WordPress Media Library
    $zip_pathfile = $tmp_pathfile . '.zip';
    while ( file_exists( $zip_file ) ) {
        $zip_file = $tmp_pathfile . '_' . time() . '.zip';
    }
    $zip = new ZipArchive();
    if ( $zip->open( $zip_file, ZipArchive::CREATE ) ) {
        $zip->addFile( $tmp_pathfile, $tmp_filename );
        $zip->close();
        $filetype = wp_check_filetype( $zip_file );
        $data = array(
            'guid'           => $zip_file, 
            'post_mime_type' => $filetype['type'],
            'post_title'     => basename( $zip_file ),
            'post_content'   => '',
            'post_status'    => 'inherit'
        );
        $theID = wp_insert_attachment( $data, $zip_file );
        $attach_data = wp_generate_attachment_metadata( $theID, $zip_file );
        wp_update_attachment_metadata( $theID, $attach_data );
        unlink( $tmp_pathfile);
    }
}

1 Answer
1

Ok, seems I was on the right track… I still haven’t found a WordPress function that achieves what I need, but the closest I have found is to use the get_allowed_mime_types function.

I created the following function which checks if the file is within the get_allowed_mime_types array and if so returns true (file processed using wp_insert_attachment, wp_generate_attachment_metadata and wp_update_attachment_metadata) or false (file zipped and then processed using wp_insert_attachment, wp_generate_attachment_metadata and wp_update_attachment_metadata)

function is_upload_allowed( $file ) {
    $filetype = wp_check_filetype( $file );
    $file_ext = $filetype['ext'];
    $mimes = get_allowed_mime_types();
    foreach ( $mimes as $type => $mime ) {
        if ( strpos( $type, $file_ext ) !== false ) {
            return true;
        }
    }
    return false;
}

So my updated code looks like this:

$upload_dir = wp_upload_dir();
$tmp_pathfile="/home/user/public_html/wp-content/uploads/tmp_file.js";
$tmp_filename="tmp_file.js";
$filetype = wp_check_filetype( $tmp_pathfile );
if ( is_upload_allowed( tmp_pathfile ) ) { // This is where I want to check if the file is supported by the WordPress Media Library
    $data = array(
        'guid' => $upload_dir['url'] . "https://wordpress.stackexchange.com/" . $tmp_filename,
        'post_title' => $tmp_filename,
        'post_content' => '',
        'post_status' => 'inherit',
        'post_mime_type' => $filetype['type']
    );
    $theID = wp_insert_attachment( $data, $tmp_pathfile );
    $attach_data = wp_generate_attachment_metadata( $theID, $tmp_pathfile );
    wp_update_attachment_metadata( $theID, $attach_data );
} else { // File not supported by the WordPress Media Library
    $zip_pathfile = $tmp_pathfile . '.zip';
    while ( file_exists( $zip_file ) ) {
        $zip_file = $tmp_pathfile . '_' . time() . '.zip';
    }
    $zip = new ZipArchive();
    if ( $zip->open( $zip_file, ZipArchive::CREATE ) ) {
        $zip->addFile( $tmp_pathfile, $tmp_filename );
        $zip->close();
        $filetype = wp_check_filetype( $zip_file );
        $data = array(
            'guid'           => $zip_file, 
            'post_mime_type' => $filetype['type'],
            'post_title'     => basename( $zip_file ),
            'post_content'   => '',
            'post_status'    => 'inherit'
        );
        $theID = wp_insert_attachment( $data, $zip_file );
        $attach_data = wp_generate_attachment_metadata( $theID, $zip_file );
        wp_update_attachment_metadata( $theID, $attach_data );
        unlink( $tmp_pathfile);
    }
}

It works, would still be happy to hear if there is a builtin WordPress function that achieves the same as the is_upload_allowed function I created.

Leave a Comment