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
@@ -2012,7 +2015,10 @@ def download_folder(self, folder, dest, recursive=False, silent=False, base_dir=
20122015 self .logger .display (f"Created empty directory '{ local_folder_path } '" )
20132016
20142017 for item in filtered_items :
2015- item_name = item .get_longname ()
2018+ item_name = sanitize_filename (item .get_longname ())
2019+ if not item_name :
2020+ self .logger .fail (f"Path traversal detected in '{ item .get_longname ()} ', skipping" )
2021+ continue
20162022 dir_path = ntpath .normpath (ntpath .join (normalized_folder , item_name ))
20172023 self .logger .debug (f"Parsing item: { item_name } , { dir_path } " )
20182024
@@ -2022,6 +2028,11 @@ def download_folder(self, folder, dest, recursive=False, silent=False, base_dir=
20222028 elif not item .is_directory ():
20232029 remote_file_path = ntpath .join (folder , item_name )
20242030 local_file_path = os .path .join (local_folder_path , item_name )
2031+ # Defense-in-depth: verify path stays under destination
2032+ resolved = Path (local_file_path ).resolve ()
2033+ if not str (resolved ).startswith (str (Path (dest ).resolve ()) + os .sep ):
2034+ self .logger .fail (f"Path traversal detected in '{ item_name } ', skipping" )
2035+ continue
20252036 self .logger .debug (f"{ dest = } { remote_file_path = } { relative_path = } { local_folder_path = } { local_file_path = } " )
20262037
20272038 try :
0 commit comments