Skip to content

Commit c8eca66

Browse files
loecheldataflake
andauthored
Merge pull request from GHSA-wqc8-x2pr-7jqh
* 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]>
1 parent b82d582 commit c8eca66

File tree

4 files changed

+99
-0
lines changed

4 files changed

+99
-0
lines changed

CHANGES.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ Features
1515
- Allow to use the package with Python 3.12 -- Caution: No security audit has
1616
been done so far.
1717

18+
Fixes
19+
+++++
20+
21+
- Restrict access to some attributes accessible via the ``inspect`` module.
22+
1823

1924
6.0 (2022-11-03)
2025
----------------

docs/contributing/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ To do so:
6464
All AST Nodes without an explicit ``visit_<AST Node>`` method, are denied by default.
6565
So the usage of this expression and functionality is not allowed.
6666

67+
* Check the documentation for `inspect <https://docs.python.org/3/library/inspect.html>`_ and adjust the ``transformer.py:INSPECT_ATTRIBUTES`` list.
6768
* Add a corresponding changelog entry.
6869
* 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:
6970

src/RestrictedPython/transformer.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,32 @@
6363
'breakpoint',
6464
])
6565

66+
# inspect attributes. See also
67+
# https://docs.python.org/3/library/inspect.html
68+
INSPECT_ATTRIBUTES = frozenset([
69+
# traceback
70+
"tb_frame",
71+
"tb_next",
72+
# code
73+
"co_code",
74+
# frame
75+
"f_back",
76+
"f_builtins",
77+
"f_code",
78+
"f_globals",
79+
"f_locals",
80+
"f_trace",
81+
# generator
82+
"gi_frame",
83+
"gi_code",
84+
"gi_yieldfrom",
85+
# coroutine
86+
"cr_await",
87+
"cr_frame",
88+
"cr_code",
89+
"cr_origin",
90+
])
91+
6692

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

873+
if node.attr in INSPECT_ATTRIBUTES:
874+
self.error(
875+
node,
876+
f'"{node.attr}" is a restricted name,'
877+
' that is forbidden to access in RestrictedPython.',
878+
)
879+
847880
if isinstance(node.ctx, ast.Load):
848881
node = self.node_contents_visit(node)
849882
new_node = ast.Call(

tests/transformer/test_inspect.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
from RestrictedPython import compile_restricted_exec
2+
3+
4+
def test_get_inspect_frame_on_generator():
5+
source_code = """
6+
generator = (statement.gi_frame for _ in (1,))
7+
generator_element = [elem for elem in generator][0]
8+
9+
"""
10+
result = compile_restricted_exec(source_code)
11+
assert result.errors == (
12+
'Line 2: "gi_frame" is a restricted name, '
13+
'that is forbidden to access in RestrictedPython.',
14+
)
15+
16+
17+
def test_get_inspect_frame_back_on_generator():
18+
source_code = """
19+
generator = (statement.gi_frame.f_back.f_back for _ in (1,))
20+
generator_element = [elem for elem in generator][0]
21+
22+
"""
23+
result = compile_restricted_exec(source_code)
24+
assert result.errors == (
25+
'Line 2: "f_back" is a restricted name, '
26+
'that is forbidden to access in RestrictedPython.',
27+
'Line 2: "f_back" is a restricted name, '
28+
'that is forbidden to access in RestrictedPython.',
29+
'Line 2: "gi_frame" is a restricted name, '
30+
'that is forbidden to access in RestrictedPython.',
31+
)
32+
33+
34+
def test_call_inspect_frame_on_generator():
35+
source_code = """
36+
generator = None
37+
frame = None
38+
39+
def test():
40+
global generator, frame
41+
frame = g.gi_frame.f_back.f_back
42+
yield frame
43+
44+
generator = test()
45+
generator.send(None)
46+
os = frame.f_builtins.get('__import__')('os')
47+
48+
result = os.listdir('/')
49+
"""
50+
result = compile_restricted_exec(source_code)
51+
assert result.errors == (
52+
'Line 7: "f_back" is a restricted name, '
53+
'that is forbidden to access in RestrictedPython.',
54+
'Line 7: "f_back" is a restricted name, '
55+
'that is forbidden to access in RestrictedPython.',
56+
'Line 7: "gi_frame" is a restricted name, '
57+
'that is forbidden to access in RestrictedPython.',
58+
'Line 12: "f_builtins" is a restricted name, '
59+
'that is forbidden to access in RestrictedPython.',
60+
)

0 commit comments

Comments
 (0)