@@ -351,17 +351,22 @@ def serve_demo_file(demo_id, filename):
351351@app .route ('/api/download_demo/<demo_id>' )
352352def download_demo_zip (demo_id ):
353353 demo_dir = os .path .join (app .config ['DEMOS_DIR' ], demo_id )
354- if not os .path .isdir (demo_dir ):
354+ # Normalize and validate that demo_dir is within DEMOS_DIR
355+ normalized_demo_dir = os .path .normpath (os .path .abspath (demo_dir ))
356+ demos_base = os .path .abspath (app .config ['DEMOS_DIR' ])
357+ if not normalized_demo_dir .startswith (demos_base + os .sep ):
358+ return "Invalid demo ID" , 400
359+ if not os .path .isdir (normalized_demo_dir ):
355360 return "Demo not found" , 404
356361
357362 zip_filename = f"demo_{ demo_id } .zip"
358363 zip_filepath = os .path .join (app .config ['TEMP_DIR' ], zip_filename )
359364
360365 with zipfile .ZipFile (zip_filepath , 'w' , zipfile .ZIP_DEFLATED ) as zipf :
361- for root , _ , files in os .walk (demo_dir ):
366+ for root , _ , files in os .walk (normalized_demo_dir ):
362367 for file in files :
363368 full_path = os .path .join (root , file )
364- arcname = os .path .relpath (full_path , demo_dir )
369+ arcname = os .path .relpath (full_path , normalized_demo_dir )
365370 zipf .write (full_path , arcname )
366371
367372 return send_from_directory (app .config ['TEMP_DIR' ], zip_filename , as_attachment = True )
0 commit comments