From ada8b6d1b1b02ae7c38f161c2a0ad866559fe18b Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 6 May 2022 01:34:11 +0200 Subject: gh-57684: Add -P cmdline option and PYTHONSAFEPATH env var (#31542) Add the -P command line option and the PYTHONSAFEPATH environment variable to not prepend a potentially unsafe path to sys.path. * Add sys.flags.safe_path flag. * Add PyConfig.safe_path member. * Programs/_bootstrap_python.c uses config.safe_path=0. * Update subprocess._optim_args_from_interpreter_flags() to handle the -P command line option. * Modules/getpath.py sets safe_path to 1 if a "._pth" file is present. --- Doc/c-api/init_config.rst | 46 +++++++++++++++++----- Doc/library/sys.rst | 24 +++++++---- Doc/using/cmdline.rst | 31 ++++++++++++++- Doc/whatsnew/3.11.rst | 10 +++++ Include/cpython/initconfig.h | 1 + Lib/subprocess.py | 6 ++- Lib/test/test_cmd_line.py | 10 +++-- Lib/test/test_embed.py | 13 +++++- Lib/test/test_support.py | 4 +- Lib/test/test_sys.py | 4 +- .../2022-05-04-14-32-24.gh-issue-57684.HrlDrM.rst | 3 ++ Misc/python.man | 14 ++++++- Modules/getpath.py | 1 + Modules/main.c | 2 +- Programs/_bootstrap_python.c | 1 + Programs/_testembed.c | 10 ++++- Python/dynload_win.c | 1 - Python/getopt.c | 2 +- Python/initconfig.c | 22 +++++++++++ Python/sysmodule.c | 4 +- 20 files changed, 174 insertions(+), 35 deletions(-) create mode 100644 Misc/NEWS.d/next/Security/2022-05-04-14-32-24.gh-issue-57684.HrlDrM.rst diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst index bab5313..728df21 100644 --- a/Doc/c-api/init_config.rst +++ b/Doc/c-api/init_config.rst @@ -543,6 +543,25 @@ PyConfig See also the :c:member:`~PyConfig.orig_argv` member. + .. c:member:: int safe_path + + If equals to zero, ``Py_RunMain()`` prepends a potentially unsafe path to + :data:`sys.path` at startup: + + * If :c:member:`argv[0] ` is equal to ``L"-m"`` + (``python -m module``), prepend the current working directory. + * If running a script (``python script.py``), prepend the script's + directory. If it's a symbolic link, resolve symbolic links. + * Otherwise (``python -c code`` and ``python``), prepend an empty string, + which means the current working directory. + + Set to 1 by the :option:`-P` command line option and the + :envvar:`PYTHONSAFEPATH` environment variable. + + Default: ``0`` in Python config, ``1`` in isolated config. + + .. versionadded:: 3.11 + .. c:member:: wchar_t* base_exec_prefix :data:`sys.base_exec_prefix`. @@ -809,13 +828,14 @@ PyConfig If greater than 0, enable isolated mode: - * :data:`sys.path` contains neither the script's directory (computed from - ``argv[0]`` or the current directory) nor the user's site-packages - directory. + * Set :c:member:`~PyConfig.safe_path` to 1: + don't prepend a potentially unsafe path to :data:`sys.path` at Python + startup. + * Set :c:member:`~PyConfig.use_environment` to 0. + * Set :c:member:`~PyConfig.user_site_directory` to 0: don't add the user + site directory to :data:`sys.path`. * Python REPL doesn't import :mod:`readline` nor enable default readline configuration on interactive prompts. - * Set :c:member:`~PyConfig.use_environment` and - :c:member:`~PyConfig.user_site_directory` to 0. Default: ``0`` in Python mode, ``1`` in isolated mode. @@ -1029,12 +1049,13 @@ PyConfig .. c:member:: wchar_t* run_filename Filename passed on the command line: trailing command line argument - without :option:`-c` or :option:`-m`. + without :option:`-c` or :option:`-m`. It is used by the + :c:func:`Py_RunMain` function. For example, it is set to ``script.py`` by the ``python3 script.py arg`` - command. + command line. - Used by :c:func:`Py_RunMain`. + See also the :c:member:`PyConfig.skip_source_first_line` option. Default: ``NULL``. @@ -1419,9 +1440,16 @@ site-package directory to :data:`sys.path`. The following configuration files are used by the path configuration: * ``pyvenv.cfg`` -* ``python._pth`` (Windows only) +* ``._pth`` file (ex: ``python._pth``) * ``pybuilddir.txt`` (Unix only) +If a ``._pth`` file is present: + +* Set :c:member:`~PyConfig.isolated` to 1. +* Set :c:member:`~PyConfig.use_environment` to 0. +* Set :c:member:`~PyConfig.site_import` to 0. +* Set :c:member:`~PyConfig.safe_path` to 1. + The ``__PYVENV_LAUNCHER__`` environment variable is used to set :c:member:`PyConfig.base_executable` diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index f433448..5f3b9b5 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -520,6 +520,7 @@ always available. :const:`hash_randomization` :option:`-R` :const:`dev_mode` :option:`-X dev <-X>` (:ref:`Python Development Mode `) :const:`utf8_mode` :option:`-X utf8 <-X>` + :const:`safe_path` :option:`-P` ============================= ================================================================ .. versionchanged:: 3.2 @@ -539,6 +540,9 @@ always available. Mode ` and the ``utf8_mode`` attribute for the new :option:`-X` ``utf8`` flag. + .. versionchanged:: 3.11 + Added the ``safe_path`` attribute for :option:`-P` option. + .. data:: float_info @@ -1138,15 +1142,19 @@ always available. the environment variable :envvar:`PYTHONPATH`, plus an installation-dependent default. - As initialized upon program startup, the first item of this list, ``path[0]``, - is the directory containing the script that was used to invoke the Python - interpreter. If the script directory is not available (e.g. if the interpreter - is invoked interactively or if the script is read from standard input), - ``path[0]`` is the empty string, which directs Python to search modules in the - current directory first. Notice that the script directory is inserted *before* - the entries inserted as a result of :envvar:`PYTHONPATH`. + By default, as initialized upon program startup, a potentially unsafe path + is prepended to :data:`sys.path` (*before* the entries inserted as a result + of :envvar:`PYTHONPATH`): + + * ``python -m module`` command line: prepend the current working + directory. + * ``python script.py`` command line: prepend the script's directory. + If it's a symbolic link, resolve symbolic links. + * ``python -c code`` and ``python`` (REPL) command lines: prepend an empty + string, which means the current working directory. - The initialization of :data:`sys.path` is documented at :ref:`sys-path-init`. + To not prepend this potentially unsafe path, use the :option:`-P` command + line option or the :envvar:`PYTHONSAFEPATH` environment variable? A program is free to modify this list for its own purposes. Only strings and bytes should be added to :data:`sys.path`; all other data types are diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst index d341ea8..668459f 100644 --- a/Doc/using/cmdline.rst +++ b/Doc/using/cmdline.rst @@ -257,6 +257,8 @@ Miscellaneous options Ignore all :envvar:`PYTHON*` environment variables, e.g. :envvar:`PYTHONPATH` and :envvar:`PYTHONHOME`, that might be set. + See also the :option:`-P` and :option:`-I` (isolated) options. + .. cmdoption:: -i @@ -271,7 +273,9 @@ Miscellaneous options .. cmdoption:: -I - Run Python in isolated mode. This also implies -E and -s. + Run Python in isolated mode. This also implies :option:`-E`, :option:`-P` + and :option:`-s` options. + In isolated mode :data:`sys.path` contains neither the script's directory nor the user's site-packages directory. All :envvar:`PYTHON*` environment variables are ignored, too. Further restrictions may be imposed to prevent @@ -301,6 +305,23 @@ Miscellaneous options Modify ``.pyc`` filenames according to :pep:`488`. +.. cmdoption:: -P + + Don't prepend a potentially unsafe path to :data:`sys.path`: + + * ``python -m module`` command line: Don't prepend the current working + directory. + * ``python script.py`` command line: Don't prepend the script's directory. + If it's a symbolic link, resolve symbolic links. + * ``python -c code`` and ``python`` (REPL) command lines: Don't prepend an + empty string, which means the current working directory. + + See also the :envvar:`PYTHONSAFEPATH` environment variable, and :option:`-E` + and :option:`-I` (isolated) options. + + .. versionadded:: 3.11 + + .. cmdoption:: -q Don't display the copyright and version messages even in interactive mode. @@ -583,6 +604,14 @@ conflict. within a Python program as the variable :data:`sys.path`. +.. envvar:: PYTHONSAFEPATH + + If this is set to a non-empty string, don't prepend a potentially unsafe + path to :data:`sys.path`: see the :option:`-P` option for details. + + .. versionadded:: 3.11 + + .. envvar:: PYTHONPLATLIBDIR If this is set to a non-empty string, it overrides the :data:`sys.platlibdir` diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 2f32b56..87dc5dd 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -362,6 +362,11 @@ Other Language Changes pickles instance attributes implemented as :term:`slots <__slots__>`. (Contributed by Serhiy Storchaka in :issue:`26579`.) +* Add :option:`-P` command line option and :envvar:`PYTHONSAFEPATH` environment + variable to not prepend a potentially unsafe path to :data:`sys.path` such as + the current directory, the script's directory or an empty string. + (Contributed by Victor Stinner in :gh:`57684`.) + Other CPython Implementation Changes ==================================== @@ -636,6 +641,9 @@ sys (equivalent to ``sys.exc_info()[1]``). (Contributed by Irit Katriel in :issue:`46328`.) +* Add the :data:`sys.flags.safe_path ` flag. + (Contributed by Victor Stinner in :gh:`57684`.) + sysconfig --------- @@ -1480,6 +1488,8 @@ New Features representation of exceptions. (Contributed by Irit Katriel in :issue:`46343`.) +* Added the :c:member:`PyConfig.safe_path` member. + (Contributed by Victor Stinner in :gh:`57684`.) Porting to Python 3.11 ---------------------- diff --git a/Include/cpython/initconfig.h b/Include/cpython/initconfig.h index 2ba1224..3b6d593 100644 --- a/Include/cpython/initconfig.h +++ b/Include/cpython/initconfig.h @@ -176,6 +176,7 @@ typedef struct PyConfig { #endif wchar_t *check_hash_pycs_mode; int use_frozen_modules; + int safe_path; /* --- Path configuration inputs ------------ */ int pathconfig_warnings; diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 9099822..6e61cc2 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -313,12 +313,14 @@ def _args_from_interpreter_flags(): args.append('-E') if sys.flags.no_user_site: args.append('-s') + if sys.flags.safe_path: + args.append('-P') # -W options warnopts = sys.warnoptions[:] - bytes_warning = sys.flags.bytes_warning xoptions = getattr(sys, '_xoptions', {}) - dev_mode = ('dev' in xoptions) + bytes_warning = sys.flags.bytes_warning + dev_mode = sys.flags.dev_mode if bytes_warning > 1: warnopts.remove("error::BytesWarning") diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py index e8f1964..2650631 100644 --- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py @@ -579,13 +579,13 @@ class CmdLineTest(unittest.TestCase): 'Cannot run -I tests when PYTHON env vars are required.') def test_isolatedmode(self): self.verify_valid_flag('-I') - self.verify_valid_flag('-IEs') + self.verify_valid_flag('-IEPs') rc, out, err = assert_python_ok('-I', '-c', 'from sys import flags as f; ' - 'print(f.no_user_site, f.ignore_environment, f.isolated)', + 'print(f.no_user_site, f.ignore_environment, f.isolated, f.safe_path)', # dummyvar to prevent extraneous -E dummyvar="") - self.assertEqual(out.strip(), b'1 1 1') + self.assertEqual(out.strip(), b'1 1 1 True') with os_helper.temp_cwd() as tmpdir: fake = os.path.join(tmpdir, "uuid.py") main = os.path.join(tmpdir, "main.py") @@ -880,7 +880,8 @@ class IgnoreEnvironmentTest(unittest.TestCase): # Issue 31845: a startup refactoring broke reading flags from env vars expected_outcome = """ (sys.flags.debug == sys.flags.optimize == - sys.flags.dont_write_bytecode == sys.flags.verbose == 0) + sys.flags.dont_write_bytecode == + sys.flags.verbose == sys.flags.safe_path == 0) """ self.run_ignoring_vars( expected_outcome, @@ -888,6 +889,7 @@ class IgnoreEnvironmentTest(unittest.TestCase): PYTHONOPTIMIZE="1", PYTHONDONTWRITEBYTECODE="1", PYTHONVERBOSE="1", + PYTHONSAFEPATH="1", ) class SyntaxErrorTests(unittest.TestCase): diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index e255418..169ae5c 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -479,6 +479,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): '_init_main': 1, '_isolated_interpreter': 0, 'use_frozen_modules': not Py_DEBUG, + 'safe_path': 0, '_is_python_build': IGNORE_CONFIG, } if MS_WINDOWS: @@ -496,6 +497,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): isolated=1, use_environment=0, user_site_directory=0, + safe_path=1, dev_mode=0, install_signal_handlers=0, use_hash_seed=0, @@ -855,6 +857,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): 'faulthandler': 1, 'platlibdir': 'my_platlibdir', 'module_search_paths': self.IGNORE_CONFIG, + 'safe_path': 1, 'check_hash_pycs_mode': 'always', 'pathconfig_warnings': 0, @@ -889,6 +892,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): 'warnoptions': ['EnvVar'], 'platlibdir': 'env_platlibdir', 'module_search_paths': self.IGNORE_CONFIG, + 'safe_path': 1, } self.check_all_configs("test_init_compat_env", config, preconfig, api=API_COMPAT) @@ -919,6 +923,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): 'warnoptions': ['EnvVar'], 'platlibdir': 'env_platlibdir', 'module_search_paths': self.IGNORE_CONFIG, + 'safe_path': 1, } self.check_all_configs("test_init_python_env", config, preconfig, api=API_PYTHON) @@ -959,12 +964,13 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): } config = { 'argv': ['script.py'], - 'orig_argv': ['python3', '-X', 'dev', 'script.py'], + 'orig_argv': ['python3', '-X', 'dev', '-P', 'script.py'], 'run_filename': os.path.abspath('script.py'), 'dev_mode': 1, 'faulthandler': 1, 'warnoptions': ['default'], 'xoptions': ['dev'], + 'safe_path': 1, } self.check_all_configs("test_preinit_parse_argv", config, preconfig, api=API_PYTHON) @@ -975,7 +981,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): 'isolated': 0, } argv = ["python3", - "-E", "-I", + "-E", "-I", "-P", "-X", "dev", "-X", "utf8", "script.py"] @@ -990,6 +996,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): def test_init_isolated_flag(self): config = { 'isolated': 1, + 'safe_path': 1, 'use_environment': 0, 'user_site_directory': 0, } @@ -999,6 +1006,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): # _PyPreConfig.isolated=1, _PyCoreConfig.isolated not set config = { 'isolated': 1, + 'safe_path': 1, 'use_environment': 0, 'user_site_directory': 0, } @@ -1008,6 +1016,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): # _PyPreConfig.isolated=0, _PyCoreConfig.isolated=1 config = { 'isolated': 1, + 'safe_path': 1, 'use_environment': 0, 'user_site_directory': 0, } diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index 90e971d..dce4980 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -522,6 +522,7 @@ class TestSupport(unittest.TestCase): ['-E'], ['-v'], ['-b'], + ['-P'], ['-q'], ['-I'], # same option multiple times @@ -541,7 +542,8 @@ class TestSupport(unittest.TestCase): with self.subTest(opts=opts): self.check_options(opts, 'args_from_interpreter_flags') - self.check_options(['-I', '-E', '-s'], 'args_from_interpreter_flags', + self.check_options(['-I', '-E', '-s', '-P'], + 'args_from_interpreter_flags', ['-I']) def test_optim_args_from_interpreter_flags(self): diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index bbf01b6..8aaf232 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -669,10 +669,10 @@ class SysModuleTest(unittest.TestCase): "dont_write_bytecode", "no_user_site", "no_site", "ignore_environment", "verbose", "bytes_warning", "quiet", "hash_randomization", "isolated", "dev_mode", "utf8_mode", - "warn_default_encoding") + "warn_default_encoding", "safe_path") for attr in attrs: self.assertTrue(hasattr(sys.flags, attr), attr) - attr_type = bool if attr == "dev_mode" else int + attr_type = bool if attr in ("dev_mode", "safe_path") else int self.assertEqual(type(getattr(sys.flags, attr)), attr_type, attr) self.assertTrue(repr(sys.flags)) self.assertEqual(len(sys.flags), len(attrs)) diff --git a/Misc/NEWS.d/next/Security/2022-05-04-14-32-24.gh-issue-57684.HrlDrM.rst b/Misc/NEWS.d/next/Security/2022-05-04-14-32-24.gh-issue-57684.HrlDrM.rst new file mode 100644 index 0000000..f7cddcc --- /dev/null +++ b/Misc/NEWS.d/next/Security/2022-05-04-14-32-24.gh-issue-57684.HrlDrM.rst @@ -0,0 +1,3 @@ +Add the :option:`-P` command line option and the :envvar:`PYTHONSAFEPATH` +environment variable to not prepend a potentially unsafe path to +:data:`sys.path`. Patch by Victor Stinner. diff --git a/Misc/python.man b/Misc/python.man index 45a4927..c2e7e50 100644 --- a/Misc/python.man +++ b/Misc/python.man @@ -43,6 +43,9 @@ python \- an interpreted, interactive, object-oriented programming language .B \-OO ] [ +.B \-P +] +[ .B \-s ] [ @@ -154,7 +157,7 @@ useful to inspect global variables or a stack trace when a script raises an exception. .TP .B \-I -Run Python in isolated mode. This also implies \fB\-E\fP and \fB\-s\fP. In +Run Python in isolated mode. This also implies \fB\-E\fP, \fB\-P\fP and \fB\-s\fP. In isolated mode sys.path contains neither the script's directory nor the user's site-packages directory. All PYTHON* environment variables are ignored, too. Further restrictions may be imposed to prevent the user from injecting @@ -177,6 +180,11 @@ adding .opt-1 before the .pyc extension. Do \fB-O\fP and also discard docstrings; change the filename for compiled (bytecode) files by adding .opt-2 before the .pyc extension. .TP +.B \-P +Don't automatically prepend a potentially unsafe path to \fBsys.path\fP such +as the current directory, the script's directory or an empty string. See also the +\fBPYTHONSAFEPATH\fP environment variable. +.TP .B \-q Do not print the version and copyright messages. These messages are also suppressed in non-interactive mode. @@ -398,6 +406,10 @@ needed for developing Python extensions and embedding the interpreter. .RE .SH ENVIRONMENT VARIABLES +.IP PYTHONSAFEPATH +If this is set to a non-empty string, don't automatically prepend a potentially +unsafe path to \fBsys.path\fP such as the current directory, the script's +directory or an empty string. See also the \fB\-P\fP option. .IP PYTHONHOME Change the location of the standard Python libraries. By default, the libraries are searched in ${prefix}/lib/python and diff --git a/Modules/getpath.py b/Modules/getpath.py index 26465c8..9aff19c 100644 --- a/Modules/getpath.py +++ b/Modules/getpath.py @@ -717,6 +717,7 @@ if pth: config['isolated'] = 1 config['use_environment'] = 0 config['site_import'] = 0 + config['safe_path'] = 1 pythonpath = [] for line in pth: line = line.partition('#')[0].strip() diff --git a/Modules/main.c b/Modules/main.c index 624c0f3..cca669b 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -570,7 +570,7 @@ pymain_run_python(int *exitcode) goto error; } } - else if (!config->isolated) { + else if (!config->safe_path) { PyObject *path0 = NULL; int res = _PyPathConfig_ComputeSysPath0(&config->argv, &path0); if (res < 0) { diff --git a/Programs/_bootstrap_python.c b/Programs/_bootstrap_python.c index f6b49c8..6ecbf0c 100644 --- a/Programs/_bootstrap_python.c +++ b/Programs/_bootstrap_python.c @@ -71,6 +71,7 @@ main(int argc, char **argv) config.parse_argv = 1; // add current script dir to sys.path config.isolated = 0; + config.safe_path = 0; #ifdef MS_WINDOWS status = PyConfig_SetArgv(&config, argc, argv); diff --git a/Programs/_testembed.c b/Programs/_testembed.c index 3830dc3..9d3d0cb 100644 --- a/Programs/_testembed.c +++ b/Programs/_testembed.c @@ -676,6 +676,8 @@ static int test_init_from_config(void) Py_FrozenFlag = 0; config.pathconfig_warnings = 0; + config.safe_path = 1; + config._isolated_interpreter = 1; init_from_config_clear(&config); @@ -742,6 +744,7 @@ static void set_most_env_vars(void) putenv("PYTHONFAULTHANDLER=1"); putenv("PYTHONIOENCODING=iso8859-1:replace"); putenv("PYTHONPLATLIBDIR=env_platlibdir"); + putenv("PYTHONSAFEPATH=1"); } @@ -823,6 +826,10 @@ static int test_init_isolated_flag(void) Py_IsolatedFlag = 0; config.isolated = 1; + // These options are set to 1 by isolated=1 + config.safe_path = 0; + config.use_environment = 1; + config.user_site_directory = 1; config_set_program_name(&config); set_all_env_vars(); @@ -901,6 +908,7 @@ static int test_preinit_dont_parse_argv(void) wchar_t *argv[] = {L"python3", L"-E", L"-I", + L"-P", L"-X", L"dev", L"-X", L"utf8", L"script.py"}; @@ -934,7 +942,7 @@ static int test_preinit_parse_argv(void) /* Pre-initialize implicitly using argv: make sure that -X dev is used to configure the allocation in preinitialization */ - wchar_t *argv[] = {L"python3", L"-X", L"dev", L"script.py"}; + wchar_t *argv[] = {L"python3", L"-X", L"dev", L"-P", L"script.py"}; config_set_argv(&config, Py_ARRAY_LENGTH(argv), argv); config_set_program_name(&config); init_from_config_clear(&config); diff --git a/Python/dynload_win.c b/Python/dynload_win.c index 854b1e6..b43e9fc 100644 --- a/Python/dynload_win.c +++ b/Python/dynload_win.c @@ -3,7 +3,6 @@ #include "Python.h" #include "pycore_fileutils.h" // _Py_add_relfile() -#include "pycore_pathconfig.h" // _PyPathConfig_ComputeSysPath0() #include "pycore_pystate.h" // _PyInterpreterState_GET() #ifdef HAVE_DIRECT_H diff --git a/Python/getopt.c b/Python/getopt.c index 2e3891a..fcea607 100644 --- a/Python/getopt.c +++ b/Python/getopt.c @@ -41,7 +41,7 @@ static const wchar_t *opt_ptr = L""; /* Python command line short and long options */ -#define SHORT_OPTS L"bBc:dEhiIJm:OqRsStuvVW:xX:?" +#define SHORT_OPTS L"bBc:dEhiIJm:OPqRsStuvVW:xX:?" static const _PyOS_LongOption longopts[] = { {L"check-hash-based-pycs", 1, 0}, diff --git a/Python/initconfig.c b/Python/initconfig.c index d928ebe..265c7ca 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -49,6 +49,7 @@ static const char usage_2[] = "\ .pyc extension; also PYTHONOPTIMIZE=x\n\ -OO : do -O changes and also discard docstrings; add .opt-2 before\n\ .pyc extension\n\ +-P : don't add sys.path[0]\n\ -q : don't print version and copyright messages on interactive startup\n\ -s : don't add user site directory to sys.path; also PYTHONNOUSERSITE\n\ -S : don't imply 'import site' on initialization\n\ @@ -113,6 +114,7 @@ PYTHONPATH : '%lc'-separated list of directories prefixed to the\n\ default module search path. The result is sys.path.\n\ "; static const char usage_5[] = +"PYTHONSAFEPATH: don't prepend a potentially unsafe path to sys.path.\n" "PYTHONHOME : alternate directory (or %lc).\n" " The default module search path uses %s.\n" "PYTHONPLATLIBDIR : override sys.platlibdir.\n" @@ -647,6 +649,10 @@ config_check_consistency(const PyConfig *config) assert(config->check_hash_pycs_mode != NULL); assert(config->_install_importlib >= 0); assert(config->pathconfig_warnings >= 0); + assert(config->_is_python_build >= 0); + assert(config->safe_path >= 0); + // config->use_frozen_modules is initialized later + // by _PyConfig_InitImportConfig(). return 1; } #endif @@ -737,6 +743,7 @@ _PyConfig_InitCompatConfig(PyConfig *config) #else config->use_frozen_modules = 1; #endif + config->safe_path = 0; config->_is_python_build = 0; config->code_debug_ranges = 1; } @@ -792,6 +799,7 @@ PyConfig_InitIsolatedConfig(PyConfig *config) config->use_hash_seed = 0; config->faulthandler = 0; config->tracemalloc = 0; + config->safe_path = 1; config->pathconfig_warnings = 0; #ifdef MS_WINDOWS config->legacy_windows_stdio = 0; @@ -959,6 +967,7 @@ _PyConfig_Copy(PyConfig *config, const PyConfig *config2) COPY_ATTR(_init_main); COPY_ATTR(_isolated_interpreter); COPY_ATTR(use_frozen_modules); + COPY_ATTR(safe_path); COPY_WSTRLIST(orig_argv); COPY_ATTR(_is_python_build); @@ -1065,6 +1074,7 @@ _PyConfig_AsDict(const PyConfig *config) SET_ITEM_INT(_isolated_interpreter); SET_ITEM_WSTRLIST(orig_argv); SET_ITEM_INT(use_frozen_modules); + SET_ITEM_INT(safe_path); SET_ITEM_INT(_is_python_build); return dict; @@ -1350,6 +1360,7 @@ _PyConfig_FromDict(PyConfig *config, PyObject *dict) GET_UINT(_init_main); GET_UINT(_isolated_interpreter); GET_UINT(use_frozen_modules); + GET_UINT(safe_path); GET_UINT(_is_python_build); #undef CHECK_VALUE @@ -1633,6 +1644,10 @@ config_read_env_vars(PyConfig *config) } } + if (config_get_env(config, "PYTHONSAFEPATH")) { + config->safe_path = 1; + } + return _PyStatus_OK(); } @@ -2000,6 +2015,7 @@ config_init_import(PyConfig *config, int compute_path_config) "(expected \"on\" or \"off\")"); } + assert(config->use_frozen_modules >= 0); return _PyStatus_OK(); } @@ -2327,6 +2343,10 @@ config_parse_cmdline(PyConfig *config, PyWideStringList *warnoptions, config->optimization_level++; break; + case 'P': + config->safe_path = 1; + break; + case 'B': config->write_bytecode = 0; break; @@ -2849,6 +2869,7 @@ _PyConfig_Read(PyConfig *config, int compute_path_config) assert(config->isolated >= 0); if (config->isolated) { + config->safe_path = 1; config->use_environment = 0; config->user_site_directory = 0; } @@ -2994,6 +3015,7 @@ _Py_DumpPathConfig(PyThreadState *tstate) PySys_WriteStderr(" isolated = %i\n", config->isolated); PySys_WriteStderr(" environment = %i\n", config->use_environment); PySys_WriteStderr(" user site = %i\n", config->user_site_directory); + PySys_WriteStderr(" safe_path = %i\n", config->safe_path); PySys_WriteStderr(" import site = %i\n", config->site_import); PySys_WriteStderr(" is in build tree = %i\n", config->_is_python_build); DUMP_CONFIG("stdlib dir", stdlib_dir); diff --git a/Python/sysmodule.c b/Python/sysmodule.c index d5a62fc..edd1d1f 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -2479,6 +2479,7 @@ static PyStructSequence_Field flags_fields[] = { {"dev_mode", "-X dev"}, {"utf8_mode", "-X utf8"}, {"warn_default_encoding", "-X warn_default_encoding"}, + {"safe_path", "-P"}, {0} }; @@ -2486,7 +2487,7 @@ static PyStructSequence_Desc flags_desc = { "sys.flags", /* name */ flags__doc__, /* doc */ flags_fields, /* fields */ - 16 + 17 }; static int @@ -2526,6 +2527,7 @@ set_flags_from_config(PyInterpreterState *interp, PyObject *flags) SetFlagObj(PyBool_FromLong(config->dev_mode)); SetFlag(preconfig->utf8_mode); SetFlag(config->warn_default_encoding); + SetFlagObj(PyBool_FromLong(config->safe_path)); #undef SetFlagObj #undef SetFlag return 0; -- cgit v0.12