Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit e409e80

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

File tree

1 file changed

+37
-21
lines changed
Filter options

1 file changed

+37
-21
lines changed

‎setuptools/package_index.py

Copy file name to clipboardExpand all lines: setuptools/package_index.py
+37-21Lines 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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.