Skip to content

Commit

Permalink
Merge pull request from GHSA-wqc8-x2pr-7jqh
Browse files Browse the repository at this point in the history
* move the commented fix into this branch

* more verbose infos, and linting

* 3 tests for generators

* - add change log entry

---------

Co-authored-by: Jens Vagelpohl <[email protected]>
  • Loading branch information
loechel and dataflake authored Jul 8, 2023
1 parent b82d582 commit c8eca66
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 0 deletions.
5 changes: 5 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ Features
- Allow to use the package with Python 3.12 -- Caution: No security audit has
been done so far.

Fixes
+++++

- Restrict access to some attributes accessible via the ``inspect`` module.


6.0 (2022-11-03)
----------------
Expand Down
1 change: 1 addition & 0 deletions docs/contributing/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ To do so:
All AST Nodes without an explicit ``visit_<AST Node>`` method, are denied by default.
So the usage of this expression and functionality is not allowed.

* Check the documentation for `inspect <https://docs.python.org/3/library/inspect.html>`_ and adjust the ``transformer.py:INSPECT_ATTRIBUTES`` list.
* Add a corresponding changelog entry.
* Additionally modify ``.meta.toml`` and run the ``meta/config`` script (for details see: https://github.com/mgedmin/check-python-versions) to update the following files:

Expand Down
33 changes: 33 additions & 0 deletions src/RestrictedPython/transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,32 @@
'breakpoint',
])

# inspect attributes. See also
# https://docs.python.org/3/library/inspect.html
INSPECT_ATTRIBUTES = frozenset([
# traceback
"tb_frame",
"tb_next",
# code
"co_code",
# frame
"f_back",
"f_builtins",
"f_code",
"f_globals",
"f_locals",
"f_trace",
# generator
"gi_frame",
"gi_code",
"gi_yieldfrom",
# coroutine
"cr_await",
"cr_frame",
"cr_code",
"cr_origin",
])


# When new ast nodes are generated they have no 'lineno', 'end_lineno',
# 'col_offset' and 'end_col_offset'. This function copies these fields from the
Expand Down Expand Up @@ -844,6 +870,13 @@ def visit_Attribute(self, node):
'"{name}" is an invalid attribute name because it ends '
'with "__roles__".'.format(name=node.attr))

if node.attr in INSPECT_ATTRIBUTES:
self.error(
node,
f'"{node.attr}" is a restricted name,'
' that is forbidden to access in RestrictedPython.',
)

if isinstance(node.ctx, ast.Load):
node = self.node_contents_visit(node)
new_node = ast.Call(
Expand Down
60 changes: 60 additions & 0 deletions tests/transformer/test_inspect.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from RestrictedPython import compile_restricted_exec


def test_get_inspect_frame_on_generator():
source_code = """
generator = (statement.gi_frame for _ in (1,))
generator_element = [elem for elem in generator][0]
"""
result = compile_restricted_exec(source_code)
assert result.errors == (
'Line 2: "gi_frame" is a restricted name, '
'that is forbidden to access in RestrictedPython.',
)


def test_get_inspect_frame_back_on_generator():
source_code = """
generator = (statement.gi_frame.f_back.f_back for _ in (1,))
generator_element = [elem for elem in generator][0]
"""
result = compile_restricted_exec(source_code)
assert result.errors == (
'Line 2: "f_back" is a restricted name, '
'that is forbidden to access in RestrictedPython.',
'Line 2: "f_back" is a restricted name, '
'that is forbidden to access in RestrictedPython.',
'Line 2: "gi_frame" is a restricted name, '
'that is forbidden to access in RestrictedPython.',
)


def test_call_inspect_frame_on_generator():
source_code = """
generator = None
frame = None
def test():
global generator, frame
frame = g.gi_frame.f_back.f_back
yield frame
generator = test()
generator.send(None)
os = frame.f_builtins.get('__import__')('os')
result = os.listdir('/')
"""
result = compile_restricted_exec(source_code)
assert result.errors == (
'Line 7: "f_back" is a restricted name, '
'that is forbidden to access in RestrictedPython.',
'Line 7: "f_back" is a restricted name, '
'that is forbidden to access in RestrictedPython.',
'Line 7: "gi_frame" is a restricted name, '
'that is forbidden to access in RestrictedPython.',
'Line 12: "f_builtins" is a restricted name, '
'that is forbidden to access in RestrictedPython.',
)

0 comments on commit c8eca66

Please sign in to comment.