@@ -808,40 +808,56 @@ def open_url(self, url, warning=None): # noqa: C901 # is too complex (12)
808
808
raise DistutilsError (f"Download error for { url } : { v } " ) from v
809
809
810
810
@staticmethod
811
- def _resolve_download_filename (url , tmpdir ):
811
+ def _sanitize (name ):
812
+ r"""
813
+ Replace unsafe path directives with underscores.
814
+
815
+ >>> san = PackageIndex._sanitize
816
+ >>> san('/home/user/.ssh/authorized_keys')
817
+ '_home_user_.ssh_authorized_keys'
818
+ >>> san('..\\foo\\bing')
819
+ '__foo_bing'
820
+ >>> san('D:bar')
821
+ 'D_bar'
822
+ >>> san('C:\\bar')
823
+ 'C__bar'
824
+ >>> san('foo..bar')
825
+ 'foo..bar'
826
+ >>> san('D:../foo')
827
+ 'D___foo'
828
+ """
829
+ pattern = '|' .join ((
830
+ # drive letters
831
+ r':' ,
832
+ # path separators
833
+ r'[/\\]' ,
834
+ # parent dirs
835
+ r'(?:(?<=([/\\]|:))\.\.(?=[/\\]|$))|(?:^\.\.(?=[/\\]|$))' ,
836
+ ))
837
+ return re .sub (pattern , r'_' , name )
838
+
839
+ @classmethod
840
+ def _resolve_download_filename (cls , url , tmpdir ):
812
841
"""
813
842
>>> import pathlib
814
843
>>> du = PackageIndex._resolve_download_filename
815
844
>>> root = getfixture('tmp_path')
816
845
>>> url = 'https://files.pythonhosted.org/packages/a9/5a/0db.../setuptools-78.1.0.tar.gz'
817
846
>>> str(pathlib.Path(du(url, root)).relative_to(root))
818
847
'setuptools-78.1.0.tar.gz'
819
-
820
- Ensures the target is always in tmpdir.
821
-
822
- >>> url = 'https://anyhost/%2fhome%2fuser%2f.ssh%2fauthorized_keys'
823
- >>> du(url, root)
824
- Traceback (most recent call last):
825
- ...
826
- ValueError: Invalid filename...
827
848
"""
828
849
name , _fragment = egg_info_for_url (url )
829
- if name :
830
- while '..' in name :
831
- name = name .replace ('..' , '.' ).replace ('\\ ' , '_' )
832
- else :
833
- name = "__downloaded__" # default if URL has no path contents
850
+ name = cls ._sanitize (
851
+ name
852
+ or
853
+ # default if URL has no path contents
854
+ '__downloaded__'
855
+ )
834
856
835
857
if name .endswith ('.egg.zip' ):
836
858
name = name [:- 4 ] # strip the extra .zip before download
837
859
838
- filename = os .path .join (tmpdir , name )
839
-
840
- # ensure path resolves within the tmpdir
841
- if not filename .startswith (str (tmpdir )):
842
- raise ValueError (f"Invalid filename { filename } " )
843
-
844
- return filename
860
+ return os .path .join (tmpdir , name )
845
861
846
862
def _download_url (self , url , tmpdir ):
847
863
"""
0 commit comments