Skip to content

Commit

Permalink
safer_getattr (#282)
Browse files Browse the repository at this point in the history
* fix a possible breakout possibility in the provided safer_getattr methode in Guards

---------

Co-authored-by: Michael Howitz <[email protected]>
Co-authored-by: Dieter Maurer <[email protected]>
  • Loading branch information
3 people authored Aug 2, 2024
1 parent d191a99 commit 4eaa5fa
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Changes
- Allow to use the package with Python 3.13 -- Caution: No security
audit has been done so far.
- Add support for single mode statements / execution.
- Fix a potential breakout capability in the provided ``safer_getattr`` method
that is part of the ``safer_builtins``.


7.1 (2024-03-14)
Expand Down
2 changes: 2 additions & 0 deletions src/RestrictedPython/Guards.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,8 @@ def safer_getattr(object, name, default=None, getattr=getattr):
http://lucumr.pocoo.org/2016/12/29/careful-with-str-format/
"""
if type(name) is not str:
raise TypeError('type(name) must be str')
if name in ('format', 'format_map') and (
isinstance(object, str) or
(isinstance(object, type) and issubclass(object, str))):
Expand Down
36 changes: 36 additions & 0 deletions tests/test_Guards.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,42 @@ def test_Guards__safer_getattr__3():
assert restricted_globals['result'] == 2


SAFER_GETATTR_BREAKOUT = """\
def g(obj, name):
# create class FakeString which inherits from str
class FakeString(str):
# overload startswith() to always return false
def startswith(self, _):
return False
return getattr(obj, FakeString(name))
# call str.__class__.__base__.__subclasses__()
subclasses = g(g(g(str, "__class__"), "__base__"), "__subclasses__")()
# traverse list of subclasses until we reach the BuiltinImporter class
x = "test"
while "BuiltinImporter" not in str(x):
x = subclasses.pop()
continue
# use BuiltinImporter to import 'os' and access to a not allowed function
result = x.load_module('os').getgid()
"""


def test_Guards__safer_getattr__4():
restricted_globals = dict(
__builtins__=safe_builtins,
__name__=None,
__metaclass__=type,
# _write_=_write_,
getattr=safer_getattr,
result=None,
)

with pytest.raises(TypeError) as err:
restricted_exec(SAFER_GETATTR_BREAKOUT, restricted_globals)
assert 'type(name) must be str' == str(err.value)


def test_call_py3_builtins():
"""It should not be allowed to access global builtins in Python3."""
result = compile_restricted_exec('builtins["getattr"]')
Expand Down

0 comments on commit 4eaa5fa

Please sign in to comment.