Skip to content

Commit e409e80

Browse files
committed
Extract _sanitize method for sanitizing the filename.
1 parent 250a6d1 commit e409e80

File tree

1 file changed

+37
-21
lines changed

1 file changed

+37
-21
lines changed

setuptools/package_index.py

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -808,40 +808,56 @@ def open_url(self, url, warning=None): # noqa: C901 # is too complex (12)
808808
raise DistutilsError(f"Download error for {url}: {v}") from v
809809

810810
@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):
812841
"""
813842
>>> import pathlib
814843
>>> du = PackageIndex._resolve_download_filename
815844
>>> root = getfixture('tmp_path')
816845
>>> url = 'https://files.pythonhosted.org/packages/a9/5a/0db.../setuptools-78.1.0.tar.gz'
817846
>>> str(pathlib.Path(du(url, root)).relative_to(root))
818847
'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...
827848
"""
828849
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+
)
834856

835857
if name.endswith('.egg.zip'):
836858
name = name[:-4] # strip the extra .zip before download
837859

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)
845861

846862
def _download_url(self, url, tmpdir):
847863
"""

0 commit comments

Comments
 (0)