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.
This commit is contained in:
Victor Stinner 2022-05-06 01:34:11 +02:00 committed by GitHub
parent f6dd14c653
commit ada8b6d1b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 174 additions and 35 deletions

View File

@ -543,6 +543,25 @@ PyConfig
See also the :c:member:`~PyConfig.orig_argv` member. 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] <PyConfig.argv>` 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 .. c:member:: wchar_t* base_exec_prefix
:data:`sys.base_exec_prefix`. :data:`sys.base_exec_prefix`.
@ -809,13 +828,14 @@ PyConfig
If greater than 0, enable isolated mode: If greater than 0, enable isolated mode:
* :data:`sys.path` contains neither the script's directory (computed from * Set :c:member:`~PyConfig.safe_path` to 1:
``argv[0]`` or the current directory) nor the user's site-packages don't prepend a potentially unsafe path to :data:`sys.path` at Python
directory. 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 * Python REPL doesn't import :mod:`readline` nor enable default readline
configuration on interactive prompts. 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. Default: ``0`` in Python mode, ``1`` in isolated mode.
@ -1029,12 +1049,13 @@ PyConfig
.. c:member:: wchar_t* run_filename .. c:member:: wchar_t* run_filename
Filename passed on the command line: trailing command line argument 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`` 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``. Default: ``NULL``.
@ -1419,9 +1440,16 @@ site-package directory to :data:`sys.path`.
The following configuration files are used by the path configuration: The following configuration files are used by the path configuration:
* ``pyvenv.cfg`` * ``pyvenv.cfg``
* ``python._pth`` (Windows only) * ``._pth`` file (ex: ``python._pth``)
* ``pybuilddir.txt`` (Unix only) * ``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 The ``__PYVENV_LAUNCHER__`` environment variable is used to set
:c:member:`PyConfig.base_executable` :c:member:`PyConfig.base_executable`

View File

@ -520,6 +520,7 @@ always available.
:const:`hash_randomization` :option:`-R` :const:`hash_randomization` :option:`-R`
:const:`dev_mode` :option:`-X dev <-X>` (:ref:`Python Development Mode <devmode>`) :const:`dev_mode` :option:`-X dev <-X>` (:ref:`Python Development Mode <devmode>`)
:const:`utf8_mode` :option:`-X utf8 <-X>` :const:`utf8_mode` :option:`-X utf8 <-X>`
:const:`safe_path` :option:`-P`
============================= ================================================================ ============================= ================================================================
.. versionchanged:: 3.2 .. versionchanged:: 3.2
@ -539,6 +540,9 @@ always available.
Mode <devmode>` and the ``utf8_mode`` attribute for the new :option:`-X` Mode <devmode>` and the ``utf8_mode`` attribute for the new :option:`-X`
``utf8`` flag. ``utf8`` flag.
.. versionchanged:: 3.11
Added the ``safe_path`` attribute for :option:`-P` option.
.. data:: float_info .. data:: float_info
@ -1138,15 +1142,19 @@ always available.
the environment variable :envvar:`PYTHONPATH`, plus an installation-dependent the environment variable :envvar:`PYTHONPATH`, plus an installation-dependent
default. default.
As initialized upon program startup, the first item of this list, ``path[0]``, By default, as initialized upon program startup, a potentially unsafe path
is the directory containing the script that was used to invoke the Python is prepended to :data:`sys.path` (*before* the entries inserted as a result
interpreter. If the script directory is not available (e.g. if the interpreter of :envvar:`PYTHONPATH`):
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`.
The initialization of :data:`sys.path` is documented at :ref:`sys-path-init`. * ``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.
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 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 and bytes should be added to :data:`sys.path`; all other data types are

View File

