Skip to content

Commit 57a2acb

Browse files
loecheld-maurericemac
authored
additional check for safer_getattr (#285)
* add an additional check for potential breakout capability via Inspection Attributes Names in the provided safer_getattr method. --------- Co-authored-by: Dieter Maurer <[email protected]> Co-authored-by: Michael Howitz <[email protected]>
1 parent d0e97cd commit 57a2acb

File tree

3 files changed

+43
-0
lines changed

3 files changed

+43
-0
lines changed

CHANGES.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ Changes
66

77
- Allow to use the package with Python 3.13 -- Caution: No security
88
audit has been done so far.
9+
- Increase the safety level of ``safer_getattr`` allowing applications to use
10+
it as ``getattr`` implementation. Such use should now follow the same policy
11+
and give the same level of protection as direct attribute access in an
12+
environment based on ``RestrictedPython``'s ``safe_builtints``.
913

1014

1115
7.2 (2024-08-02)

src/RestrictedPython/Guards.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import builtins
1919

2020
from RestrictedPython._compat import IS_PY311_OR_GREATER
21+
from RestrictedPython.transformer import INSPECT_ATTRIBUTES
2122

2223

2324
safe_builtins = {}
@@ -253,6 +254,10 @@ def safer_getattr(object, name, default=None, getattr=getattr):
253254
(isinstance(object, type) and issubclass(object, str))):
254255
raise NotImplementedError(
255256
'Using the format*() methods of `str` is not safe')
257+
if name in INSPECT_ATTRIBUTES:
258+
raise AttributeError(
259+
f'"{name}" is a restricted name,'
260+
' that is forbidden to access in RestrictedPython.')
256261
if name.startswith('_'):
257262
raise AttributeError(
258263
'"{name}" is an invalid attribute name because it '

tests/test_Guards.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,40 @@ def test_Guards__safer_getattr__4():
295295
assert 'type(name) must be str' == str(err.value)
296296

297297

298+
SAFER_GETATTR_BREAKOUT2 = """\
299+
g = None
300+
leak = None
301+
def test():
302+
global g, leak
303+
leak = getattr(getattr(getattr(g, "gi_frame"), "f_back"), "f_back")
304+
yield leak
305+
g = test()
306+
g.send(None)
307+
os = getattr(leak, "f_builtins").get('__import__')('os')
308+
result = os.getgid()
309+
"""
310+
311+
312+
def test_Guards__safer_getattr__5():
313+
restricted_globals = dict(
314+
__builtins__=safe_builtins,
315+
__name__=None,
316+
__metaclass__=type,
317+
# _write_=_write_,
318+
getattr=safer_getattr,
319+
result=None,
320+
)
321+
322+
# restricted_exec(SAFER_GETATTR_BREAKOUT2, restricted_globals)
323+
# assert restricted_globals['result'] == 20
324+
with pytest.raises(AttributeError) as err:
325+
restricted_exec(SAFER_GETATTR_BREAKOUT2, restricted_globals)
326+
assert (
327+
'"gi_frame" is a restricted name, '
328+
'that is forbidden to access in RestrictedPython.'
329+
) == str(err.value)
330+
331+
298332
def test_call_py3_builtins():
299333
"""It should not be allowed to access global builtins in Python3."""
300334
result = compile_restricted_exec('builtins["getattr"]')

0 commit comments

Comments
 (0)