Skip to content

Commit

Permalink
Merge pull request #4479 from gitcoinco/validate-uploads
Browse files Browse the repository at this point in the history
Validate uploaded file type and size
  • Loading branch information
thelostone-mc authored May 29, 2019
2 parents c9b68e2 + 18d11bb commit 3249e6f
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 30 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ FROM python:3.7-alpine3.8

ENV PYTHONUNBUFFERED 1
ENV PYTHONDONTWRITEBYTECODE 1
ARG PACKAGES="postgresql-libs libxml2 libxslt freetype libffi jpeg libmaxminddb bash git tar gzip inkscape"
ARG PACKAGES="postgresql-libs libxml2 libxslt freetype libffi jpeg libmaxminddb bash git tar gzip inkscape libmagic"
ARG BUILD_DEPS="gcc g++ postgresql-dev libxml2-dev libxslt-dev freetype-dev libffi-dev jpeg-dev linux-headers autoconf automake libtool make dos2unix"
WORKDIR /code

Expand Down
6 changes: 5 additions & 1 deletion app/assets/v2/js/jobs.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,11 @@ const save_job_status = function() {
};

$.ajax(profile).done(function(response) {
_alert(response.message, 'info');
if (response.status == 200) {
_alert(response.message, 'info');
} else {
_alert(response.message, 'error');
}
}).fail(function(error) {
_alert(error, 'error');
});
Expand Down
36 changes: 20 additions & 16 deletions app/assets/v2/js/pages/bounty_details.js
Original file line number Diff line number Diff line change
Expand Up @@ -681,22 +681,26 @@ var show_interest_modal = function() {
};

$.ajax(ndaSend).done(function(response) {
_alert(response.message, 'info');
add_interest(document.result['pk'], {
issue_message: msg,
signed_nda: response.bounty_doc_id
}).then(success => {
if (success) {
$(self).attr('href', '/uninterested');
$(self).find('span').text(gettext('Stop Work'));
$(self).parent().attr('title', '<div class="tooltip-info tooltip-sm">' + gettext('Notify the funder that you will not be working on this project') + '</div>');
modals.bootstrapModal('hide');
}
}).catch((error) => {
if (error.responseJSON.error === 'You may only work on max of 3 issues at once.')
return;
throw error;
});
if (response.status == 200) {
_alert(response.message, 'info');
add_interest(document.result['pk'], {
issue_message: msg,
signed_nda: response.bounty_doc_id
}).then(success => {
if (success) {
$(self).attr('href', '/uninterested');
$(self).find('span').text(gettext('Stop Work'));
$(self).parent().attr('title', '<div class="tooltip-info tooltip-sm">' + gettext('Notify the funder that you will not be working on this project') + '</div>');
modals.bootstrapModal('hide');
}
}).catch((error) => {
if (error.responseJSON.error === 'You may only work on max of 3 issues at once.')
return;
throw error;
});
} else {
_alert(response.message, 'error');
}
}).fail(function(error) {
_alert(error, 'error');
});
Expand Down
14 changes: 10 additions & 4 deletions app/assets/v2/js/pages/new_bounty.js
Original file line number Diff line number Diff line change
Expand Up @@ -719,10 +719,16 @@ $('#submitBounty').validate({
};

$.ajax(settings).done(function(response) {
_alert(response.message, 'info');
ipfsBounty.payload.unsigned_nda = response.bounty_doc_id;
if (data.featuredBounty) payFeaturedBounty();
else do_bounty();
if (response.status == 200) {
_alert(response.message, 'info');
ipfsBounty.payload.unsigned_nda = response.bounty_doc_id;
if (data.featuredBounty) payFeaturedBounty();
else do_bounty();
} else {
_alert('Unable to upload NDA. ', 'error');
unloading_button($('.js-submit'));
console.log('NDA error:', response.message);
}
}).fail(function(error) {
_alert('Unable to upload NDA. ', 'error');
unloading_button($('.js-submit'));
Expand Down
46 changes: 38 additions & 8 deletions app/dashboard/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_GET, require_POST

import magic
from app.utils import clean_str, ellipses, get_default_network
from avatar.utils import get_avatar_context_for_user
from dashboard.utils import ProfileHiddenException, ProfileNotFoundException, get_bounty_from_invite_url, profile_helper
Expand Down Expand Up @@ -1767,6 +1768,11 @@ def profile_job_opportunity(request, handle):
Args:
handle (str): The profile handle.
"""
uploaded_file = request.FILES.get('job_cv')
error_response = invalid_file_response(uploaded_file, supported=['application/pdf'])
# 400 is ok because file upload is optional here
if error_response and error_response['status'] != '400':
return JsonResponse(error_response)
try:
profile = profile_helper(handle, True)
profile.job_search_status = request.POST.get('job_search_status', None)
Expand All @@ -1788,6 +1794,29 @@ def profile_job_opportunity(request, handle):
return JsonResponse(response)


def invalid_file_response(uploaded_file, supported):
response = None
if not uploaded_file:
response = {
'status': 400,
'message': 'No File Found'
}
elif uploaded_file.size > 31457280:
# 30MB max file size
response = {
'status': 413,
'message': 'File Too Large'
}
else:
file_mime = magic.from_buffer(next(uploaded_file.chunks()), mime=True)
logger.info('uploaded file: %s' % file_mime)
if file_mime not in supported:
response = {
'status': 415,
'message': 'Invalid File Type'
}
return response

@csrf_exempt
@require_POST
def bounty_upload_nda(request):
Expand All @@ -1796,22 +1825,23 @@ def bounty_upload_nda(request):
Args:
bounty_id (int): The bounty id.
"""
if request.FILES.get('docs', None):
uploaded_file = request.FILES.get('docs', None)
error_response = invalid_file_response(
uploaded_file, supported=['application/pdf',
'application/msword',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document'])
if not error_response:
bountydoc = BountyDocuments.objects.create(
doc=request.FILES.get('docs', None),
doc=uploaded_file,
doc_type=request.POST.get('doc_type', None)
)
response = {
'status': 200,
'bounty_doc_id': bountydoc.pk,
'message': 'NDA saved'
}
else:
response = {
'status': 400,
'message': 'No File Found'
}
return JsonResponse(response)

return JsonResponse(error_response) if error_response else JsonResponse(response)



Expand Down
1 change: 1 addition & 0 deletions requirements/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,4 @@ raven==6.9.0
sentry-sdk
websocket-client
bleach
python-magic

0 comments on commit 3249e6f

Please sign in to comment.