Skip to content

Commit 4eaa5fa

Browse files
loechelicemacd-maurer
authored
safer_getattr (#282)
* 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]>
1 parent d191a99 commit 4eaa5fa

File tree

3 files changed

+40
-0
lines changed

3 files changed

+40
-0
lines changed

CHANGES.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ Changes
99
- Allow to use the package with Python 3.13 -- Caution: No security
1010
audit has been done so far.
1111
- Add support for single mode statements / execution.
12+
- Fix a potential breakout capability in the provided ``safer_getattr`` method
13+
that is part of the ``safer_builtins``.
1214

1315

1416
7.1 (2024-03-14)

src/RestrictedPython/Guards.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,8 @@ def safer_getattr(object, name, default=None, getattr=getattr):
246246
http://lucumr.pocoo.org/2016/12/29/careful-with-str-format/
247247
248248
"""
249+
if type(name) is not str:
250+
raise TypeError('type(name) must be str')
249251
if name in ('format', 'format_map') and (
250252
isinstance(object, str) or
251253
(isinstance(object, type) and issubclass(object, str))):

tests/test_Guards.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,42 @@ def test_Guards__safer_getattr__3():
259259
assert restricted_globals['result'] == 2
260260

261261

262+
SAFER_GETATTR_BREAKOUT = """\
263+
def g(obj, name):
264+
# create class FakeString which inherits from str
265+
class FakeString(str):
266+
# overload startswith() to always return false
267+
def startswith(self, _):
268+
return False
269+
return getattr(obj, FakeString(name))
270+
271+
# call str.__class__.__base__.__subclasses__()
272+
subclasses = g(g(g(str, "__class__"), "__base__"), "__subclasses__")()
273+
# traverse list of subclasses until we reach the BuiltinImporter class
274+
x = "test"
275+
while "BuiltinImporter" not in str(x):
276+
x = subclasses.pop()
277+
continue
278+
# use BuiltinImporter to import 'os' and access to a not allowed function
279+
result = x.load_module('os').getgid()
280+
"""
281+
282+
283+
def test_Guards__safer_getattr__4():
284+
restricted_globals = dict(
285+
__builtins__=safe_builtins,
286+
__name__=None,
287+
__metaclass__=type,
288+
# _write_=_write_,
289+
getattr=safer_getattr,
290+
result=None,
291+
)
292+
293+
with pytest.raises(TypeError) as err:
294+
restricted_exec(SAFER_GETATTR_BREAKOUT, restricted_globals)
295+
assert 'type(name) must be str' == str(err.value)
296+
297+
262298
def test_call_py3_builtins():
263299
"""It should not be allowed to access global builtins in Python3."""
264300
result = compile_restricted_exec('builtins["getattr"]')

0 commit comments

Comments
 (0)