How to sanitize user-provided paths in Python and Flask? - python

I'm using user-provided names as path components in my Python/Flask web app: for example, the user creates a project named "hello", prompting my web app to make a folder called "data/hello/" to store files that the user will upload. I am wondering how to sanitize the user provided name so that, e.g., the user can't type in "../hello" and have the folder be created in a different directory. The best pathname-based solution I can come up with is something like this:
import os.path
rpath = os.path.relpath(input_path)
safepath = rpath[rpath.index('../'):]
Is there a better way to make sure my web app doesn't access files outside of the data directory? Thanks!

Werkzeug provides secure_filename to help with this, an example is provided in the Flask docs
#app.route('/', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
file = request.files['file']
if file and allowed_file(file.filename):
filename = secure_filename(file.filename) #Sanitised here
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return redirect(url_for('uploaded_file',
filename=filename))

Related

How to access uploaded images via URL in Django?

I'm building a simple blog style web app for a friend. I am using CK editor and mostly stock Django 4.1. I'm stumped in how in what would be the best approach to let the creator to upload and serve images in their posts. CK editor allows you to add images via URL and I'd like for it to be possible to the creator to upload their files to the backend and would like to know if there are best practices when it comes to this.
Right now I have this:
def editor_upload(request):
if request.method == 'GET':
form = UploadFileForm
path = os.path.join(settings.STATIC_ROOT, 'files')
files = os.listdir(path)
return render(request, 'upload.html', {"form": form, "files": files})
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES)
file = request.FILES['file']
title = str(file)
if form.is_valid():
path = os.path.join(settings.STATIC_ROOT, 'files')
fs = FileSystemStorage(location=path)
fs.save(title, file)
return redirect('/editor/upload')
def open_file(request, file_name):
print(file_name)
return render(request, 'file.html', {"file": file_name})
in editor_upload, the user can see what files there are in the specified folder (but cannot access them yet) and there is the logic to upload new pictures to the folder. The problem comes now: I don't know how to properly access them.
In open_file, the request receives the name of the file and gives it to the HTML templating. This does not work because the template looks into the project folder and not into the root static folder. I must admit I'm a bit lost with how the static folders in Django works because I've been mostly utilizing just the project static folder. Also, I don't think this would work for CK editor to grab the image.
Any ideas on how I could solve this? I also tried doing it by uploading the image to a database but I had kind of the same problem. I need to somehow generate an URL to pass into CK editor. Many thanks!

How to display csv data in tabular form in Flask Python?

I'm making a web app using the Flask framework with python, I want to make the web able to upload csv without saving and displaying data in a table with the template I made, I've added the syntax for uploading and processing the data until it's in a table view, but after running the website it goes to the 404 not found error message, how can I fix it?
I've made a code in main.py
from datetime import datetime
from flask import Flask, render_template, request, session
from FlaskWebProject2 import app
import os
import pandas as pd
from werkzeug.utils import secure_filename
#*** Flask configuration
UPLOAD_FOLDER = os.path.join('staticFiles', 'uploads')
ALLOWED_EXTENSIONS = {'csv'}
app = Flask(__name__, template_folder='templateFiles', static_folder='staticFiles')
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.secret_key = 'This is your secret key to utilize session in Flask'
#app.route('/')
def index():
return render_template('index.html')
#app.route('/upload', methods=['POST', 'GET'])
def uploadFile():
if request.method == 'POST':
# upload file flask
uploaded_df = request.files['uploaded-file']
# Extracting uploaded data file name
data_filename = secure_filename(uploaded_df.filename)
# flask upload file to database (defined uploaded folder in static path)
uploaded_df.save(os.path.join(app.config['UPLOAD_FOLDER'], data_filename))
# Storing uploaded file path in flask session
session['uploaded_data_file_path'] = os.path.join(app.config['UPLOAD_FOLDER'], data_filename)
return render_template('index_upload_and_show_data_page2.html')
#app.route('/show_data')
def showData():
# Retrieving uploaded file path from session
data_file_path = session.get('uploaded_data_file_path', None)
# read csv file in python flask (reading uploaded csv file from uploaded server location)
uploaded_df = pd.read_csv(data_file_path)
# pandas dataframe to html table flask
uploaded_df_html = uploaded_df.to_html()
return render_template('show_csv_data.html', data_var = uploaded_df_html)
I want the website can show the home page and upload, read and show csv data works properly
If you are requesting /upload and getting a 404. This is natural. You have added a handler for /upload endpoint:
#app.route('/upload', methods=['POST', 'GET'])
def uploadFile():
...
But uploadFile only supports POST requests. When you enter localhost/upload into the browser, the browser sends a GET request to the web server. There are two solutions for you with the same result:
Add another if to uploadFile function and check if incoming request is GET and if it is, show a page(e.g. a form to upload file).
Write another function like upload_file_form exclusively for GET requests to show a form.
P.S.: per PEP8, you should use camel_case for function names. e.g. upload_file rather than uploadFile

Flask Uploading + Folder Creation + FrontEnd Showing it

My problem is the following:
I'm creating and commercial proposal app, it should receive files in the input form, save them inside a folder named as the model.id and show them in the front end, inside the page of the proposal.
My code:
#comercial.route('/NovoOrçamento', methods=['GET', 'POST'])
def novo_orcamento():
form = OrcamentoForm()
if request.method == 'POST':
if form.validate_on_submit():
form.to_database()
else:
flash('Something went wrong.')
return redirect('./NovoOrçamento')
if 'arquivos' not in request.files:
flash('no files')
files = request.files.getlist('arquivos')
for file in files:
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
# My problem starts here at file.save()
return redirect('./Dashboard')
return render_template('novo_orcamento.html', form=form)
I don't have the rest of the code because idk how to actually do it.
I think you are looking for a library for handling and managing file uploads and downloads. I would suggest looking at Flask_uploads library. It allows you to configure multiple file models with different settings for allowed file types, allowed sizes, and where to save them (and many more).
Miguel Grinberg just wrote a superb blog post about exactly your problem this week:
https://blog.miguelgrinberg.com/post/handling-file-uploads-with-flask
If you want to try it on your on, you have to save the uploaded files first, then serve them. Have a look at Flask's send_from_directory function.

