diff --git a/README.rst b/README.rst index cf9a47b..2c4a7ce 100644 --- a/README.rst +++ b/README.rst @@ -22,6 +22,8 @@ An unproblematic code example Python allows you to execute a large set of commands. This would not harm any system. +.. code-block:: pycon + >>> from RestrictedPython import compile_restricted >>> from RestrictedPython import safe_builtins >>> @@ -42,15 +44,17 @@ Problematic code example This example directly executed in Python could harm your system. - >>> from RestrictedPython import compile_restricted - >>> from RestrictedPython import safe_builtins - >>> - >>> source_code = """ - ... import os - ... - ... os.listdir('/') - ... """ - >>> byte_code = compile_restricted(source_code, '', 'exec') - >>> exec(byte_code, {'__builtins__': safe_builtins}, {}) - Traceback (most recent call last): - ImportError: __import__ not found +.. code-block:: pycon + + >>> from RestrictedPython import compile_restricted + >>> from RestrictedPython import safe_builtins + >>> + >>> source_code = """ + ... import os + ... + ... os.listdir('/') + ... """ + >>> byte_code = compile_restricted(source_code, '', 'exec') + >>> exec(byte_code, {'__builtins__': safe_builtins}, {}) + Traceback (most recent call last): + ImportError: __import__ not found diff --git a/docs/CHANGES.rst b/docs/CHANGES.rst index 9af2e61..7c789f0 100644 --- a/docs/CHANGES.rst +++ b/docs/CHANGES.rst @@ -15,7 +15,6 @@ Changes - Fix regression in ``RestrictionCapableEval`` which broke when using list comprehensions. - 4.0b1 (2017-09-15) ------------------ @@ -39,7 +38,6 @@ Changes - Remove ``__len__`` method in ``.Guards._write_wrapper`` because it is no longer reachable by code using the wrapper. - 4.0a3 (2017-06-20) ------------------ @@ -48,8 +46,6 @@ Changes - Update configurations to give better feedback and helpful reports. - - 4.0a2 (2017-05-26) ------------------ @@ -59,7 +55,6 @@ Changes - Drop support for long-deprecated ``sets`` module. [tseaver] - 4.0a1 (2017-05-05) ------------------ @@ -73,7 +68,6 @@ Changes - The ``compile_restricted*`` functions now return a ``namedtuple CompileResult`` instead of a simple ``tuple``. - 3.6.0 (2010-07-09) ------------------ diff --git a/docs/RestrictedPython3/index.rst b/docs/RestrictedPython3/index.rst index 21f37ef..508690d 100644 --- a/docs/RestrictedPython3/index.rst +++ b/docs/RestrictedPython3/index.rst @@ -1,7 +1,6 @@ RestrictedPython 3.6.x and before ================================= - Technical foundation of RestrictedPython ........................................ @@ -14,7 +13,6 @@ RestrictedPython based on the With Python 2.6 the compiler module with all its sub modules has been declared deprecated with no direct upgrade Path or recommendations for a replacement. - Version Support of RestrictedPython 3.6.x ......................................... diff --git a/docs/api/index.rst b/docs/api/index.rst index d0a5d2d..cdcae19 100644 --- a/docs/api/index.rst +++ b/docs/api/index.rst @@ -1,24 +1,22 @@ API of RestrictedPython 4.0 =========================== -.. code:: Python +.. code-block:: python compile_restricted(source, filename, mode [, flags [, dont_inherit]]) -.. code:: Python +.. code-block:: python compile_restricted_exec(source, filename, mode [, flags [, dont_inherit [, policy]]]) -.. code:: Python +.. code-block:: python compile_restricted_eval(source, filename, mode [, flags [, dont_inherit [, policy]]]) - -.. code:: Python +.. code-block:: python compile_restricted_single(source, filename, mode [, flags [, dont_inherit [, policy]]]) - -.. code:: Python +.. code-block:: python compile_restricted_function(source, filename, mode [, flags [, dont_inherit [, policy]]]) diff --git a/docs/basics/index.rst b/docs/basics/index.rst index 5aaff75..82a217b 100644 --- a/docs/basics/index.rst +++ b/docs/basics/index.rst @@ -1,7 +1,6 @@ Grundlagen von RestrictedPython und der Sicherheitskonzepte von Zope2 ===================================================================== - Motivation für RestrictedPython ------------------------------- @@ -36,14 +35,9 @@ Die zentrale Frage ist: Vertraue ich der Software, bzw. den Entwicklern der Software und führe diese aus. - - - Python ist eine Turing-vollständige Prgrammiersprache. Somit haben Entwickler grundsätzlich erstmal keine Limitierungen beim programmieren. - - und können somit potentiell die Applikation und den Server selber schaden. RestrictedPython und AccessControl zielen auf diese Besonderheit und versuchen einen reduzierten Subset der Programmiersprache Python zur verfügung zu stellen. diff --git a/docs/contributing/index.rst b/docs/contributing/index.rst index 62c3fb3..7ffccd7 100644 --- a/docs/contributing/index.rst +++ b/docs/contributing/index.rst @@ -3,8 +3,6 @@ Contributing Contributing to RestrictedPython 4+ - - Todos ----- diff --git a/docs/idea.rst b/docs/idea.rst index c100b84..9cbe982 100644 --- a/docs/idea.rst +++ b/docs/idea.rst @@ -28,7 +28,7 @@ Python itself offers three methods that provide such a workflow: Therefore RestrictedPython offers a replacement for the python builtin function ``compile()`` (Python 2: https://docs.python.org/2/library/functions.html#compile / Python 3 https://docs.python.org/3/library/functions.html#compile). This method is defined as following: -.. code:: Python +.. code-block:: python compile(source, filename, mode [, flags [, dont_inherit]]) @@ -42,7 +42,7 @@ There are three valid string values for ``mode``: For RestrictedPython this ``compile()`` method is replaced by: -.. code:: Python +.. code-block:: python compile_restricted(source, filename, mode [, flags [, dont_inherit]]) diff --git a/docs/install/index.rst b/docs/install/index.rst index 0286941..9d71eec 100644 --- a/docs/install/index.rst +++ b/docs/install/index.rst @@ -5,6 +5,6 @@ RestrictedPython is usually not used stand alone, if you use it in context of yo For a standalone usage: -.. code:: bash +.. code-block:: console - pip install RestrictedPython + $ pip install RestrictedPython diff --git a/docs/roadmap/index.rst b/docs/roadmap/index.rst index 3fd98aa..a854496 100644 --- a/docs/roadmap/index.rst +++ b/docs/roadmap/index.rst @@ -26,7 +26,6 @@ Full code coverage tests. stephan-hof did propose a solution, should be discussed and if approved implemented. - RestrictedPython 4.1+ --------------------- diff --git a/docs/upgrade/index.rst b/docs/upgrade/index.rst index 9ce393e..ba35ef1 100644 --- a/docs/upgrade/index.rst +++ b/docs/upgrade/index.rst @@ -30,7 +30,6 @@ The ``compiler`` module predates several major improvements of the Python develo While ``compiler`` still uses the old `CamelCase`_ Syntax (``visitNode(self, node, walker)``) the ``ast.AST`` did now use the Python common ``visit_Node(self, node)`` syntax. Also the names of classes have been changed, where ``compiler`` uses ``Walker`` and ``Mutator`` the corresponding elements in ``ast.AST`` are ``NodeVisitor`` and ``NodeTransformator``. - ``ast`` module (Abstract Syntax Trees) -------------------------------------- @@ -48,7 +47,6 @@ The ``ast`` module consists of four areas: * ``PyCF_ONLY_AST`` - ``NodeVisitor`` & ``NodeTransformer`` ..................................... @@ -58,15 +56,8 @@ In contrast, a ``NodeTransformer`` (which inherits from a ``NodeVisitor``) is al Modifying the AST ----------------- - - - - - - - Technical Backgrounds - Links to External Documentation ---------------------------------------------------------- +....................................................... * Concept of Immutable Types and Python Example (https://en.wikipedia.org/wiki/Immutable_object#Python) * Python 3 Standard Library Documentation on AST module ``ast`` (https://docs.python.org/3/library/ast.html) diff --git a/docs/usage/api.rst b/docs/usage/api.rst index 5e9fe91..dc80ed7 100644 --- a/docs/usage/api.rst +++ b/docs/usage/api.rst @@ -128,7 +128,6 @@ RestrictedPython has tree major scopes: ... compiled_function.__defaults__ or ()) >>> result = new_function(*[], **{}) - 2. restricted builtins * ``safe_builtins`` diff --git a/docs/usage/basic_usage.rst b/docs/usage/basic_usage.rst index c7beff7..2ed7d67 100644 --- a/docs/usage/basic_usage.rst +++ b/docs/usage/basic_usage.rst @@ -25,9 +25,11 @@ With RestrictedPython that workflow should be as straight forward as possible: pass """ - byte_code = compile_restricted(source_code, - filename='', - mode='exec') + byte_code = compile_restricted( + source_code, + filename='', + mode='exec' + ) exec(byte_code) do_something() @@ -51,7 +53,7 @@ restrict the access to the available library modules and methods. Providing defined dictionaries for ``exec()`` should be used in context of RestrictedPython. -.. code:: Python +.. code-block:: python byte_code = exec(byte_code, { ... }, { ... }) @@ -79,10 +81,11 @@ So you normally end up using: """ try: - byte_code = compile_restricted(source_code, - filename='', - mode='exec') - + byte_code = compile_restricted( + source_code, + filename='', + mode='exec' + ) exec(byte_code, safe_builtins, None) except SyntaxError as e: pass diff --git a/docs/usage/framework_usage.rst b/docs/usage/framework_usage.rst index 0711d8a..6c78a34 100644 --- a/docs/usage/framework_usage.rst +++ b/docs/usage/framework_usage.rst @@ -43,11 +43,13 @@ A policy is basically a special ``NodeTransformer`` that could be instantiated w class OwnRestrictingNodeTransformer(RestrictingNodeTransformer): pass - policy_instance = OwnRestrictingNodeTransformer(errors=[], - warnings=[], - used_names=[]) + policy_instance = OwnRestrictingNodeTransformer( + errors=[], + warnings=[], + used_names=[] + ) -All ``compile_restricted*`` methods do have a optional parameter ``policy``, where a specific policy could be provided. +All ``compile_restricted*`` methods do have an optional parameter ``policy``, where a specific policy could be provided. .. testcode:: own_policy @@ -58,11 +60,12 @@ All ``compile_restricted*`` methods do have a optional parameter ``policy``, whe policy = OwnRestrictingNodeTransformer - byte_code = compile_restricted(source_code, - filename='', - mode='exec', - policy=policy # Policy Class - ) + byte_code = compile_restricted( + source_code, + filename='', + mode='exec', + policy=policy # Policy Class + ) exec(byte_code, globals(), None) One special case "unrestricted RestrictedPython" (defined to unblock ports of Zope Packages to Python 3) is to actually use RestrictedPython in an unrestricted mode, by providing a Null-Policy (aka ``None``). @@ -77,9 +80,10 @@ That special case would be written as: pass """ - byte_code = compile_restricted(source_code, - filename='', - mode='exec', - policy=None # Null-Policy -> unrestricted - ) + byte_code = compile_restricted( + source_code, + filename='', + mode='exec', + policy=None # Null-Policy -> unrestricted + ) exec(byte_code, globals(), None) diff --git a/docs/usage/policy.rst b/docs/usage/policy.rst index 245ebc7..0708653 100644 --- a/docs/usage/policy.rst +++ b/docs/usage/policy.rst @@ -8,7 +8,6 @@ Policies & builtins Should be described in detail. Especially the difference between builtins and a policy which is a NodeTransformer. - RestrictedPython provides a way to define Policies, by redefining restricted versions of ``print``, ``getattr``, ``setattr``, ``import``, etc.. As shortcuts it offers three stripped down versions of Pythons ``__builtins__``: diff --git a/setup.py b/setup.py index 06cb2a2..637b154 100644 --- a/setup.py +++ b/setup.py @@ -20,7 +20,8 @@ def read(*rnames): - return open(os.path.join(os.path.dirname(__file__), *rnames)).read() + with open(os.path.join(os.path.dirname(__file__), *rnames)) as f: + return f.read() tests_require = [ @@ -29,52 +30,55 @@ def read(*rnames): ] -setup(name='RestrictedPython', - version='4.0b3.dev0', - url='http://pypi.python.org/pypi/RestrictedPython', - license='ZPL 2.1', - description='RestrictedPython is a defined subset of the Python ' - 'language which allows to provide a program input into ' - 'a trusted environment.', - long_description=(read('README.rst') + '\n' + - read('docs', 'CHANGES.rst')), - classifiers=[ - 'License :: OSI Approved :: Zope Public License', - 'Programming Language :: Python', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: Implementation :: CPython', - 'Topic :: Security', - ], - keywords='restricted execution security untrusted code', - author='Zope Foundation and Contributors', - author_email='zope-dev@zope.org', - packages=find_packages('src'), - package_dir={'': 'src'}, - install_requires=[ - 'setuptools', - - ], - setup_requires=[ - 'pytest-runner', - ], - tests_require=tests_require, - extras_require={ - 'docs': [ - 'Sphinx', - ], - 'test': tests_require, - 'release': [ - 'zest.releaser', - ], - 'develop': [ - 'pdbpp', - 'isort', - ], - }, - include_package_data=True, - zip_safe=False, - ) +setup( + name='RestrictedPython', + version='4.0b3.dev0', + url='http://pypi.python.org/pypi/RestrictedPython', + license='ZPL 2.1', + description=( + 'RestrictedPython is a defined subset of the Python language which ' + 'allows to provide a program input into a trusted environment.' + ), + long_description=( + read('README.rst') + '\n' + + read('docs', 'CHANGES.rst') + ), + classifiers=[ + 'License :: OSI Approved :: Zope Public License', + 'Programming Language :: Python', + 'Operating System :: OS Independent', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: Implementation :: CPython', + 'Topic :: Security', + ], + keywords='restricted execution security untrusted code', + author='Zope Foundation and Contributors', + author_email='zope-dev@zope.org', + packages=find_packages('src'), + package_dir={'': 'src'}, + install_requires=[ + 'setuptools', + ], + setup_requires=[ + 'pytest-runner', + ], + tests_require=tests_require, + extras_require={ + 'docs': [ + 'Sphinx', + ], + 'test': tests_require, + 'release': [ + 'zest.releaser', + ], + 'develop': [ + 'pdbpp', + 'isort', + ], + }, + include_package_data=True, + zip_safe=False +) diff --git a/src/RestrictedPython/README.rst b/src/RestrictedPython/README.rst index 4ccfc8e..baea238 100644 --- a/src/RestrictedPython/README.rst +++ b/src/RestrictedPython/README.rst @@ -7,22 +7,28 @@ RestrictedPython provides a ``restricted_compile`` function that works like the built-in ``compile`` function, except that it allows the controlled and restricted execution of code: - >>> src = ''' - ... def hello_world(): - ... return "Hello World!" - ... ''' - >>> from RestrictedPython import compile_restricted - >>> code = compile_restricted(src, '', 'exec') +.. code-block:: pycon + + >>> src = ''' + ... def hello_world(): + ... return "Hello World!" + ... ''' + >>> from RestrictedPython import compile_restricted + >>> code = compile_restricted(src, '', 'exec') The resulting code can be executed using the ``exec`` built-in: - >>> exec(code) +.. code-block:: pycon + + >>> exec(code) As a result, the ``hello_world`` function is now available in the global namespace: - >>> hello_world() - 'Hello World!' +.. code-block:: pycon + + >>> hello_world() + 'Hello World!' Compatibility ============= @@ -33,7 +39,8 @@ Implementing a policy ===================== RestrictedPython only provides the raw material for restricted execution. -To actually enforce any restrictions, you need to supply a policy implementation by providing restricted versions of ``print``, +To actually enforce any restrictions, you need to supply a policy +implementation by providing restricted versions of ``print``, ``getattr``, ``setattr``, ``import``, etc. These restricted implementations are hooked up by providing a set of specially named objects in the global dict that you use for execution of code. @@ -67,26 +74,30 @@ Specifically: ``RestrictedPython.Guards.safe_builtins``. To help illustrate how this works under the covers, here's an example -function:: +function: + +.. code-block:: python + + def f(x): + x.foo = x.foo + x[0] + print x + return printed - def f(x): - x.foo = x.foo + x[0] - print x - return printed +and (sort of) how it looks after restricted compilation: -and (sort of) how it looks after restricted compilation:: +.. code-block:: python - def f(x): - # Make local variables from globals. - _print = _print_() - _write = _write_ - _getattr = _getattr_ - _getitem = _getitem_ + def f(x): + # Make local variables from globals. + _print = _print_() + _write = _write_ + _getattr = _getattr_ + _getitem = _getitem_ - # Translation of f(x) above - _write(x).foo = _getattr(x, 'foo') + _getitem(x, 0) - print >>_print, x - return _print() + # Translation of f(x) above + _write(x).foo = _getattr(x, 'foo') + _getitem(x, 0) + print >>_print, x + return _print() Examples ======== @@ -98,29 +109,33 @@ To support the ``print`` statement in restricted code, we supply a ``_print_`` object (note that it's a *factory*, e.g. a class or a callable, from which the restricted machinery will create the object): - >>> from RestrictedPython.PrintCollector import PrintCollector - >>> _print_ = PrintCollector - >>> _getattr_ = getattr +.. code-block:: pycon - >>> src = ''' - ... print("Hello World!") - ... ''' - >>> code = compile_restricted(src, '', 'exec') - >>> exec(code) + >>> from RestrictedPython.PrintCollector import PrintCollector + >>> _print_ = PrintCollector + >>> _getattr_ = getattr + + >>> src = ''' + ... print("Hello World!") + ... ''' + >>> code = compile_restricted(src, '', 'exec') + >>> exec(code) As you can see, the text doesn't appear on stdout. The print collector collects it. We can have access to the text using the ``printed`` variable, though: - >>> src = ''' - ... print("Hello World!") - ... result = printed - ... ''' - >>> code = compile_restricted(src, '', 'exec') - >>> exec(code) +.. code-block:: pycon + + >>> src = ''' + ... print("Hello World!") + ... result = printed + ... ''' + >>> code = compile_restricted(src, '', 'exec') + >>> exec(code) - >>> result - 'Hello World!\n' + >>> result + 'Hello World!\n' Built-ins --------- @@ -128,17 +143,19 @@ Built-ins By supplying a different ``__builtins__`` dictionary, we can rule out unsafe operations, such as opening files: - >>> from RestrictedPython.Guards import safe_builtins - >>> restricted_globals = dict(__builtins__ = safe_builtins) +.. code-block:: pycon - >>> src = ''' - ... open('/etc/passwd') - ... ''' - >>> code = compile_restricted(src, '', 'exec') - >>> exec(code, restricted_globals) - Traceback (most recent call last): - ... - NameError: name 'open' is not defined + >>> from RestrictedPython.Guards import safe_builtins + >>> restricted_globals = dict(__builtins__ = safe_builtins) + + >>> src = ''' + ... open('/etc/passwd') + ... ''' + >>> code = compile_restricted(src, '', 'exec') + >>> exec(code, restricted_globals) + Traceback (most recent call last): + ... + NameError: name 'open' is not defined Guards ------ @@ -147,54 +164,63 @@ Here's an example of a write guard that never lets restricted code modify (assign, delete an attribute or item) except dictionaries and lists: - >>> from RestrictedPython.Guards import full_write_guard - >>> _write_ = full_write_guard - >>> _getattr_ = getattr +.. code-block:: pycon - >>> class BikeShed(object): - ... colour = 'green' - ... - >>> shed = BikeShed() + >>> from RestrictedPython.Guards import full_write_guard + >>> _write_ = full_write_guard + >>> _getattr_ = getattr + + >>> class BikeShed(object): + ... colour = 'green' + ... + >>> shed = BikeShed() Normally accessing attriutes works as expected, because we're using the standard ``getattr`` function for the ``_getattr_`` guard: - >>> src = ''' - ... print(shed.colour) - ... result = printed - ... ''' - >>> code = compile_restricted(src, '', 'exec') - >>> exec(code) +.. code-block:: pycon - >>> result - 'green\n' + >>> src = ''' + ... print(shed.colour) + ... result = printed + ... ''' + >>> code = compile_restricted(src, '', 'exec') + >>> exec(code) + + >>> result + 'green\n' However, changing an attribute doesn't work: - >>> src = ''' - ... shed.colour = 'red' - ... ''' - >>> code = compile_restricted(src, '', 'exec') - >>> exec(code) - Traceback (most recent call last): - ... - TypeError: attribute-less object (assign or del) +.. code-block:: pycon + + >>> src = ''' + ... shed.colour = 'red' + ... ''' + >>> code = compile_restricted(src, '', 'exec') + >>> exec(code) + Traceback (most recent call last): + ... + TypeError: attribute-less object (assign or del) As said, this particular write guard (``full_write_guard``) will allow restricted code to modify lists and dictionaries: - >>> fibonacci = [1, 1, 2, 3, 4] - >>> transl = dict(one=1, two=2, tres=3) - >>> src = ''' - ... # correct mistake in list - ... fibonacci[-1] = 5 - ... # one item doesn't belong - ... del transl['tres'] - ... ''' - >>> code = compile_restricted(src, '', 'exec') - >>> exec(code) - - >>> fibonacci - [1, 1, 2, 3, 5] - >>> sorted(transl.keys()) - ['one', 'two'] +.. code-block:: pycon + + >>> fibonacci = [1, 1, 2, 3, 4] + >>> transl = dict(one=1, two=2, tres=3) + >>> src = ''' + ... # correct mistake in list + ... fibonacci[-1] = 5 + ... # one item doesn't belong + ... del transl['tres'] + ... ''' + >>> code = compile_restricted(src, '', 'exec') + >>> exec(code) + + >>> fibonacci + [1, 1, 2, 3, 5] + + >>> sorted(transl.keys()) + ['one', 'two']