@ -257,6 +257,8 @@ Miscellaneous options
Ignore all :envvar:`PYTHON*` environment variables, e.g. Ignore all :envvar:`PYTHON*` environment variables, e.g.
:envvar:`PYTHONPATH` and :envvar:`PYTHONHOME`, that might be set. :envvar:`PYTHONPATH` and :envvar:`PYTHONHOME`, that might be set.
See also the :option:`-P` and :option:`-I` (isolated) options.
.. cmdoption:: -i .. cmdoption:: -i
@ -271,7 +273,9 @@ Miscellaneous options
.. cmdoption:: -I .. 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 In isolated mode :data:`sys.path` contains neither the script's directory nor
the user's site-packages directory. All :envvar:`PYTHON*` environment the user's site-packages directory. All :envvar:`PYTHON*` environment
variables are ignored, too. Further restrictions may be imposed to prevent variables are ignored, too. Further restrictions may be imposed to prevent
@ -301,6 +305,23 @@ Miscellaneous options
Modify ``.pyc`` filenames according to :pep:`488`. 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 .. cmdoption:: -q
Don't display the copyright and version messages even in interactive mode. 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`. 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 .. envvar:: PYTHONPLATLIBDIR
If this is set to a non-empty string, it overrides the :data:`sys.platlibdir` If this is set to a non-empty string, it overrides the :data:`sys.platlibdir`

View File