calling local python file in flask service

I have two python code, the first one is a flask python code(main.py) which collects image from the user and stored it in my local directory, and the second one is tesseract-ocr(ocrDetection.py) python code where the image are getting started to detect text in it.
Now I desired to integrate these two codes, by importing ocr code in flask [ import ocrDetection in main.py]
import os
#import magic
import urllib.request
from app import app
from flask import Flask, flash, request, redirect, render_template
from werkzeug.utils import secure_filename
ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg'])
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
#app.route('/')
def upload_form():
return render_template('upload.html')
#app.route('/', methods=['POST'])
def upload_file():
if request.method == 'POST':
# check if the post request has the files part
if 'files[]' not in request.files:
flash('No file part')
return redirect(request.url)
files = request.files.getlist('files[]')
for file in files:
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
flash('File(s) successfully uploaded')
return redirect('/')
#app.route('/')
def usemain():
if request.method == 'POST':
import ocrDetection
ocrDetection.mask()
if __name__ == "__main__":
app.run(host='192.168.106.51')
this is not working properly if I give my OCR program after __name__=="__main__" like this
if __name__ == "__main__":
app.run(host='192.168.106.51')
import ocrDetection
it works only when I quit the server (ctrl+c), but I want to start my OCR program simultaniously after giving submit button
This will not work because you have two same routes ('/') which create conflict. I suggest that you create a new route for OCR e.g. '/ocr_detection' or create ocr_detection() as a module in a separate file.
To call OCR module, you can perhaps use it as API endpoint if you want to call from client like browser or as a module to simply call it from your backend (from within the views).
If you call it from within upload_file() module then it will be a sequential call and your browser has to wait until OCR is finished. But to avoid this, you can run periodic script which monitors the UPLOAD_DIR for new files and OCR them. This way your user browsing experience will also be much better.
....
#app.route('/', methods=['POST'])
def upload_file():
if request.method == 'POST':
# check if the post request has the files part
if 'files[]' not in request.files:
flash('No file part')
return redirect(request.url)
files = request.files.getlist('files[]')
for file in files:
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
ocr_detection(filename)
flash('File(s) successfully uploaded')
return redirect('/')
def ocr_detection(file):
import ocrDetection
ocrDetection.mask()
if __name__ == "__main__":
app.run(host='192.168.106.51')

Uploading images to a Flask server using FilePond (React component)

I have a locally hosted Flask server and a small React application. I'm trying to use FilePond as an easy solution for image uploading. FilePond takes care of sending each image to the server.
So the problem I'm obviously having is with the backend code. I've set up my server like this, per Flask's docs
UPLOAD_FOLDER='/images'
ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg', 'gif'])
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
#app.route('/', methods=['GET', 'POST'])
def upload_file():
print(request.files)
if request.method == "POST":
# check if the post request has the file part
if 'file' not in request.files:
print('No file part')
return make_response("No File Part", 400)
file = request.files["file"]
# if user does not select file, browser also submit an empty part
# without filename
if file.filename == '':
print('No selected file')
return make_response("No Selected File", 400)
if file and allowed_file(file.filename):
filename = secure_filename(file.filename) # filenames can be dangerous!
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return make_response("Success", 201)
However, when put a
print(request.files)
statement at the beginning of the upload_file() function, it seems that this is the request the server is receiving:
ImmutableMultiDict([('images', <FileStorage: 'imageName.jpg' ('image/jpeg')>)])
and I have no idea how to deal with that. I don't know if 'images' is where 'file' is supposed to be. I'm just not really sure where to go from here or what to do with the data being received. I don't know what is, I've never seen that <> syntax being used.
Can anyone help?
Here's a pastebin for the whole server-side code
Thank you!
Here's a proof of concept app that lets you upload multiple images using a form:
Note the enctype="multipart/form-data" attribute of the form, without this you cannot upload files.
Also note the multiple attribute of the file input. This allows client to select multiple files. You need to use request.files.getlist() to obtain the list of all uploaded files.
Once you generate the path to save the file, saving a werkzeug.FileStorage object is is just calling its .save(path_to_save) method.
from flask import Flask, request, render_template_string, redirect, abort
from werkzeug import secure_filename
from pathlib import Path
UPLOAD_DIR: Path = Path(__file__).parent / 'uploads'
UPLOAD_DIR.mkdir(parents=True, exist_ok=True)
app = Flask(__name__)
def is_valid_upload(upload) -> bool:
# some validation logic
return Path(upload.filename).suffix.lower() in ['.jpg', '.jpeg']
#app.route('/', methods=['GET', 'POST'])
def upload():
html = '''
<form action='/' method='POST' enctype="multipart/form-data">
<input type="file" name='images' multiple>
<button>Upload</button>
</form>
'''
if request.method == 'GET':
return html
uploaded_files = request.files.getlist('images')
if not uploaded_files or not uploaded_files[0].filename:
return redirect('/')
valid_uploads = list(filter(is_valid_upload, uploaded_files))
if not valid_uploads:
return 'invalid image(s)', 400
for upload in valid_uploads:
filename = secure_filename(upload.filename)
save_path = str(UPLOAD_DIR / filename)
upload.save(save_path)
return 'uploaded'
if __name__ == "__main__":
app.run()

Categories