Skip to content

Commit

Permalink
new function safer_getattr_raise (#288)
Browse files Browse the repository at this point in the history
* new function `safer_getattr_raise` similar to `safer_getattr` but with `default` handling like `getattr`

* [force ci]

* force ci

* Apply review suggestion from @icemac

Co-authored-by: Michael Howitz <[email protected]>

---------

Co-authored-by: Michael Howitz <[email protected]>
  • Loading branch information
d-maurer and icemac authored Oct 9, 2024
1 parent 95f323c commit 2f8f153
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 1 deletion.
7 changes: 7 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ Changes
- Allow to use the package with Python 3.13 -- Caution: No security
audit has been done so far.

- Provide new function ``RestrictedPython.Guards.safer_getattr_raise``.
It is similar to ``safer_getattr`` but handles its parameter
``default`` like ``getattr``, i.e. it raises ``AttributeError``
if the attribute lookup fails and this parameter is not provided,
fixes `#287 <https://github.com/zopefoundation/RestrictedPython/issues/287>`_.



7.3 (2024-09-30)
----------------
Expand Down
11 changes: 10 additions & 1 deletion src/RestrictedPython/Guards.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,9 @@ def guarded_delattr(object, name):
safe_builtins['delattr'] = guarded_delattr


raise_ = object()


def safer_getattr(object, name, default=None, getattr=getattr):
"""Getattr implementation which prevents using format on string objects.
Expand All @@ -263,12 +266,18 @@ def safer_getattr(object, name, default=None, getattr=getattr):
'"{name}" is an invalid attribute name because it '
'starts with "_"'.format(name=name)
)
return getattr(object, name, default)
args = (object, name) + (() if default is raise_ else (default,))
return getattr(*args)


safe_builtins['_getattr_'] = safer_getattr


def safer_getattr_raise(object, name, default=raise_):
"""like ``safer_getattr`` but raising ``AttributeError`` if failing."""
return safer_getattr(object, name, default)


def guarded_iter_unpack_sequence(it, spec, _getiter_):
"""Protect sequence unpacking of targets in a 'for loop'.
Expand Down
12 changes: 12 additions & 0 deletions tests/test_Guards.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,18 @@ def test_Guards__safer_getattr__5():
) == str(err.value)


def test_Guards__safer_getattr_raise():
from types import SimpleNamespace

from RestrictedPython.Guards import safer_getattr_raise

o = SimpleNamespace(a="a")
assert safer_getattr_raise(o, "a") == "a"
assert safer_getattr_raise(o, "b", None) is None
with pytest.raises(AttributeError):
safer_getattr_raise(o, "b")


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 2f8f153

Please sign in to comment.