From b753369765d138cb1e78aabc42820a238b559fa6 Mon Sep 17 00:00:00 2001 From: Min RK Date: Mon, 29 Jun 2015 11:53:53 -0700 Subject: [PATCH] disable executing code in .pth files --- Lib/site.py | 12 ++++++++---- Lib/test/test_site.py | 32 +++++++++++++++++++++----------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/Lib/site.py b/Lib/site.py index b7d0331..a11d129 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -31,7 +31,7 @@ A path configuration file is a file whose name has the form to be added to sys.path. Non-existing directories (or non-directories) are never added to sys.path; no directory is added to sys.path more than once. Blank lines and lines beginning with -'#' are skipped. Lines starting with 'import' are executed. +'#' are skipped. For example, suppose sys.prefix and sys.exec_prefix are set to /usr/local and there is a directory /usr/local/lib/python2.5/site-packages @@ -72,6 +72,7 @@ import sys import os import builtins import _sitebuiltins +import warnings # Prefixes for site-packages; add additional prefixes like /usr/local here PREFIXES = [sys.prefix, sys.exec_prefix] @@ -145,8 +146,8 @@ def _init_pathinfo(): def addpackage(sitedir, name, known_paths): """Process a .pth file within the site-packages directory: - For each line in the file, either combine it with sitedir to a path - and add that to known_paths, or execute it if it starts with 'import '. + For each line in the file, combine it with sitedir to a path + and add that to known_paths. """ if known_paths is None: known_paths = _init_pathinfo() @@ -164,7 +165,10 @@ def addpackage(sitedir, name, known_paths): continue try: if line.startswith(("import ", "import\t")): - exec(line) + warnings.warn( + "Executing code in .pth files is no longer supported." + " Ignoring: %r in %r:%i" % (line, fullname, n), + ImportWarning) continue line = line.rstrip() dir, dircase = makepath(sitedir, line) diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index 6615080..c8c10af 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -77,7 +77,7 @@ class HelperFunctionsTests(unittest.TestCase): def pth_file_tests(self, pth_file): """Contain common code for testing results of reading a .pth file""" - self.assertIn(pth_file.imported, sys.modules, + self.assertNotIn(pth_file.imported, sys.modules, "%s not in sys.modules" % pth_file.imported) self.assertIn(site.makepath(pth_file.good_dir_path)[0], sys.path) self.assertFalse(os.path.exists(pth_file.bad_dir_path)) @@ -108,32 +108,41 @@ class HelperFunctionsTests(unittest.TestCase): pth_file.close() return pth_dir, pth_basename + def test_addpackage_import_warn(self): + pth_dir, pth_fn = self.make_pth("foo", imported='time') + with captured_stderr() as err_out: + site.addpackage(pth_dir, pth_fn, set()) + self.assertRegex(err_out.getvalue(), "Ignoring") + self.assertRegex(err_out.getvalue(), + re.escape(os.path.join(pth_dir, pth_fn))) + self.assertNotIn('time', sys.modules) + def test_addpackage_import_bad_syntax(self): # Issue 10642 pth_dir, pth_fn = self.make_pth("import bad)syntax\n") with captured_stderr() as err_out: site.addpackage(pth_dir, pth_fn, set()) - self.assertRegex(err_out.getvalue(), "line 1") + self.assertRegex(err_out.getvalue(), "Ignoring") self.assertRegex(err_out.getvalue(), re.escape(os.path.join(pth_dir, pth_fn))) # XXX: the previous two should be independent checks so that the # order doesn't matter. The next three could be a single check # but my regex foo isn't good enough to write it. - self.assertRegex(err_out.getvalue(), 'Traceback') - self.assertRegex(err_out.getvalue(), r'import bad\)syntax') - self.assertRegex(err_out.getvalue(), 'SyntaxError') + # Verify that the import wasn't attempted: + self.assertNotRegex(err_out.getvalue(), 'Traceback') + self.assertNotRegex(err_out.getvalue(), 'SyntaxError') - def test_addpackage_import_bad_exec(self): + def test_addpackage_import_warn(self): # Issue 10642 pth_dir, pth_fn = self.make_pth("randompath\nimport nosuchmodule\n") with captured_stderr() as err_out: site.addpackage(pth_dir, pth_fn, set()) - self.assertRegex(err_out.getvalue(), "line 2") + self.assertRegex(err_out.getvalue(), "Ignoring") self.assertRegex(err_out.getvalue(), re.escape(os.path.join(pth_dir, pth_fn))) # XXX: ditto previous XXX comment. - self.assertRegex(err_out.getvalue(), 'Traceback') - self.assertRegex(err_out.getvalue(), 'ImportError') + self.assertNotRegex(err_out.getvalue(), 'Traceback') + self.assertNotRegex(err_out.getvalue(), 'ImportError') @unittest.skipIf(sys.platform == "win32", "Windows does not raise an " "error for file paths containing null characters") @@ -257,7 +266,7 @@ class HelperFunctionsTests(unittest.TestCase): class PthFile(object): """Helper class for handling testing of .pth files""" - def __init__(self, filename_base=TESTFN, imported="time", + def __init__(self, filename_base=TESTFN, imported="", good_dirname="__testdir__", bad_dirname="__bad"): """Initialize instance variables""" self.filename = filename_base + ".pth" @@ -284,7 +293,8 @@ class PthFile(object): try: print("#import @bad module name", file=FILE) print("\n", file=FILE) - print("import %s" % self.imported, file=FILE) + if self.imported: + print("import %s" % self.imported, file=FILE) print(self.good_dirname, file=FILE) print(self.bad_dirname, file=FILE) finally: -- 2.4.5