Ticket #5328: 0001-Added-support-for-blacklisting-whitelisting-mimetype.patch

File 0001-Added-support-for-blacklisting-whitelisting-mimetype.patch, 9.8 KB (added by molgrum, 9 years ago)
  • mediagoblin/edit/views.py

    From 52bf45eb3ac481c08b2a6a4fe7d3c5d79bf76f9e Mon Sep 17 00:00:00 2001
    From: Andreas Nilsson <nilsson.andreas.85@gmail.com>
    Date: Sun, 21 Jun 2015 14:09:13 +0200
    Subject: [PATCH] Added support for blacklisting/whitelisting mimetypes. Also
     checks max_file_size for attachments.
    
    I added support for blacklisting and whitelisting mimetypes in
    attachments. The idea is to block for example javascript and html to be
    uploaded as attachments.
    
    The code I have written so far reads from mediagoblin.ini:
    [attachments] section and "blacklist" / "whitelist" properties.
    
    Set in mediagoblin_local.ini:
    allow_attachments = true
    
    And add section:
    
    [attachments]
    blacklist = application/javascript, text/html, somethingelse
    
    Blacklisting is for setting up what mime types to ALWAYS deny.
    Whitelisting is for setting up what mime types to ONLY allow.
    
    Also added support for limiting attachment file size by reading
    max_file_size from the .ini file:
    max_file_size = 1024
    
    That would be one Gigabyte (I think).
    
    The solution is not very elegant but it works!
    ---
     mediagoblin/edit/views.py                          | 62 ++++++++++++++++-----
     mediagoblin/static/js/file_size.js                 | 65 ++++++++++++++++------
     .../templates/mediagoblin/edit/attachments.html    |  9 +++
     3 files changed, 105 insertions(+), 31 deletions(-)
    
    diff --git a/mediagoblin/edit/views.py b/mediagoblin/edit/views.py
    index 97e33e6..2ca0d9d 100644
    a b def edit_media(request, media):  
    103103        {'media': media,
    104104         'form': form})
    105105
    106 
    107 # Mimetypes that browsers parse scripts in.
    108 # Content-sniffing isn't taken into consideration.
    109 UNSAFE_MIMETYPES = [
    110         'text/html',
    111         'text/svg+xml']
    112 
     106def read_list_from_config(section):
     107    global_config = mg_globals.global_config
     108    mimelist = []
     109    if 'attachments' in global_config and section in global_config['attachments']:
     110        mimelist = global_config['attachments'][section][:] # FIXME: [:] is a bit hackish
     111    return mimelist
    113112
    114113@get_media_entry_by_id
    115114@require_active_login
    def edit_attachments(request, media):  
    117116    if mg_globals.app_config['allow_attachments']:
    118117        form = forms.EditAttachmentsForm()
    119118
     119        # Mimetypes that browsers parse scripts in.
     120        # Content-sniffing isn't taken into consideration.
     121        UNSAFE_MIMETYPES = []
     122        SAFE_MIMETYPES = []
     123
     124        # Blacklisting is for setting up what mime types to ALWAYS deny
     125        UNSAFE_MIMETYPES = read_list_from_config('blacklist')
     126
     127        # Whitelisting is for setting up what mime types to ONLY allow (optional)
     128        SAFE_MIMETYPES = read_list_from_config('whitelist')
     129
     130        bMimeAllowed = True
     131
    120132        # Add any attachements
    121133        if 'attachment_file' in request.files \
    122134            and request.files['attachment_file']:
    def edit_attachments(request, media):  
    131143            # This method isn't flawless as we do the mimetype lookup on the
    132144            # machine parsing the upload form, and not necessarily the machine
    133145            # serving the attachments.
    134             if mimetypes.guess_type(
    135                     request.files['attachment_file'].filename)[0] in \
    136                     UNSAFE_MIMETYPES:
    137                 public_filename = secure_filename('{0}.notsafe'.format(
    138                     request.files['attachment_file'].filename))
     146
     147            attachment_file = request.files['attachment_file'].filename
     148
     149            # Check blacklist and (optionally) whitelist, guess_type strict is set to False
     150            # to include more mimetypes to be scanned
     151            if (SAFE_MIMETYPES != []) and (not mimetypes.guess_type(attachment_file, False)[0] \
     152                in SAFE_MIMETYPES):
     153                    # We hit a mime type that is NOT whitelisted and whitelist exists
     154                    messages.add_message(request, messages.ERROR, "Sorry, that format is not allowed!")
     155                    bMimeAllowed = False
     156                    #raise Forbidden("Attachment mimetype is not allowed")
     157                    public_filename = secure_filename('{0}.notsafe'.format(
     158                    attachment_file))
     159            elif (UNSAFE_MIMETYPES != []) and (mimetypes.guess_type(attachment_file, False)[0] \
     160                in UNSAFE_MIMETYPES):
     161                    # We hit a mime type that is blacklisted and blacklist exists
     162                    messages.add_message(request, messages.ERROR, "Sorry, that format is not allowed!")
     163                    bMimeAllowed = False
     164                    #raise Forbidden("Attachment mimetype is not allowed")
     165                    public_filename = secure_filename('{0}.notsafe'.format(
     166                    attachment_file))
    139167            else:
    140                 public_filename = secure_filename(
    141                         request.files['attachment_file'].filename)
     168                public_filename = secure_filename(attachment_file)
     169
     170            if not bMimeAllowed or request.form['max_size_reached'] == "true":
     171                return render_to_response(
     172                    request,
     173                    'mediagoblin/edit/attachments.html',
     174                    {'media': media,
     175                     'form': form})
    142176
    143177            attachment_public_filepath \
    144178                = mg_globals.public_store.get_unique_filepath(
  • mediagoblin/static/js/file_size.js

    diff --git a/mediagoblin/static/js/file_size.js b/mediagoblin/static/js/file_size.js
    index 2238ef8..9ba366b 100644
    a b  
    1616 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    1717 */
    1818
    19 $(document).ready(function(){
    20     var file = document.getElementById('file');
    21     var uploaded = parseInt(document.getElementById('uploaded').value);
    22     var upload_limit = parseInt(document.getElementById('upload_limit').value);
    23     var max_file_size = parseInt(document.getElementById('max_file_size').value);
     19var file;
     20var uploaded;
     21var upload_limit;
     22var max_file_size;
     23
     24// FIXME: Very hackish, please fix if you can. This is because the extra HTML won't appear.
     25var popup;
    2426
    25     file.onchange = function() {
    26         var file_size = file.files[0].size / (1024.0 * 1024);
     27function check_file_size() {
     28    var file_size = file.files[0].size / (1024.0 * 1024);
    2729
    28         if (file_size >= max_file_size) {
    29             $('#file').after('<p id="file_size_error" class="form_field_error">Sorry, the file size is too big.</p>');
     30    if (file_size >= max_file_size) {
     31        if (popup)
     32        {
     33            alert("Sorry, the file size is too big.")
     34            document.getElementById('max_size_reached').value = true;
    3035        }
    31         else if (document.getElementById('file_size_error')) {
    32             $('#file_size_error').hide();
     36        else if (document.getElementById('upload_limit_error')) {
     37            $('#file').after('<p id="file_size_error" class="form_field_error">Sorry, the file size is too big.</p>');
    3338        }
     39    }
     40    else if (document.getElementById('file_size_error')) {
     41        $('#file_size_error').hide();
     42    }
    3443
    35         if (upload_limit) {
    36             if ( uploaded + file_size >= upload_limit) {
    37                 $('#file').after('<p id="upload_limit_error" class="form_field_error">Sorry, uploading this file will put you over your upload limit.</p>');
     44    if (upload_limit) {
     45        if ( uploaded + file_size >= upload_limit) {
     46            if (popup)
     47            {
     48                alert("Sorry, uploading this file will put you over your upload limit.");
     49                document.getElementById('max_size_reached').value = true;
    3850            }
    3951            else if (document.getElementById('upload_limit_error')) {
    40                 $('#upload_limit_error').hide();
    41             console.log(file_size >= max_file_size);
     52                $('#file').after('<p id="upload_limit_error" class="form_field_error">Sorry, uploading this file will put you over your upload limit.</p>');
    4253            }
    4354        }
    44     };
     55        else if (document.getElementById('upload_limit_error')) {
     56            $('#upload_limit_error').hide();
     57            console.log(file_size >= max_file_size);
     58        }
     59    }
     60}
     61
     62$(document).ready(function(){
     63    popup = false;
     64    file = document.getElementById('file');
     65    if (file == null)
     66    {
     67        popup = true;
     68        file = document.getElementById('attachment_file');
     69    }
     70    uploaded = parseInt(document.getElementById('uploaded').value);
     71    upload_limit = parseInt(document.getElementById('upload_limit').value);
     72    max_file_size = parseInt(document.getElementById('max_file_size').value);
     73
     74    file.onchange = check_file_size
     75
    4576});
  • mediagoblin/templates/mediagoblin/edit/attachments.html

    diff --git a/mediagoblin/templates/mediagoblin/edit/attachments.html b/mediagoblin/templates/mediagoblin/edit/attachments.html
    index d1e33c4..b6d213c 100644
    a b  
    1919
    2020{% import "/mediagoblin/utils/wtforms.html" as wtforms_util %}
    2121
     22{% block mediagoblin_head %}
     23  <script type="text/javascript"
     24          src="{{ request.staticdirect('/js/file_size.js') }}"></script>
     25{% endblock %}
     26
    2227{% block title -%}
    2328  {% trans media_title=media.title -%}
    2429      Editing attachments for {{ media_title }}
     
    6065        <a class="button_action" href="{{ media.url_for_self(request.urlgen) }}">
    6166          {%- trans %}Cancel{% endtrans -%}
    6267        </a>
     68        <input type="hidden" id="uploaded" value="{{ media.get_uploader.uploaded }}" />
     69        <input type="hidden" id="upload_limit" value="{{ media.get_uploader.upload_limit }}" />
     70        <input type="hidden" id="max_file_size" value="{{ global_config['mediagoblin']['max_file_size'] }}" />
     71        <input type="hidden" name="max_size_reached" id="max_size_reached" value="false" />
    6372        <input type="submit" value="{% trans %}Save changes{% endtrans %}"
    6473               class="button_form" />
    6574        {{ csrf_token }}