@ -362,6 +362,11 @@ Other Language Changes
pickles instance attributes implemented as :term:`slots <__slots__>`. pickles instance attributes implemented as :term:`slots <__slots__>`.
(Contributed by Serhiy Storchaka in :issue:`26579`.) (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 Other CPython Implementation Changes
==================================== ====================================
@ -636,6 +641,9 @@ sys
(equivalent to ``sys.exc_info()[1]``). (equivalent to ``sys.exc_info()[1]``).
(Contributed by Irit Katriel in :issue:`46328`.) (Contributed by Irit Katriel in :issue:`46328`.)
* Add the :data:`sys.flags.safe_path <sys.flags>` flag.
(Contributed by Victor Stinner in :gh:`57684`.)
sysconfig sysconfig
--------- ---------
@ -1480,6 +1488,8 @@ New Features
representation of exceptions. representation of exceptions.
(Contributed by Irit Katriel in :issue:`46343`.) (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 Porting to Python 3.11
---------------------- ----------------------

View File

@ -176,6 +176,7 @@ typedef struct PyConfig {
#endif #endif
wchar_t *check_hash_pycs_mode; wchar_t *check_hash_pycs_mode;
int use_frozen_modules; int use_frozen_modules;
int safe_path;
/* --- Path configuration inputs ------------ */ /* --- Path configuration inputs ------------ */
int pathconfig_warnings; int pathconfig_warnings;

View File

@ -313,12 +313,14 @@ def _args_from_interpreter_flags():
args.append('-E') args.append('-E')
if sys.flags.no_user_site: if sys.flags.no_user_site:
args.append('-s') args.append('-s')
if sys.flags.safe_path:
args.append('-P')
# -W options # -W options
warnopts = sys.warnoptions[:] warnopts = sys.warnoptions[:]
bytes_warning = sys.flags.bytes_warning
xoptions = getattr(sys, '_xoptions', {}) 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: if bytes_warning > 1:
warnopts.remove("error::BytesWarning") warnopts.remove("error::BytesWarning")

View File

@ -579,13 +579,13 @@ class CmdLineTest(unittest.TestCase):
'Cannot run -I tests when PYTHON env vars are required.') 'Cannot run -I tests when PYTHON env vars are required.')
def test_isolatedmode(self): def test_isolatedmode(self):
self.verify_valid_flag('-I') self.verify_valid_flag('-I')
self.verify_valid_flag('-IEs') self.verify_valid_flag('-IEPs')
rc, out, err = assert_python_ok('-I', '-c', rc, out, err = assert_python_ok('-I', '-c',
'from sys import flags as f; ' '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 to prevent extraneous -E
dummyvar="") 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: with os_helper.temp_cwd() as tmpdir:
fake = os.path.join(tmpdir, "uuid.py") fake = os.path.join(tmpdir, "uuid.py")
main = os.path.join(tmpdir, "main.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 # Issue 31845: a startup refactoring broke reading flags from env vars
expected_outcome = """ expected_outcome = """
(sys.flags.debug == sys.flags.optimize == (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( self.run_ignoring_vars(
expected_outcome, expected_outcome,
@ -888,6 +889,7 @@ class IgnoreEnvironmentTest(unittest.TestCase):
PYTHONOPTIMIZE="1", PYTHONOPTIMIZE="1",
PYTHONDONTWRITEBYTECODE="1", PYTHONDONTWRITEBYTECODE="1",
PYTHONVERBOSE="1", PYTHONVERBOSE="1",
PYTHONSAFEPATH="1",
) )
class SyntaxErrorTests(unittest.TestCase): class SyntaxErrorTests(unittest.TestCase):

View File

@ -479,6 +479,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
'_init_main': 1, '_init_main': 1,
'_isolated_interpreter': 0, '_isolated_interpreter': 0,
'use_frozen_modules': not Py_DEBUG, 'use_frozen_modules': not Py_DEBUG,
'safe_path': 0,
'_is_python_build': IGNORE_CONFIG, '_is_python_build': IGNORE_CONFIG,
} }
if MS_WINDOWS: if MS_WINDOWS:
@ -496,6 +497,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
isolated=1, isolated=1,
use_environment=0, use_environment=0,
user_site_directory=0, user_site_directory=0,
safe_path=1,
dev_mode=0, dev_mode=0,
install_signal_handlers=0, install_signal_handlers=0,
use_hash_seed=0, use_hash_seed=0,
@ -855,6 +857,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
'faulthandler': 1, 'faulthandler': 1,
'platlibdir': 'my_platlibdir', 'platlibdir': 'my_platlibdir',
'module_search_paths': self.IGNORE_CONFIG, 'module_search_paths': self.IGNORE_CONFIG,
'safe_path': 1,
'check_hash_pycs_mode': 'always', 'check_hash_pycs_mode': 'always',
'pathconfig_warnings': 0, 'pathconfig_warnings': 0,
@ -889,6 +892,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
'warnoptions': ['EnvVar'], 'warnoptions': ['EnvVar'],
'platlibdir': 'env_platlibdir', 'platlibdir': 'env_platlibdir',
'module_search_paths': self.IGNORE_CONFIG, 'module_search_paths': self.IGNORE_CONFIG,
'safe_path': 1,
} }
self.check_all_configs("test_init_compat_env", config, preconfig, self.check_all_configs("test_init_compat_env", config, preconfig,
api=API_COMPAT) api=API_COMPAT)
@ -919,6 +923,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
'warnoptions': ['EnvVar'], 'warnoptions': ['EnvVar'],
'platlibdir': 'env_platlibdir', 'platlibdir': 'env_platlibdir',
'module_search_paths': self.IGNORE_CONFIG, 'module_search_paths': self.IGNORE_CONFIG,
'safe_path': 1,
} }
self.check_all_configs("test_init_python_env", config, preconfig, self.check_all_configs("test_init_python_env", config, preconfig,
api=API_PYTHON) api=API_PYTHON)
@ -959,12 +964,13 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
} }
config = { config = {
'argv': ['script.py'], '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'), 'run_filename': os.path.abspath('script.py'),
'dev_mode': 1, 'dev_mode': 1,
'faulthandler': 1, 'faulthandler': 1,
'warnoptions': ['default'], 'warnoptions': ['default'],
'xoptions': ['dev'], 'xoptions': ['dev'],
'safe_path': 1,
} }
self.check_all_configs("test_preinit_parse_argv", config, preconfig, self.check_all_configs("test_preinit_parse_argv", config, preconfig,
api=API_PYTHON) api=API_PYTHON)
@ -975,7 +981,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
'isolated': 0, 'isolated': 0,
} }
argv = ["python3", argv = ["python3",
"-E", "-I", "-E", "-I", "-P",
"-X", "dev", "-X", "dev",
"-X", "utf8", "-X", "utf8",
"script.py"] "script.py"]
@ -990,6 +996,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
def test_init_isolated_flag(self): def test_init_isolated_flag(self):
config = { config = {
'isolated': 1, 'isolated': 1,
'safe_path': 1,
'use_environment': 0, 'use_environment': 0,
'user_site_directory': 0, 'user_site_directory': 0,
} }
@ -999,6 +1006,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
# _PyPreConfig.isolated=1, _PyCoreConfig.isolated not set # _PyPreConfig.isolated=1, _PyCoreConfig.isolated not set
config = { config = {
'isolated': 1, 'isolated': 1,
'safe_path': 1,
'use_environment': 0, 'use_environment': 0,
'user_site_directory': 0, 'user_site_directory': 0,
} }
@ -1008,6 +1016,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
# _PyPreConfig.isolated=0, _PyCoreConfig.isolated=1 # _PyPreConfig.isolated=0, _PyCoreConfig.isolated=1
config = { config = {
'isolated': 1, 'isolated': 1,
'safe_path': 1,
'use_environment': 0, 'use_environment': 0,
'user_site_directory': 0, 'user_site_directory': 0,
} }

