Fixed security hole.
The file_storage.index() view function didn't sanitize its input. This made is possible for an attacker to overwrite any file, including the files of Pillar itself.
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
from multiprocessing import Process
|
from multiprocessing import Process
|
||||||
@@ -7,7 +8,7 @@ from flask import Blueprint
|
|||||||
from flask import abort
|
from flask import abort
|
||||||
from flask import jsonify
|
from flask import jsonify
|
||||||
from flask import send_from_directory
|
from flask import send_from_directory
|
||||||
from flask import url_for
|
from flask import url_for, helpers
|
||||||
from eve.methods.put import put_internal
|
from eve.methods.put import put_internal
|
||||||
from application import app
|
from application import app
|
||||||
from application.utils.imaging import generate_local_thumbnails
|
from application.utils.imaging import generate_local_thumbnails
|
||||||
@@ -19,6 +20,9 @@ from application.utils.cdn import hash_file_path
|
|||||||
from application.utils.gcs import GoogleCloudStorageBucket
|
from application.utils.gcs import GoogleCloudStorageBucket
|
||||||
from application.utils.encoding import Encoder
|
from application.utils.encoding import Encoder
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
file_storage = Blueprint('file_storage', __name__,
|
file_storage = Blueprint('file_storage', __name__,
|
||||||
template_folder='templates',
|
template_folder='templates',
|
||||||
static_folder='../../static/storage',)
|
static_folder='../../static/storage',)
|
||||||
@@ -104,22 +108,34 @@ def build_thumbnails(file_path=None, file_id=None):
|
|||||||
|
|
||||||
|
|
||||||
@file_storage.route('/file', methods=['POST'])
|
@file_storage.route('/file', methods=['POST'])
|
||||||
@file_storage.route('/file/<path:file_name>')
|
@file_storage.route('/file/<path:file_name>', endpoint='index_with_path', methods=['GET', 'POST'])
|
||||||
def index(file_name=None):
|
def index(file_name=None):
|
||||||
#GET file
|
# GET file -> read it
|
||||||
if file_name:
|
if request.method == 'GET':
|
||||||
return send_from_directory(app.config['STORAGE_DIR'], file_name)
|
return send_from_directory(app.config['STORAGE_DIR'], file_name)
|
||||||
#POST file
|
|
||||||
|
# POST file -> save it
|
||||||
|
|
||||||
|
# Sanitize the filename; source: http://stackoverflow.com/questions/7406102/
|
||||||
file_name = request.form['name']
|
file_name = request.form['name']
|
||||||
|
keepcharacters = {' ', '.', '_'}
|
||||||
|
file_name = ''.join(c for c in file_name if c.isalnum() or c in keepcharacters).strip()
|
||||||
|
file_name = file_name.lstrip('.')
|
||||||
|
|
||||||
|
# Determine & create storage directory
|
||||||
folder_name = file_name[:2]
|
folder_name = file_name[:2]
|
||||||
file_folder_path = os.path.join(app.config['STORAGE_DIR'],
|
file_folder_path = helpers.safe_join(app.config['STORAGE_DIR'], folder_name)
|
||||||
folder_name)
|
|
||||||
if not os.path.exists(file_folder_path):
|
if not os.path.exists(file_folder_path):
|
||||||
|
log.info('Creating folder path %r', file_folder_path)
|
||||||
os.mkdir(file_folder_path)
|
os.mkdir(file_folder_path)
|
||||||
file_path = os.path.join(file_folder_path, file_name)
|
|
||||||
|
# Save uploaded file
|
||||||
|
file_path = helpers.safe_join(file_folder_path, file_name)
|
||||||
|
log.info('Saving file %r', file_path)
|
||||||
request.files['data'].save(file_path)
|
request.files['data'].save(file_path)
|
||||||
|
|
||||||
return "{}", 200
|
# TODO: possibly nicer to just return a redirect to the file's URL.
|
||||||
|
return jsonify({'url': url_for('file_storage.index_with_path', file_name=file_name)})
|
||||||
|
|
||||||
|
|
||||||
def process_file(src_file):
|
def process_file(src_file):
|
||||||
|
Reference in New Issue
Block a user