def test_post_update_existing(self, scan_and_complete_patch): """ The uploaded file should be saved to quarantine in spite of having the same name as an previously uploaded file. """ os.mkdir(self.upload_basepath) rf = RequestsFactory(self.request_id) rf.add_file(self.upload_path) file_contents = b"contents" with scan_and_complete_patch: response = self.client.post( '/upload/' + self.request_id, data={ "file": (BytesIO(file_contents), self.filename), "update": True } ) self.assertEqual( json.loads(response.data.decode()), { "files": [{ "name": self.filename_secure, "original_name": self.filename, "size": len(file_contents) }] } ) # checked mocked call (is_update = True) scan_and_complete_patch.assert_called_once_with( self.request_id, self.quarantine_path, True) # check redis key set self.assertEqual(redis.get(self.key_update).decode(), upload_status.PROCESSING) # check file saved self.assertTrue(os.path.exists(self.quarantine_path))
def status(): """ Check the status of an upload. Request Parameters: - request_id - filename - for_update (bool, optional) :returns: { "status": upload status } """ try: status = redis.get( get_upload_key(request.args['request_id'], secure_filename(request.args['filename']), eval_request_bool(request.args.get('for_update')))) if status is not None: response = {"status": status.decode("utf-8")} else: response = {"error": "Upload status not found."} status_code = 200 except KeyError: response = {} status_code = 422 return jsonify(response), status_code
def status(): """ Check the status of an upload. Request Parameters: - request_id - filename - for_update (bool, optional) :returns: { "status": upload status } """ try: status = redis.get( get_upload_key( request.args['request_id'], secure_filename(request.args['filename']), eval_request_bool(request.args.get('for_update')) ) ) if status is not None: response = {"status": status.decode("utf-8")} else: response = {"error": "Upload status not found."} status_code = 200 except KeyError: sentry.captureException() response = {} status_code = 422 return jsonify(response), status_code
def test_post(self, scan_and_complete_patch): file_contents = b"contents" with scan_and_complete_patch: # make request response = self.client.post( '/upload/' + self.request_id, data={ "file": (BytesIO(file_contents), self.filename) } ) # check response self.assertEqual( json.loads(response.data.decode()), { "files" : [{ "name": self.filename_secure, "original_name": self.filename, "size": len(file_contents) }] } ) # checked mocked call (is_update = False) scan_and_complete_patch.assert_called_once_with( self.request_id, self.quarantine_path, False) # check redis key set self.assertEqual(redis.get(self.key).decode(), upload_status.PROCESSING) # check file saved self.assertTrue(os.path.exists(self.quarantine_path))
def test_good_file_update(self): with patch( 'app.upload.utils.scan_file' ): scan_and_complete_upload(self.request_id, self.quarantine_path, is_update=True) self.assertFalse(os.path.exists(self.quarantine_path)) self.assertFalse(os.path.exists(self.upload_path)) self.assertTrue(os.path.exists(self.update_path)) self.assertEqual(redis.get(self.key_update).decode(), upload_status.READY)
def redis_get_file_metadata(request_or_response_id, filepath, is_update=False): """ Returns a tuple containing a file's ( size (int), mime type (str), and hash (str) ). """ data = redis.get(_get_file_metadata_key( request_or_response_id, filepath, is_update)).decode().split(':') data[0] = int(data[0]) # size return tuple(data)
def redis_get_file_metadata(request_or_response_id, filepath, is_update=False): """ Returns a tuple containing a file's ( size (int), mime type (str), and hash (str) ). """ data = redis.get( _get_file_metadata_key(request_or_response_id, filepath, is_update)).decode().split(':') data[0] = int(data[0]) # size return tuple(data)
def delete(r_id_type, r_id, filecode): """ Removes an uploaded file. :param r_id_type: "response" or "request" :param r_id: the Response or Request identifier :param filecode: the encoded name of the uploaded file (base64 without padding) Optional request body parameters: - quarantined_only (bool) only delete the file if it is quarantined (beware: takes precedence over 'updated_only') - updated_only (bool) only delete the file if it is in the 'updated' directory :returns: On success: { "deleted": filename } On failure: { "error": error message } """ filename = secure_filename(b64decode_lenient(filecode)) if r_id_type not in ["request", "response"]: response = {"error": "Invalid ID type."} else: try: if r_id_type == "response": response = Responses.query.filter_by(id=r_id, deleted=False) r_id = response.request_id path = '' quarantined_only = eval_request_bool( request.form.get('quarantined_only')) has_add_edit = (is_allowed(user=current_user, request_id=r_id, permission=permission.ADD_FILE) or is_allowed(user=current_user, request_id=r_id, permission=permission.EDIT_FILE)) if quarantined_only and has_add_edit: path = os.path.join( current_app.config['UPLOAD_QUARANTINE_DIRECTORY'], r_id) elif eval_request_bool(request.form.get('updated_only')) and \ is_allowed(user=current_user, request_id=r_id, permission=permission.EDIT_FILE): path = os.path.join(current_app.config['UPLOAD_DIRECTORY'], r_id, UPDATED_FILE_DIRNAME) else: path_for_status = { upload_status.PROCESSING: current_app.config['UPLOAD_QUARANTINE_DIRECTORY'], upload_status.SCANNING: current_app.config['UPLOAD_QUARANTINE_DIRECTORY'], upload_status.READY: current_app.config['UPLOAD_DIRECTORY'] } status = redis.get(get_upload_key(r_id, filename)) if status is not None: dest_path = path_for_status[status.decode("utf-8")] if (dest_path == current_app.config['UPLOAD_QUARANTINE_DIRECTORY'] and has_add_edit ) or (dest_path == current_app.config['UPLOAD_DIRECTORY'] and is_allowed(user=current_user, request_id=r_id, permission=permission.ADD_FILE)): path = os.path.join(dest_path, r_id) filepath = os.path.join(path, filename) found = False if path != '': if quarantined_only: if os.path.exists(filepath): os.remove(filepath) found = True else: if fu.exists(filepath): fu.remove(filepath) found = True if found: response = {"deleted": filename} else: response = {"error": "Upload not found."} except Exception as e: current_app.logger.exception( "Error on DELETE /upload/: {}".format(e)) response = {"error": "Failed to delete '{}'".format(filename)} return jsonify(response), 200
def delete(r_id_type, r_id, filecode): """ Removes an uploaded file. :param r_id_type: "response" or "request" :param r_id: the Response or Request identifier :param filecode: the encoded name of the uploaded file (base64 without padding) Optional request body parameters: - quarantined_only (bool) only delete the file if it is quarantined (beware: takes precedence over 'updated_only') - updated_only (bool) only delete the file if it is in the 'updated' directory :returns: On success: { "deleted": filename } On failure: { "error": error message } """ filename = secure_filename(b64decode_lenient(filecode)) if r_id_type not in ["request", "response"]: response = {"error": "Invalid ID type."} else: try: if r_id_type == "response": response = Responses.query.filter_by(id=r_id, deleted=False) r_id = response.request_id path = '' quarantined_only = eval_request_bool(request.form.get('quarantined_only')) has_add_edit = (is_allowed(user=current_user, request_id=r_id, permission=permission.ADD_FILE) or is_allowed(user=current_user, request_id=r_id, permission=permission.EDIT_FILE)) if quarantined_only and has_add_edit: path = os.path.join( current_app.config['UPLOAD_QUARANTINE_DIRECTORY'], r_id ) elif eval_request_bool(request.form.get('updated_only')) and \ is_allowed(user=current_user, request_id=r_id, permission=permission.EDIT_FILE): path = os.path.join( current_app.config['UPLOAD_DIRECTORY'], r_id, UPDATED_FILE_DIRNAME ) else: path_for_status = { upload_status.PROCESSING: current_app.config['UPLOAD_QUARANTINE_DIRECTORY'], upload_status.SCANNING: current_app.config['UPLOAD_QUARANTINE_DIRECTORY'], upload_status.READY: current_app.config['UPLOAD_DIRECTORY'] } status = redis.get(get_upload_key(r_id, filename)) if status is not None: dest_path = path_for_status[status.decode("utf-8")] if (dest_path == current_app.config['UPLOAD_QUARANTINE_DIRECTORY'] and has_add_edit) or ( dest_path == current_app.config['UPLOAD_DIRECTORY'] and is_allowed(user=current_user, request_id=r_id, permission=permission.ADD_FILE) ): path = os.path.join( dest_path, r_id ) filepath = os.path.join(path, filename) found = False if path != '': if quarantined_only: if os.path.exists(filepath): os.remove(filepath) found = True else: if fu.exists(filepath): fu.remove(filepath) found = True if found: response = {"deleted": filename} else: response = {"error": "Upload not found."} except Exception as e: sentry.captureException() current_app.logger.exception("Error on DELETE /upload/: {}".format(e)) response = {"error": "Failed to delete '{}'".format(filename)} return jsonify(response), 200
def test_scan_good_file(self): scan_and_complete_upload(self.request_id, self.quarantine_path) self.assertFalse(os.path.exists(self.quarantine_path)) self.assertTrue(os.path.exists(self.upload_path)) self.assertEqual(redis.get(self.key).decode(), upload_status.READY)