Skip to content

Commit 79c7462

Browse files
committed
Fix Uncontrolled data used in path expression
1 parent 562855f commit 79c7462

File tree

1 file changed

+38
-35
lines changed

1 file changed

+38
-35
lines changed

tissuumaps/views.py

+38-35
Original file line numberDiff line numberDiff line change
@@ -329,8 +329,6 @@ def internal_server_error(e):
329329
),
330330
500,
331331
)
332-
return redirect("/"), 500, {"Refresh": "1; url=/"}
333-
334332

335333
def _get_slide(path, originalPath=None):
336334
path = os.path.abspath(os.path.join(app.basedir, path))
@@ -414,6 +412,9 @@ def index():
414412
@requires_auth
415413
def base_static(path):
416414
completePath = os.path.abspath(os.path.join(app.basedir, path))
415+
if not completePath.startswith(app.basedir):
416+
# Directory traversal
417+
abort(404)
417418
directory = os.path.dirname(completePath) + "/web/"
418419
filename = os.path.basename(completePath)
419420
return send_from_directory(directory, filename)
@@ -432,6 +433,9 @@ def slide(filename):
432433
if not path:
433434
path = "./"
434435
path = os.path.abspath(os.path.join(app.basedir, path, filename))
436+
if not path.startswith(app.basedir):
437+
# Directory traversal
438+
abort(404)
435439
if not os.path.isfile(path) and not os.path.isfile(path + ".dzi"):
436440
abort(404)
437441
# slide = _get_slide(path)
@@ -495,7 +499,10 @@ def tmapFile(filename):
495499

496500
# Create the absolute path to the JSON file
497501
json_filename = os.path.abspath(os.path.join(app.basedir, path, filename) + ".tmap")
498-
502+
if not json_filename.startswith(app.basedir):
503+
# Directory traversal
504+
abort(404)
505+
499506
# Define error message feedback to user, None means no error message
500507
errorMessage = None
501508
warningMessage = None
@@ -624,40 +631,17 @@ def csvFile(completePath):
624631
if os.path.isfile(completePath):
625632
# We can not gzip csv files with the PapaParse library
626633
return send_from_directory(directory, filename)
627-
628-
# We keep the old gzip code anyway:
629-
# gz files have to be names cgz for some browser
630-
if os.path.isfile(completePath + ".gz"):
631-
os.rename(completePath + ".gz", completePath + ".cgz")
632-
633-
generate_cgz = False
634-
if not os.path.isfile(completePath + ".cgz"):
635-
generate_cgz = True
636-
elif os.path.getmtime(completePath) > os.path.getmtime(completePath + ".cgz"):
637-
# In this case, the csv file has been recently modified and the cgz file is
638-
# stale, so it must be regenerated.
639-
generate_cgz = True
640-
if generate_cgz:
641-
with open(completePath, "rb") as f_in, gzip.open(
642-
completePath + ".cgz", "wb", compresslevel=9
643-
) as f_out:
644-
f_out.writelines(f_in)
645-
646-
response = make_response(send_from_directory(directory, filename + ".cgz"))
647-
response.headers["Content-Encoding"] = "gzip"
648-
response.headers["Vary"] = "Accept-Encoding"
649-
response.headers["Transfer-Encoding"] = "gzip"
650-
response.headers["Content-Length"] = os.path.getsize(completePath + ".cgz")
651-
response.headers["Content-Type"] = "text/csv; charset=UTF-8"
652-
return response
653634
else:
654635
abort(404)
655636

656637

