@@ -1132,11 +1132,17 @@ def hf_hub_download(
11321132
11331133 # cross platform transcription of filename, to be used as a local file path.
11341134 relative_filename = os .path .join (* filename .split ("/" ))
1135+ if os .name == "nt" :
1136+ if relative_filename .startswith ("..\\ " ) or "\\ ..\\ " in relative_filename :
1137+ raise ValueError (
1138+ f"Invalid filename: cannot handle filename '{ relative_filename } ' on Windows. Please ask the repository"
1139+ " owner to rename this file."
1140+ )
11351141
11361142 # if user provides a commit_hash and they already have the file on disk,
11371143 # shortcut everything.
11381144 if REGEX_COMMIT_HASH .match (revision ):
1139- pointer_path = os . path . join (storage_folder , "snapshots" , revision , relative_filename )
1145+ pointer_path = _get_pointer_path (storage_folder , revision , relative_filename )
11401146 if os .path .exists (pointer_path ):
11411147 if local_dir is not None :
11421148 return _to_local_dir (pointer_path , local_dir , relative_filename , use_symlinks = local_dir_use_symlinks )
@@ -1231,7 +1237,7 @@ def hf_hub_download(
12311237
12321238 # Return pointer file if exists
12331239 if commit_hash is not None :
1234- pointer_path = os . path . join (storage_folder , "snapshots" , commit_hash , relative_filename )
1240+ pointer_path = _get_pointer_path (storage_folder , commit_hash , relative_filename )
12351241 if os .path .exists (pointer_path ):
12361242 if local_dir is not None :
12371243 return _to_local_dir (
@@ -1260,7 +1266,7 @@ def hf_hub_download(
12601266 assert etag is not None , "etag must have been retrieved from server"
12611267 assert commit_hash is not None , "commit_hash must have been retrieved from server"
12621268 blob_path = os .path .join (storage_folder , "blobs" , etag )
1263- pointer_path = os . path . join (storage_folder , "snapshots" , commit_hash , relative_filename )
1269+ pointer_path = _get_pointer_path (storage_folder , commit_hash , relative_filename )
12641270
12651271 os .makedirs (os .path .dirname (blob_path ), exist_ok = True )
12661272 os .makedirs (os .path .dirname (pointer_path ), exist_ok = True )
@@ -1549,14 +1555,34 @@ def _chmod_and_replace(src: str, dst: str) -> None:
15491555 os .replace (src , dst )
15501556
15511557
1558+ def _get_pointer_path (storage_folder : str , revision : str , relative_filename : str ) -> str :
1559+ # Using `os.path.abspath` instead of `Path.resolve()` to avoid resolving symlinks
1560+ snapshot_path = os .path .join (storage_folder , "snapshots" )
1561+ pointer_path = os .path .join (snapshot_path , revision , relative_filename )
1562+ if Path (os .path .abspath (snapshot_path )) not in Path (os .path .abspath (pointer_path )).parents :
1563+ raise ValueError (
1564+ "Invalid pointer path: cannot create pointer path in snapshot folder if"
1565+ f" `storage_folder='{ storage_folder } '`, `revision='{ revision } '` and"
1566+ f" `relative_filename='{ relative_filename } '`."
1567+ )
1568+ return pointer_path
1569+
1570+
15521571def _to_local_dir (
15531572 path : str , local_dir : str , relative_filename : str , use_symlinks : Union [bool , Literal ["auto" ]]
15541573) -> str :
15551574 """Place a file in a local dir (different than cache_dir).
15561575
15571576 Either symlink to blob file in cache or duplicate file depending on `use_symlinks` and file size.
15581577 """
1578+ # Using `os.path.abspath` instead of `Path.resolve()` to avoid resolving symlinks
15591579 local_dir_filepath = os .path .join (local_dir , relative_filename )
1580+ if Path (os .path .abspath (local_dir )) not in Path (os .path .abspath (local_dir_filepath )).parents :
1581+ raise ValueError (
1582+ f"Cannot copy file '{ relative_filename } ' to local dir '{ local_dir } ': file would not be in the local"
1583+ " directory."
1584+ )
1585+
15601586 os .makedirs (os .path .dirname (local_dir_filepath ), exist_ok = True )
15611587 real_blob_path = os .path .realpath (path )
15621588
0 commit comments