View File

@ -522,6 +522,7 @@ class TestSupport(unittest.TestCase):
['-E'], ['-E'],
['-v'], ['-v'],
['-b'], ['-b'],
['-P'],
['-q'], ['-q'],
['-I'], ['-I'],
# same option multiple times # same option multiple times
@ -541,7 +542,8 @@ class TestSupport(unittest.TestCase):
with self.subTest(opts=opts): with self.subTest(opts=opts):
self.check_options(opts, 'args_from_interpreter_flags') 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']) ['-I'])
def test_optim_args_from_interpreter_flags(self): def test_optim_args_from_interpreter_flags(self):

View File

@ -669,10 +669,10 @@ class SysModuleTest(unittest.TestCase):
"dont_write_bytecode", "no_user_site", "no_site", "dont_write_bytecode", "no_user_site", "no_site",
"ignore_environment", "verbose", "bytes_warning", "quiet", "ignore_environment", "verbose", "bytes_warning", "quiet",
"hash_randomization", "isolated", "dev_mode", "utf8_mode", "hash_randomization", "isolated", "dev_mode", "utf8_mode",
"warn_default_encoding") "warn_default_encoding", "safe_path")
for attr in attrs: for attr in attrs:
self.assertTrue(hasattr(sys.flags, attr), attr) 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.assertEqual(type(getattr(sys.flags, attr)), attr_type, attr)
self.assertTrue(repr(sys.flags)) self.assertTrue(repr(sys.flags))
self.assertEqual(len(sys.flags), len(attrs)) self.assertEqual(len(sys.flags), len(attrs))

View File

@ -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.

View File

