bpo-41043: Escape literal part of the path for glob(). (GH-20994)

This commit is contained in:
Serhiy Storchaka 2020-06-20 11:10:31 +03:00 committed by GitHub
parent a041e116db
commit 9355868458
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 37 additions and 31 deletions

View File

@ -5,7 +5,7 @@ Implements the Distutils 'build_py' command."""
import os import os
import importlib.util import importlib.util
import sys import sys
from glob import glob import glob
from distutils.core import Command from distutils.core import Command
from distutils.errors import * from distutils.errors import *
@ -125,7 +125,7 @@ class build_py (Command):
files = [] files = []
for pattern in globs: for pattern in globs:
# Each pattern has to be converted to a platform-specific path # Each pattern has to be converted to a platform-specific path
filelist = glob(os.path.join(src_dir, convert_path(pattern))) filelist = glob.glob(os.path.join(glob.escape(src_dir), convert_path(pattern)))
# Files that match more than one pattern are only added once # Files that match more than one pattern are only added once
files.extend([fn for fn in filelist if fn not in files files.extend([fn for fn in filelist if fn not in files
and os.path.isfile(fn)]) and os.path.isfile(fn)])
@ -216,7 +216,7 @@ class build_py (Command):
def find_package_modules(self, package, package_dir): def find_package_modules(self, package, package_dir):
self.check_package(package, package_dir) self.check_package(package, package_dir)
module_files = glob(os.path.join(package_dir, "*.py")) module_files = glob.glob(os.path.join(glob.escape(package_dir), "*.py"))
modules = [] modules = []
setup_script = os.path.abspath(self.distribution.script_name) setup_script = os.path.abspath(self.distribution.script_name)

View File

@ -38,7 +38,7 @@ def listicons(icondir=ICONDIR):
"""Utility to display the available icons.""" """Utility to display the available icons."""
root = Tk() root = Tk()
import glob import glob
list = glob.glob(os.path.join(icondir, "*.gif")) list = glob.glob(os.path.join(glob.escape(icondir), "*.gif"))
list.sort() list.sort()
images = [] images = []
row = column = 0 row = column = 0

View File

@ -152,7 +152,7 @@ def testall(list, recursive, toplevel):
if recursive or toplevel: if recursive or toplevel:
print('recursing down:') print('recursing down:')
import glob import glob
names = glob.glob(os.path.join(filename, '*')) names = glob.glob(os.path.join(glob.escape(filename), '*'))
testall(names, recursive, 0) testall(names, recursive, 0)
else: else:
print('*** directory (use -r) ***') print('*** directory (use -r) ***')

View File

@ -473,7 +473,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
except Exception: except Exception:
ret = [] ret = []
# Then, try to complete file names as well. # Then, try to complete file names as well.
globs = glob.glob(text + '*') globs = glob.glob(glob.escape(text) + '*')
for fn in globs: for fn in globs:
if os.path.isdir(fn): if os.path.isdir(fn):
ret.append(fn + '/') ret.append(fn + '/')

View File

@ -241,7 +241,7 @@ def testall(list, recursive, toplevel):
if recursive or toplevel: if recursive or toplevel:
print('recursing down:') print('recursing down:')
import glob import glob
names = glob.glob(os.path.join(filename, '*')) names = glob.glob(os.path.join(glob.escape(filename), '*'))
testall(names, recursive, 0) testall(names, recursive, 0)
else: else:
print('*** directory (use -r) ***') print('*** directory (use -r) ***')

View File

@ -4260,7 +4260,7 @@ class _TestImportStar(unittest.TestCase):
def get_module_names(self): def get_module_names(self):
import glob import glob
folder = os.path.dirname(multiprocessing.__file__) folder = os.path.dirname(multiprocessing.__file__)
pattern = os.path.join(folder, '*.py') pattern = os.path.join(glob.escape(folder), '*.py')
files = glob.glob(pattern) files = glob.glob(pattern)
modules = [os.path.splitext(os.path.split(f)[1])[0] for f in files] modules = [os.path.splitext(os.path.split(f)[1])[0] for f in files]
modules = ['multiprocessing.' + m for m in modules] modules = ['multiprocessing.' + m for m in modules]

View File

@ -602,7 +602,7 @@ class Regrtest:
def cleanup(self): def cleanup(self):
import glob import glob
path = os.path.join(self.tmp_dir, 'test_python_*') path = os.path.join(glob.escape(self.tmp_dir), 'test_python_*')
print("Cleanup %s directory" % self.tmp_dir) print("Cleanup %s directory" % self.tmp_dir)
for name in glob.glob(path): for name in glob.glob(path):
if os.path.isdir(name): if os.path.isdir(name):

View File

@ -1345,7 +1345,7 @@ class PythonSymlink:
dll, dll,
os.path.join(dest_dir, os.path.basename(dll)) os.path.join(dest_dir, os.path.basename(dll))
)) ))
for runtime in glob.glob(os.path.join(src_dir, "vcruntime*.dll")): for runtime in glob.glob(os.path.join(glob.escape(src_dir), "vcruntime*.dll")):
self._also_link.append(( self._also_link.append((
runtime, runtime,
os.path.join(dest_dir, os.path.basename(runtime)) os.path.join(dest_dir, os.path.basename(runtime))

View File

@ -70,7 +70,7 @@ class BaseTest(unittest.TestCase):
# simply use the bigger test data for all tests. # simply use the bigger test data for all tests.
test_size = 0 test_size = 0
BIG_TEXT = bytearray(128*1024) BIG_TEXT = bytearray(128*1024)
for fname in glob.glob(os.path.join(os.path.dirname(__file__), '*.py')): for fname in glob.glob(os.path.join(glob.escape(os.path.dirname(__file__)), '*.py')):
with open(fname, 'rb') as fh: with open(fname, 'rb') as fh:
test_size += fh.readinto(memoryview(BIG_TEXT)[test_size:]) test_size += fh.readinto(memoryview(BIG_TEXT)[test_size:])
if test_size > 128*1024: if test_size > 128*1024:

View File

@ -11,7 +11,7 @@ import test.support
from test.support.script_helper import assert_python_failure from test.support.script_helper import assert_python_failure
CRASHER_DIR = os.path.join(os.path.dirname(__file__), "crashers") CRASHER_DIR = os.path.join(os.path.dirname(__file__), "crashers")
CRASHER_FILES = os.path.join(CRASHER_DIR, "*.py") CRASHER_FILES = os.path.join(glob.escape(CRASHER_DIR), "*.py")
infinite_loops = ["infinite_loop_re.py", "nasty_eq_vs_dict.py"] infinite_loops = ["infinite_loop_re.py", "nasty_eq_vs_dict.py"]

View File

@ -33,7 +33,7 @@ def dbm_iterator():
def delete_files(): def delete_files():
# we don't know the precise name the underlying database uses # we don't know the precise name the underlying database uses
# so we use glob to locate all names # so we use glob to locate all names
for f in glob.glob(_fname + "*"): for f in glob.glob(glob.escape(_fname) + "*"):
test.support.unlink(f) test.support.unlink(f)

View File

@ -486,7 +486,7 @@ class ImportTests(unittest.TestCase):
pyexe = os.path.join(tmp, os.path.basename(sys.executable)) pyexe = os.path.join(tmp, os.path.basename(sys.executable))
shutil.copy(sys.executable, pyexe) shutil.copy(sys.executable, pyexe)
shutil.copy(dllname, tmp) shutil.copy(dllname, tmp)
for f in glob.glob(os.path.join(sys.prefix, "vcruntime*.dll")): for f in glob.glob(os.path.join(glob.escape(sys.prefix), "vcruntime*.dll")):
shutil.copy(f, tmp) shutil.copy(f, tmp)
shutil.copy(pydname, tmp2) shutil.copy(pydname, tmp2)

View File

@ -979,7 +979,7 @@ class _TestMboxMMDF(_TestSingleFile):
super().tearDown() super().tearDown()
self._box.close() self._box.close()
self._delete_recursively(self._path) self._delete_recursively(self._path)
for lock_remnant in glob.glob(self._path + '.*'): for lock_remnant in glob.glob(glob.escape(self._path) + '.*'):
support.unlink(lock_remnant) support.unlink(lock_remnant)
def assertMailboxEmpty(self): def assertMailboxEmpty(self):
@ -1311,7 +1311,7 @@ class TestBabyl(_TestSingleFile, unittest.TestCase):
super().tearDown() super().tearDown()
self._box.close() self._box.close()
self._delete_recursively(self._path) self._delete_recursively(self._path)
for lock_remnant in glob.glob(self._path + '.*'): for lock_remnant in glob.glob(glob.escape(self._path) + '.*'):
support.unlink(lock_remnant) support.unlink(lock_remnant)
def test_labels(self): def test_labels(self):

View File

@ -556,7 +556,7 @@ class CheckActualTests(BaseTestCase):
args = ['-Wd', '-E', '-bb', '-m', 'test.regrtest', '--list-tests'] args = ['-Wd', '-E', '-bb', '-m', 'test.regrtest', '--list-tests']
output = self.run_python(args) output = self.run_python(args)
rough_number_of_tests_found = len(output.splitlines()) rough_number_of_tests_found = len(output.splitlines())
actual_testsuite_glob = os.path.join(os.path.dirname(__file__), actual_testsuite_glob = os.path.join(glob.escape(os.path.dirname(__file__)),
'test*.py') 'test*.py')
rough_counted_test_py_files = len(glob.glob(actual_testsuite_glob)) rough_counted_test_py_files = len(glob.glob(actual_testsuite_glob))
# We're not trying to duplicate test finding logic in here, # We're not trying to duplicate test finding logic in here,

View File

@ -543,7 +543,7 @@ class StartupImportTests(unittest.TestCase):
# found in sys.path (see site.addpackage()). Skip the test if at least # found in sys.path (see site.addpackage()). Skip the test if at least
# one .pth file is found. # one .pth file is found.
for path in isolated_paths: for path in isolated_paths:
pth_files = glob.glob(os.path.join(path, "*.pth")) pth_files = glob.glob(os.path.join(glob.escape(path), "*.pth"))
if pth_files: if pth_files:
self.skipTest(f"found {len(pth_files)} .pth files in: {path}") self.skipTest(f"found {len(pth_files)} .pth files in: {path}")

View File

@ -1605,7 +1605,7 @@ class TestRoundtrip(TestCase):
import glob, random import glob, random
fn = support.findfile("tokenize_tests.txt") fn = support.findfile("tokenize_tests.txt")
tempdir = os.path.dirname(fn) or os.curdir tempdir = os.path.dirname(fn) or os.curdir
testfiles = glob.glob(os.path.join(tempdir, "test*.py")) testfiles = glob.glob(os.path.join(glob.escape(tempdir), "test*.py"))
# Tokenize is broken on test_pep3131.py because regular expressions are # Tokenize is broken on test_pep3131.py because regular expressions are
# broken on the obscure unicode identifiers in it. *sigh* # broken on the obscure unicode identifiers in it. *sigh*

View File

@ -41,7 +41,7 @@ class TestUnicodeFiles(unittest.TestCase):
self._do_copyish(filename, filename) self._do_copyish(filename, filename)
# Filename should appear in glob output # Filename should appear in glob output
self.assertTrue( self.assertTrue(
os.path.abspath(filename)==os.path.abspath(glob.glob(filename)[0])) os.path.abspath(filename)==os.path.abspath(glob.glob(glob.escape(filename))[0]))
# basename should appear in listdir. # basename should appear in listdir.
path, base = os.path.split(os.path.abspath(filename)) path, base = os.path.split(os.path.abspath(filename))
file_list = os.listdir(path) file_list = os.listdir(path)

View File

@ -413,7 +413,7 @@ class Grail(BaseBrowser):
tempdir = os.path.join(tempfile.gettempdir(), tempdir = os.path.join(tempfile.gettempdir(),
".grail-unix") ".grail-unix")
user = pwd.getpwuid(os.getuid())[0] user = pwd.getpwuid(os.getuid())[0]
filename = os.path.join(tempdir, user + "-*") filename = os.path.join(glob.escape(tempdir), glob.escape(user) + "-*")
maybes = glob.glob(filename) maybes = glob.glob(filename)
if not maybes: if not maybes:
return None return None

View File

@ -0,0 +1,2 @@
Fixed the use of :func:`~glob.glob` in the stdlib: literal part of the path
is now always correctly escaped.

View File

@ -41,6 +41,8 @@ def walk_tree(root, *,
def glob_tree(root, *, def glob_tree(root, *,
suffix=None, suffix=None,
_glob=glob.iglob, _glob=glob.iglob,
_escape=glob.escape,
_join=os.path.join,
): ):
"""Yield each file in the tree under the given directory name. """Yield each file in the tree under the given directory name.
@ -51,9 +53,9 @@ def glob_tree(root, *,
if not isinstance(suffix, str): if not isinstance(suffix, str):
raise ValueError('suffix must be a string') raise ValueError('suffix must be a string')
for filename in _glob(f'{root}/*{suffix}'): for filename in _glob(_join(_escape(root), f'*{suffix}')):
yield filename yield filename
for filename in _glob(f'{root}/**/*{suffix}'): for filename in _glob(_join(_escape(root), f'**/*{suffix}')):
yield filename yield filename

View File

@ -37,7 +37,9 @@ IGNORED_VARS = {
def find_capi_vars(root): def find_capi_vars(root):
capi_vars = {} capi_vars = {}
for dirname in SOURCE_DIRS: for dirname in SOURCE_DIRS:
for filename in glob.glob(os.path.join(ROOT_DIR, dirname, '**/*.[hc]'), for filename in glob.glob(os.path.join(
glob.escape(os.path.join(ROOT_DIR, dirname)),
'**/*.[hc]'),
recursive=True): recursive=True):
with open(filename) as file: with open(filename) as file:
for name in _find_capi_vars(file): for name in _find_capi_vars(file):

View File

@ -7,7 +7,7 @@ import sys
import time import time
import traceback import traceback
import tokenize import tokenize
from glob import glob from glob import glob, escape
from pathlib import PurePath from pathlib import PurePath
from typing import List, Optional, Any, Tuple from typing import List, Optional, Any, Tuple
@ -109,7 +109,7 @@ def parse_directory(directory: str, verbose: bool, excluded_files: List[str], sh
files = [] files = []
total_seconds = 0 total_seconds = 0
for file in sorted(glob(f"{directory}/**/*.py", recursive=True)): for file in sorted(glob(os.path.join(escape(directory), f"**/*.py"), recursive=True)):
# Only attempt to parse Python files and files that are not excluded # Only attempt to parse Python files and files that are not excluded
if any(PurePath(file).match(pattern) for pattern in excluded_files): if any(PurePath(file).match(pattern) for pattern in excluded_files):
continue continue

View File

@ -39,7 +39,7 @@ if __name__ == "__main__":
f = sys.stdout if use_stdout else open(outfile, "w") f = sys.stdout if use_stdout else open(outfile, "w")
# mnemonic -> (library code, error prefix, header file) # mnemonic -> (library code, error prefix, header file)
error_libraries = {} error_libraries = {}
for error_header in glob.glob(os.path.join(openssl_inc, 'include/openssl/*err.h')): for error_header in glob.glob(os.path.join(glob.escape(openssl_inc), 'include/openssl/*err.h')):
base = os.path.basename(error_header) base = os.path.basename(error_header)
if base in ('buffererr.h', 'objectserr.h', 'storeerr.h'): if base in ('buffererr.h', 'objectserr.h', 'storeerr.h'):
# Deprecated in 3.0. # Deprecated in 3.0.

View File

@ -8,7 +8,7 @@ import os
import re import re
import sys import sys
import sysconfig import sysconfig
from glob import glob from glob import glob, escape
try: try:
@ -401,7 +401,7 @@ class PyBuildExt(build_ext):
# Python header files # Python header files
headers = [sysconfig.get_config_h_filename()] headers = [sysconfig.get_config_h_filename()]
headers += glob(os.path.join(sysconfig.get_path('include'), "*.h")) headers += glob(os.path.join(escape(sysconfig.get_path('include')), "*.h"))
for ext in self.extensions: for ext in self.extensions:
ext.sources = [ find_module_file(filename, moddirlist) ext.sources = [ find_module_file(filename, moddirlist)
@ -2431,7 +2431,7 @@ class PyBuildExt(build_ext):
if "blake2" in configured: if "blake2" in configured:
blake2_deps = glob( blake2_deps = glob(
os.path.join(self.srcdir, 'Modules/_blake2/impl/*') os.path.join(escape(self.srcdir), 'Modules/_blake2/impl/*')
) )
blake2_deps.append('hashlib.h') blake2_deps.append('hashlib.h')
self.add(Extension( self.add(Extension(
@ -2446,7 +2446,7 @@ class PyBuildExt(build_ext):
if "sha3" in configured: if "sha3" in configured:
sha3_deps = glob( sha3_deps = glob(
os.path.join(self.srcdir, 'Modules/_sha3/kcp/*') os.path.join(escape(self.srcdir), 'Modules/_sha3/kcp/*')
) )
sha3_deps.append('hashlib.h') sha3_deps.append('hashlib.h')
self.add(Extension( self.add(Extension(