From ad73a9cf97770023665a1bb1c6390a3c99478139 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sat, 10 Aug 2013 16:36:18 +0200 Subject: [PATCH] Issue #16400: Add command line option for isolated mode. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -I Run Python in isolated mode. This also implies -E and -s. 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 malicious code. --- Doc/c-api/init.rst | 6 +++++- Doc/using/cmdline.rst | 17 +++++++++++++++-- Doc/whatsnew/3.4.rst | 3 ++- Include/pydebug.h | 1 + Lib/test/test_cmd_line.py | 26 ++++++++++++++++++++++++++ Lib/test/test_sys.py | 2 +- Misc/NEWS | 2 ++ Misc/python.man | 14 ++++++++++++-- Modules/main.c | 9 ++++++++- Python/pythonrun.c | 1 + Python/sysmodule.c | 6 ++++-- 11 files changed, 77 insertions(+), 10 deletions(-) diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index 6f847d94e16..32007d55a9e 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -329,7 +329,11 @@ Process-wide parameters .. c:function:: void PySys_SetArgv(int argc, wchar_t **argv) - This function works like :c:func:`PySys_SetArgvEx` with *updatepath* set to 1. + This function works like :c:func:`PySys_SetArgvEx` with *updatepath* set + to 1 unless the :program:`python` interpreter was started with the + :option:`-I`. + + .. versionchanged:: 3.4 The *updatepath* value depends on :option:`-I`. .. c:function:: void Py_SetPythonHome(wchar_t *home) diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst index a7273bcfee6..c14f6c76e00 100644 --- a/Doc/using/cmdline.rst +++ b/Doc/using/cmdline.rst @@ -24,7 +24,7 @@ Command line When invoking Python, you may specify any of these options:: - python [-bBdEhiOqsSuvVWx?] [-c command | -m module-name | script | - ] [args] + python [-bBdEhiIOqsSuvVWx?] [-c command | -m module-name | script | - ] [args] The most common use case is, of course, a simple invocation of a script:: @@ -175,6 +175,8 @@ Generic options Python 3.0 +.. _using-on-misc-options: + Miscellaneous options ~~~~~~~~~~~~~~~~~~~~~ @@ -213,6 +215,17 @@ Miscellaneous options raises an exception. See also :envvar:`PYTHONINSPECT`. +.. cmdoption:: -I + + Run Python in isolated mode. This also implies -E and -s. + 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 + the user from injecting malicious code. + + .. versionadded:: 3.4 + + .. cmdoption:: -O Turn on basic optimizations. This changes the filename extension for @@ -398,7 +411,7 @@ Environment variables --------------------- These environment variables influence Python's behavior, they are processed -before the command-line switches other than -E. It is customary that +before the command-line switches other than -E or -I. It is customary that command-line switches override environmental variables where there is a conflict. diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst index 6011f8e30cb..0333c111301 100644 --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -107,7 +107,8 @@ Significantly Improved Library Modules: Security improvements: -* None yet. +* command line option for :ref:`isolated mode `, + :issue:`16499`. Please read on for a comprehensive list of user-facing changes. diff --git a/Include/pydebug.h b/Include/pydebug.h index 97c2f8c4257..8fe98183f9e 100644 --- a/Include/pydebug.h +++ b/Include/pydebug.h @@ -20,6 +20,7 @@ PyAPI_DATA(int) Py_DontWriteBytecodeFlag; PyAPI_DATA(int) Py_NoUserSiteDirectory; PyAPI_DATA(int) Py_UnbufferedStdioFlag; PyAPI_DATA(int) Py_HashRandomizationFlag; +PyAPI_DATA(int) Py_IsolatedFlag; /* this is a wrapper around getenv() that pays attention to Py_IgnoreEnvironmentFlag. It should be used for getting variables like diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py index 3fa8ade4668..efe45469ebc 100644 --- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py @@ -4,6 +4,7 @@ import test.support, unittest import os +import shutil import sys import subprocess import tempfile @@ -439,6 +440,31 @@ class CmdLineTest(unittest.TestCase): self.assertEqual(b'', out) + def test_isolatedmode(self): + self.verify_valid_flag('-I') + self.verify_valid_flag('-IEs') + rc, out, err = assert_python_ok('-I', '-c', + 'from sys import flags as f; ' + 'print(f.no_user_site, f.ignore_environment, f.isolated)', + # dummyvar to prevent extranous -E + dummyvar="") + self.assertEqual(out.strip(), b'1 1 1') + with test.support.temp_cwd() as tmpdir: + fake = os.path.join(tmpdir, "uuid.py") + main = os.path.join(tmpdir, "main.py") + with open(fake, "w") as f: + f.write("raise RuntimeError('isolated mode test')\n") + with open(main, "w") as f: + f.write("import uuid\n") + f.write("print('ok')\n") + self.assertRaises(subprocess.CalledProcessError, + subprocess.check_output, + [sys.executable, main], cwd=tmpdir, + stderr=subprocess.DEVNULL) + out = subprocess.check_output([sys.executable, "-I", main], + cwd=tmpdir) + self.assertEqual(out.strip(), b"ok") + def test_main(): test.support.run_unittest(CmdLineTest) test.support.reap_children() diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index e31bbc2215e..65cb8ef2aff 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -515,7 +515,7 @@ class SysModuleTest(unittest.TestCase): attrs = ("debug", "inspect", "interactive", "optimize", "dont_write_bytecode", "no_user_site", "no_site", "ignore_environment", "verbose", - "bytes_warning", "quiet", "hash_randomization") + "bytes_warning", "quiet", "hash_randomization", "isolated") for attr in attrs: self.assertTrue(hasattr(sys.flags, attr), attr) self.assertEqual(type(getattr(sys.flags, attr)), int, attr) diff --git a/Misc/NEWS b/Misc/NEWS index b0e392f8ea0..e7efb036582 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,8 @@ Projected Release date: 2013-09-08 Core and Builtins ----------------- +- Issue #16400: Add command line option for isolated mode. + - Issue #15301: Parsing fd, uid, and gid parameters for builtins in Modules/posixmodule.c is now far more robust. diff --git a/Misc/python.man b/Misc/python.man index bcb3ec87233..52aedee5bf0 100644 --- a/Misc/python.man +++ b/Misc/python.man @@ -26,11 +26,14 @@ python \- an interpreted, interactive, object-oriented programming language .B \-i ] [ -.B \-m -.I module-name +.B \-I ] .br [ +.B \-m +.I module-name +] +[ .B \-q ] [ @@ -139,6 +142,13 @@ command. It does not read the $PYTHONSTARTUP file. This can be 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 +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 +malicious code. +.TP .BI "\-m " module-name Searches .I sys.path diff --git a/Modules/main.c b/Modules/main.c index 435bd1bfcb2..25190b8e7fa 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -43,7 +43,7 @@ static wchar_t **orig_argv; static int orig_argc; /* command line options */ -#define BASE_OPTS L"bBc:dEhiJm:OqRsStuvVW:xX:?" +#define BASE_OPTS L"bBc:dEhiIJm:OqRsStuvVW:xX:?" #define PROGRAM_OPTS BASE_OPTS @@ -65,6 +65,7 @@ Options and arguments (and corresponding environment variables):\n\ static char *usage_2 = "\ -i : inspect interactively after running script; forces a prompt even\n\ if stdin does not appear to be a terminal; also PYTHONINSPECT=x\n\ +-I : isolate Python from the user's environment (implies -E and -s)\n\ -m mod : run library module as a script (terminates option list)\n\ -O : optimize generated bytecode slightly; also PYTHONOPTIMIZE=x\n\ -OO : remove doc-strings in addition to the -O optimizations\n\ @@ -426,6 +427,12 @@ Py_Main(int argc, wchar_t **argv) Py_InteractiveFlag++; break; + case 'I': + Py_IsolatedFlag++; + Py_NoUserSiteDirectory++; + Py_IgnoreEnvironmentFlag++; + break; + /* case 'J': reserved for Jython */ case 'O': diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 86c32068297..b0bc5496471 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -112,6 +112,7 @@ int Py_IgnoreEnvironmentFlag; /* e.g. PYTHONPATH, PYTHONHOME */ int Py_NoUserSiteDirectory = 0; /* for -s and site.py */ int Py_UnbufferedStdioFlag = 0; /* Unbuffered binary std{in,out,err} */ int Py_HashRandomizationFlag = 0; /* for -R and PYTHONHASHSEED */ +int Py_IsolatedFlag = 0; /* for -I, isolate from user's env */ PyThreadState *_Py_Finalizing = NULL; diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 754bcfe6541..72004f8a14a 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1369,6 +1369,7 @@ static PyStructSequence_Field flags_fields[] = { {"bytes_warning", "-b"}, {"quiet", "-q"}, {"hash_randomization", "-R"}, + {"isolated", "-I"}, {0} }; @@ -1376,7 +1377,7 @@ static PyStructSequence_Desc flags_desc = { "sys.flags", /* name */ flags__doc__, /* doc */ flags_fields, /* fields */ - 12 + 13 }; static PyObject* @@ -1406,6 +1407,7 @@ make_flags(void) SetFlag(Py_BytesWarningFlag); SetFlag(Py_QuietFlag); SetFlag(Py_HashRandomizationFlag); + SetFlag(Py_IsolatedFlag); #undef SetFlag if (PyErr_Occurred()) { @@ -1944,7 +1946,7 @@ PySys_SetArgvEx(int argc, wchar_t **argv, int updatepath) void PySys_SetArgv(int argc, wchar_t **argv) { - PySys_SetArgvEx(argc, argv, 1); + PySys_SetArgvEx(argc, argv, Py_IsolatedFlag == 0); } /* Reimplementation of PyFile_WriteString() no calling indirectly