@ -43,6 +43,9 @@ python \- an interpreted, interactive, object-oriented programming language
.B \-OO .B \-OO
] ]
[ [
.B \-P
]
[
.B \-s .B \-s
] ]
[ [
@ -154,7 +157,7 @@ useful to inspect global variables or a stack trace when a script
raises an exception. raises an exception.
.TP .TP
.B \-I .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 isolated mode sys.path contains neither the script's directory nor the user's
site-packages directory. All PYTHON* environment variables are ignored, too. site-packages directory. All PYTHON* environment variables are ignored, too.
Further restrictions may be imposed to prevent the user from injecting 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 Do \fB-O\fP and also discard docstrings; change the filename for
compiled (bytecode) files by adding .opt-2 before the .pyc extension. compiled (bytecode) files by adding .opt-2 before the .pyc extension.
.TP .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 .B \-q
Do not print the version and copyright messages. These messages are Do not print the version and copyright messages. These messages are
also suppressed in non-interactive mode. also suppressed in non-interactive mode.
@ -398,6 +406,10 @@ needed for developing Python extensions and embedding the
interpreter. interpreter.
.RE .RE
.SH ENVIRONMENT VARIABLES .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 .IP PYTHONHOME
Change the location of the standard Python libraries. By default, the Change the location of the standard Python libraries. By default, the
libraries are searched in ${prefix}/lib/python<version> and libraries are searched in ${prefix}/lib/python<version> and

View File

@ -717,6 +717,7 @@ if pth:
config['isolated'] = 1 config['isolated'] = 1
config['use_environment'] = 0 config['use_environment'] = 0
config['site_import'] = 0 config['site_import'] = 0
config['safe_path'] = 1
pythonpath = [] pythonpath = []
for line in pth: for line in pth:
line = line.partition('#')[0].strip() line = line.partition('#')[0].strip()

View File

@ -570,7 +570,7 @@ pymain_run_python(int *exitcode)
goto error; goto error;
} }
} }
else if (!config->isolated) { else if (!config->safe_path) {
PyObject *path0 = NULL; PyObject *path0 = NULL;
int res = _PyPathConfig_ComputeSysPath0(&config->argv, &path0); int res = _PyPathConfig_ComputeSysPath0(&config->argv, &path0);
if (res < 0) { if (res < 0) {

View File

@ -71,6 +71,7 @@ main(int argc, char **argv)
config.parse_argv = 1; config.parse_argv = 1;
// add current script dir to sys.path // add current script dir to sys.path
config.isolated = 0; config.isolated = 0;
config.safe_path = 0;
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
status = PyConfig_SetArgv(&config, argc, argv); status = PyConfig_SetArgv(&config, argc, argv);

View File

@ -676,6 +676,8 @@ static int test_init_from_config(void)
Py_FrozenFlag = 0; Py_FrozenFlag = 0;
config.pathconfig_warnings = 0; config.pathconfig_warnings = 0;
config.safe_path = 1;
config._isolated_interpreter = 1; config._isolated_interpreter = 1;
init_from_config_clear(&config); init_from_config_clear(&config);
@ -742,6 +744,7 @@ static void set_most_env_vars(void)
putenv("PYTHONFAULTHANDLER=1"); putenv("PYTHONFAULTHANDLER=1");
putenv("PYTHONIOENCODING=iso8859-1:replace"); putenv("PYTHONIOENCODING=iso8859-1:replace");
putenv("PYTHONPLATLIBDIR=env_platlibdir"); putenv("PYTHONPLATLIBDIR=env_platlibdir");
putenv("PYTHONSAFEPATH=1");
} }
@ -823,6 +826,10 @@ static int test_init_isolated_flag(void)
Py_IsolatedFlag = 0; Py_IsolatedFlag = 0;
config.isolated = 1; 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); config_set_program_name(&config);
set_all_env_vars(); set_all_env_vars();
@ -901,6 +908,7 @@ static int test_preinit_dont_parse_argv(void)
wchar_t *argv[] = {L"python3", wchar_t *argv[] = {L"python3",
L"-E", L"-E",
L"-I", L"-I",
L"-P",
L"-X", L"dev", L"-X", L"dev",
L"-X", L"utf8", L"-X", L"utf8",
L"script.py"}; L"script.py"};
@ -934,7 +942,7 @@ static int test_preinit_parse_argv(void)
/* Pre-initialize implicitly using argv: make sure that -X dev /* Pre-initialize implicitly using argv: make sure that -X dev
is used to configure the allocation in preinitialization */ 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_argv(&config, Py_ARRAY_LENGTH(argv), argv);
config_set_program_name(&config); config_set_program_name(&config);
init_from_config_clear(&config); init_from_config_clear(&config);

View File

@ -3,7 +3,6 @@
#include "Python.h" #include "Python.h"
#include "pycore_fileutils.h" // _Py_add_relfile() #include "pycore_fileutils.h" // _Py_add_relfile()
#include "pycore_pathconfig.h" // _PyPathConfig_ComputeSysPath0()
#include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_pystate.h" // _PyInterpreterState_GET()
#ifdef HAVE_DIRECT_H #ifdef HAVE_DIRECT_H

View File

@ -41,7 +41,7 @@ static const wchar_t *opt_ptr = L"";
/* Python command line short and long options */ /* 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[] = { static const _PyOS_LongOption longopts[] = {
{L"check-hash-based-pycs", 1, 0}, {L"check-hash-based-pycs", 1, 0},

View File

@ -49,6 +49,7 @@ static const char usage_2[] = "\
.pyc extension; also PYTHONOPTIMIZE=x\n\ .pyc extension; also PYTHONOPTIMIZE=x\n\
-OO : do -O changes and also discard docstrings; add .opt-2 before\n\ -OO : do -O changes and also discard docstrings; add .opt-2 before\n\
.pyc extension\n\ .pyc extension\n\
-P : don't add sys.path[0]\n\
-q : don't print version and copyright messages on interactive startup\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 add user site directory to sys.path; also PYTHONNOUSERSITE\n\
-S : don't imply 'import site' on initialization\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\ default module search path. The result is sys.path.\n\
"; ";
static const char usage_5[] = static const char usage_5[] =
"PYTHONSAFEPATH: don't prepend a potentially unsafe path to sys.path.\n"
"PYTHONHOME : alternate <prefix> directory (or <prefix>%lc<exec_prefix>).\n" "PYTHONHOME : alternate <prefix> directory (or <prefix>%lc<exec_prefix>).\n"
" The default module search path uses %s.\n" " The default module search path uses %s.\n"
"PYTHONPLATLIBDIR : override sys.platlibdir.\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->check_hash_pycs_mode != NULL);
assert(config->_install_importlib >= 0); assert(config->_install_importlib >= 0);
assert(config->pathconfig_warnings >= 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; return 1;
} }
#endif #endif
@ -737,6 +743,7 @@ _PyConfig_InitCompatConfig(PyConfig *config)
#else #else
config->use_frozen_modules = 1; config->use_frozen_modules = 1;
#endif #endif
config->safe_path = 0;
config->_is_python_build = 0; config->_is_python_build = 0;
config->code_debug_ranges = 1; config->code_debug_ranges = 1;
} }
@ -792,6 +799,7 @@ PyConfig_InitIsolatedConfig(PyConfig *config)
config->use_hash_seed = 0; config->use_hash_seed = 0;
config->faulthandler = 0; config->faulthandler = 0;
config->tracemalloc = 0; config->tracemalloc = 0;
config->safe_path = 1;
config->pathconfig_warnings = 0; config->pathconfig_warnings = 0;
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
config->legacy_windows_stdio = 0; config->legacy_windows_stdio = 0;
@ -959,6 +967,7 @@ _PyConfig_Copy(PyConfig *config, const PyConfig *config2)
COPY_ATTR(_init_main); COPY_ATTR(_init_main);
COPY_ATTR(_isolated_interpreter); COPY_ATTR(_isolated_interpreter);
COPY_ATTR(use_frozen_modules); COPY_ATTR(use_frozen_modules);
COPY_ATTR(safe_path);
COPY_WSTRLIST(orig_argv); COPY_WSTRLIST(orig_argv);
COPY_ATTR(_is_python_build); COPY_ATTR(_is_python_build);
@ -1065,6 +1074,7 @@ _PyConfig_AsDict(const PyConfig *config)
SET_ITEM_INT(_isolated_interpreter); SET_ITEM_INT(_isolated_interpreter);
SET_ITEM_WSTRLIST(orig_argv); SET_ITEM_WSTRLIST(orig_argv);
SET_ITEM_INT(use_frozen_modules); SET_ITEM_INT(use_frozen_modules);
SET_ITEM_INT(safe_path);
SET_ITEM_INT(_is_python_build); SET_ITEM_INT(_is_python_build);
return dict; return dict;
@ -1350,6 +1360,7 @@ _PyConfig_FromDict(PyConfig *config, PyObject *dict)
GET_UINT(_init_main); GET_UINT(_init_main);
GET_UINT(_isolated_interpreter); GET_UINT(_isolated_interpreter);
GET_UINT(use_frozen_modules); GET_UINT(use_frozen_modules);
GET_UINT(safe_path);
GET_UINT(_is_python_build); GET_UINT(_is_python_build);
#undef CHECK_VALUE #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(); return _PyStatus_OK();
} }
@ -2000,6 +2015,7 @@ config_init_import(PyConfig *config, int compute_path_config)
"(expected \"on\" or \"off\")"); "(expected \"on\" or \"off\")");
} }
assert(config->use_frozen_modules >= 0);
return _PyStatus_OK(); return _PyStatus_OK();
} }
@ -2327,6 +2343,10 @@ config_parse_cmdline(PyConfig *config, PyWideStringList *warnoptions,
config->optimization_level++; config->optimization_level++;
break; break;
case 'P':
config->safe_path = 1;
break;
case 'B': case 'B':
config->write_bytecode = 0; config->write_bytecode = 0;
break; break;
@ -2849,6 +2869,7 @@ _PyConfig_Read(PyConfig *config, int compute_path_config)
assert(config->isolated >= 0); assert(config->isolated >= 0);
if (config->isolated) { if (config->isolated) {
config->safe_path = 1;
config->use_environment = 0; config->use_environment = 0;
config->user_site_directory = 0; config->user_site_directory = 0;
} }
@ -2994,6 +3015,7 @@ _Py_DumpPathConfig(PyThreadState *tstate)
PySys_WriteStderr(" isolated = %i\n", config->isolated); PySys_WriteStderr(" isolated = %i\n", config->isolated);
PySys_WriteStderr(" environment = %i\n", config->use_environment); PySys_WriteStderr(" environment = %i\n", config->use_environment);
PySys_WriteStderr(" user site = %i\n", config->user_site_directory); 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(" import site = %i\n", config->site_import);
PySys_WriteStderr(" is in build tree = %i\n", config->_is_python_build); PySys_WriteStderr(" is in build tree = %i\n", config->_is_python_build);
DUMP_CONFIG("stdlib dir", stdlib_dir); DUMP_CONFIG("stdlib dir", stdlib_dir);

View File

@ -2479,6 +2479,7 @@ static PyStructSequence_Field flags_fields[] = {
{"dev_mode", "-X dev"}, {"dev_mode", "-X dev"},
{"utf8_mode", "-X utf8"}, {"utf8_mode", "-X utf8"},
{"warn_default_encoding", "-X warn_default_encoding"}, {"warn_default_encoding", "-X warn_default_encoding"},
{"safe_path", "-P"},
{0} {0}
}; };
@ -2486,7 +2487,7 @@ static PyStructSequence_Desc flags_desc = {
"sys.flags", /* name */ "sys.flags", /* name */
flags__doc__, /* doc */ flags__doc__, /* doc */
flags_fields, /* fields */ flags_fields, /* fields */
16 17
}; };
static int static int
@ -2526,6 +2527,7 @@ set_flags_from_config(PyInterpreterState *interp, PyObject *flags)
SetFlagObj(PyBool_FromLong(config->dev_mode)); SetFlagObj(PyBool_FromLong(config->dev_mode));
SetFlag(preconfig->utf8_mode); SetFlag(preconfig->utf8_mode);
SetFlag(config->warn_default_encoding); SetFlag(config->warn_default_encoding);
SetFlagObj(PyBool_FromLong(config->safe_path));
#undef SetFlagObj #undef SetFlagObj
#undef SetFlag #undef SetFlag
return 0; return 0;