@@ -329,8 +329,6 @@ def internal_server_error(e):
329
329
),
330
330
500 ,
331
331
)
332
- return redirect ("/" ), 500 , {"Refresh" : "1; url=/" }
333
-
334
332
335
333
def _get_slide (path , originalPath = None ):
336
334
path = os .path .abspath (os .path .join (app .basedir , path ))
@@ -414,6 +412,9 @@ def index():
414
412
@requires_auth
415
413
def base_static (path ):
416
414
completePath = os .path .abspath (os .path .join (app .basedir , path ))
415
+ if not completePath .startswith (app .basedir ):
416
+ # Directory traversal
417
+ abort (404 )
417
418
directory = os .path .dirname (completePath ) + "/web/"
418
419
filename = os .path .basename (completePath )
419
420
return send_from_directory (directory , filename )
@@ -432,6 +433,9 @@ def slide(filename):
432
433
if not path :
433
434
path = "./"
434
435
path = os .path .abspath (os .path .join (app .basedir , path , filename ))
436
+ if not path .startswith (app .basedir ):
437
+ # Directory traversal
438
+ abort (404 )
435
439
if not os .path .isfile (path ) and not os .path .isfile (path + ".dzi" ):
436
440
abort (404 )
437
441
# slide = _get_slide(path)
@@ -495,7 +499,10 @@ def tmapFile(filename):
495
499
496
500
# Create the absolute path to the JSON file
497
501
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
+
499
506
# Define error message feedback to user, None means no error message
500
507
errorMessage = None
501
508
warningMessage = None
@@ -624,40 +631,17 @@ def csvFile(completePath):
624
631
if os .path .isfile (completePath ):
625
632
# We can not gzip csv files with the PapaParse library
626
633
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
653
634
else :
654
635
abort (404 )
655
636
656
637
657
638
@app .route ("/<path:completePath>.<any(json, geojson, pbf):ext>" )
658
639
@requires_auth
659
640
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 )
661
645
directory = os .path .dirname (completePath )
662
646
filename = os .path .basename (completePath )
663
647
if os .path .isfile (completePath ):
@@ -670,12 +654,15 @@ def jsonFile(completePath, ext):
670
654
@requires_auth
671
655
def dzi (path ):
672
656
completePath = os .path .join (app .basedir , path )
657
+ if not completePath .startswith (app .basedir ):
658
+ # Directory traversal
659
+ abort (404 )
673
660
# Check if a .dzi file exists, else use OpenSlide:
674
661
if os .path .isfile (completePath + ".dzi" ):
675
662
directory = os .path .dirname (completePath )
676
663
filename = os .path .basename (completePath ) + ".dzi"
677
664
return send_from_directory (directory , filename )
678
- slide = _get_slide (path )
665
+ slide = _get_slide (completePath )
679
666
format = app .config ["DEEPZOOM_FORMAT" ]
680
667
resp = make_response (slide .get_dzi (format ))
681
668
resp .mimetype = "application/xml"
@@ -685,7 +672,11 @@ def dzi(path):
685
672
@app .route ("/<path:path>.dzi/info" )
686
673
@requires_auth
687
674
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 )
689
680
associated_images = []
690
681
for key , im in slide .associated_images .items ():
691
682
output = io .BytesIO ()
@@ -702,7 +693,10 @@ def dzi_asso(path):
702
693
703
694
@app .route ("/<path:path>_files/<int:level>/<int:col>_<int:row>.<format>" )
704
695
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 )
706
700
if os .path .isfile (f"{ completePath } _files/{ level } /{ col } _{ row } .{ format } " ):
707
701
directory = f"{ completePath } _files/{ level } /"
708
702
filename = f"{ col } _{ row } .{ format } "
@@ -810,6 +804,9 @@ def h5ad(filename, ext):
810
804
completePath = os .path .abspath (
811
805
os .path .join (app .basedir , path , filename ) + "." + ext
812
806
)
807
+ if not completePath .startswith (app .basedir ):
808
+ # Directory traversal
809
+ abort (404 )
813
810
# Check if a .h5ad file exists:
814
811
if not os .path .isfile (completePath ):
815
812
abort (404 )
@@ -837,7 +834,10 @@ def h5ad(filename, ext):
837
834
"/<path:path>.<any(h5ad, adata):ext>_files/csv/<string:type>/<string:filename>.csv"
838
835
)
839
836
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 )
841
841
filename = unquote (filename )
842
842
csvPath = f"{ completePath } _files/csv/{ type } /{ filename } .csv"
843
843
generate_csv = True
@@ -1050,7 +1050,10 @@ def get_tree():
1050
1050
@app .route ("/get_file_tree" )
1051
1051
def get_file_tree ():
1052
1052
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 )
1054
1057
return jsonify (get_file_tree_data (root_path ))
1055
1058
else :
1056
1059
return jsonify ([])
0 commit comments