44import re
55import struct
66import ipaddress
7+ from pathlib import Path
8+
9+ from nxc .helpers .path import sanitize_filename
710from Cryptodome .Hash import MD4
811from textwrap import dedent
912
@@ -2005,7 +2008,10 @@ def download_folder(self, folder, dest, recursive=False, silent=False, base_dir=
20052008 self .logger .display (f"Created empty directory '{ local_folder_path } '" )
20062009
20072010 for item in filtered_items :
2008- item_name = item .get_longname ()
2011+ item_name = sanitize_filename (item .get_longname ())
2012+ if not item_name :
2013+ self .logger .fail (f"Path traversal detected in '{ item .get_longname ()} ', skipping" )
2014+ continue
20092015 dir_path = ntpath .normpath (ntpath .join (normalized_folder , item_name ))
20102016 self .logger .debug (f"Parsing item: { item_name } , { dir_path } " )
20112017
@@ -2015,6 +2021,11 @@ def download_folder(self, folder, dest, recursive=False, silent=False, base_dir=
20152021 elif not item .is_directory ():
20162022 remote_file_path = ntpath .join (folder , item_name )
20172023 local_file_path = os .path .join (local_folder_path , item_name )
2024+ # Defense-in-depth: verify path stays under destination
2025+ resolved = Path (local_file_path ).resolve ()
2026+ if not str (resolved ).startswith (str (Path (dest ).resolve ()) + os .sep ):
2027+ self .logger .fail (f"Path traversal detected in '{ item_name } ', skipping" )
2028+ continue
20182029 self .logger .debug (f"{ dest = } { remote_file_path = } { relative_path = } { local_folder_path = } { local_file_path = } " )
20192030
20202031 try :
0 commit comments