657638
@app.route("/<path:completePath>.<any(json, geojson, pbf):ext>")
658639
@requires_auth
659640
def jsonFile(completePath, ext):
660-
completePath = os.path.join(app.basedir, completePath + "." + ext)
641+
completePath = os.path.abspath(os.path.join(app.basedir, completePath + "." + ext))
642+
if not completePath.startswith(app.basedir):
643+
# Directory traversal
644+
abort(404)
661645
directory = os.path.dirname(completePath)
662646
filename = os.path.basename(completePath)
663647
if os.path.isfile(completePath):
@@ -670,12 +654,15 @@ def jsonFile(completePath, ext):
670654
@requires_auth
671655
def dzi(path):
672656
completePath = os.path.join(app.basedir, path)
657+
if not completePath.startswith(app.basedir):
658+
# Directory traversal
659+
abort(404)
673660
# Check if a .dzi file exists, else use OpenSlide:
674661
if os.path.isfile(completePath + ".dzi"):
675662
directory = os.path.dirname(completePath)
676663
filename = os.path.basename(completePath) + ".dzi"
677664
return send_from_directory(directory, filename)
678-
slide = _get_slide(path)
665+
slide = _get_slide(completePath)
679666
format = app.config["DEEPZOOM_FORMAT"]
680667
resp = make_response(slide.get_dzi(format))
681668
resp.mimetype = "application/xml"
@@ -685,7 +672,11 @@ def dzi(path):
685672
@app.route("/<path:path>.dzi/info")
686673
@requires_auth
687674
def dzi_asso(path):
688-
slide = _get_slide(path)
675+
completePath = os.path.join(app.basedir, path)
676+
if not completePath.startswith(app.basedir):
677+
# Directory traversal
678+
abort(404)
679+
slide = _get_slide(completePath)
689680
associated_images = []
690681
for key, im in slide.associated_images.items():
691682
output = io.BytesIO()
@@ -702,7 +693,10 @@ def dzi_asso(path):
702693

703694
@app.route("/<path:path>_files/<int:level>/<int:col>_<int:row>.<format>")
704695
def tile(path, level, col, row, format):
705-
completePath = os.path.join(app.basedir, path)
696+
completePath = os.path.abspath(os.path.join(app.basedir, path))
697+
if not completePath.startswith(app.basedir):
698+
# Directory traversal
699+
abort(404)
706700
if os.path.isfile(f"{completePath}_files/{level}/{col}_{row}.{format}"):
707701
directory = f"{completePath}_files/{level}/"
708702
filename = f"{col}_{row}.{format}"
@@ -810,6 +804,9 @@ def h5ad(filename, ext):
810804
completePath = os.path.abspath(
811805
os.path.join(app.basedir, path, filename) + "." + ext
812806
)
807+
if not completePath.startswith(app.basedir):
808+
# Directory traversal
809+
abort(404)
813810
# Check if a .h5ad file exists:
814811
if not os.path.isfile(completePath):
815812
abort(404)
@@ -837,7 +834,10 @@ def h5ad(filename, ext):
837834
"/<path:path>.<any(h5ad, adata):ext>_files/csv/<string:type>/<string:filename>.csv"
838835
)
839836
def h5ad_csv(path, type, filename, ext):
840-
completePath = os.path.join(app.basedir, path + "." + ext)
837+
completePath = os.path.abspath(os.path.join(app.basedir, path + "." + ext))
838+
if not completePath.startswith(app.basedir):
839+
# Directory traversal
840+
abort(404)
841841
filename = unquote(filename)
842842
csvPath = f"{completePath}_files/csv/{type}/{filename}.csv"
843843
generate_csv = True
@@ -1050,7 +1050,10 @@ def get_tree():
10501050
@app.route("/get_file_tree")
10511051
def get_file_tree():
10521052
if not app.config["READ_ONLY"]:
1053-
root_path = app.config["SLIDE_DIR"] + "/" + request.args.get("root", "./")
1053+
root_path = os.path.abspath(app.basedir + "/" + request.args.get("root", "./"))
1054+
if not root_path.startswith(app.basedir):
1055+
# Directory traversal
1056+
abort(404)
10541057
return jsonify(get_file_tree_data(root_path))
10551058
else:
10561059
return jsonify([])

0 commit comments

Comments
 (0)