From 2f1b1f458ac6c2fd18756b8a0c13717d3a43b3b0 Mon Sep 17 00:00:00 2001 From: dieter Date: Mon, 30 Sep 2024 11:04:35 +0200 Subject: [PATCH 1/4] new function `safer_getattr_raise` similar to `safer_getattr` but with `default` handling like `getattr` --- CHANGES.rst | 7 +++++++ src/RestrictedPython/Guards.py | 11 ++++++++++- tests/test_Guards.py | 12 ++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 10fad5c..a724f49 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -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 `_. + + 7.3 (2024-09-30) ---------------- diff --git a/src/RestrictedPython/Guards.py b/src/RestrictedPython/Guards.py index 5aa720b..ce092f0 100644 --- a/src/RestrictedPython/Guards.py +++ b/src/RestrictedPython/Guards.py @@ -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. @@ -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'. diff --git a/tests/test_Guards.py b/tests/test_Guards.py index f3db19b..18347d1 100644 --- a/tests/test_Guards.py +++ b/tests/test_Guards.py @@ -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 as ga + + o = SimpleNamespace(a="a") + assert ga(o, "a") == "a" + assert ga(o, "b", None) is None + with pytest.raises(AttributeError): + ga(o, "b") + + def test_call_py3_builtins(): """It should not be allowed to access global builtins in Python3.""" result = compile_restricted_exec('builtins["getattr"]') From a6387e9f39653d1b32d90340edec5b400ad0347c Mon Sep 17 00:00:00 2001 From: dieter Date: Wed, 2 Oct 2024 07:50:41 +0200 Subject: [PATCH 2/4] [force ci] From 4c1e240b372c1c4bf09f5f563f09768e0c45d29b Mon Sep 17 00:00:00 2001 From: dieter Date: Wed, 2 Oct 2024 07:52:18 +0200 Subject: [PATCH 3/4] force ci From eb4d5a9e8dec10185c6d7c7d34b27c64d286b6ea Mon Sep 17 00:00:00 2001 From: Dieter Maurer Date: Wed, 2 Oct 2024 08:45:47 +0200 Subject: [PATCH 4/4] Apply review suggestion from @icemac Co-authored-by: Michael Howitz --- tests/test_Guards.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_Guards.py b/tests/test_Guards.py index 18347d1..b9c5b2f 100644 --- a/tests/test_Guards.py +++ b/tests/test_Guards.py @@ -332,13 +332,13 @@ def test_Guards__safer_getattr__5(): def test_Guards__safer_getattr_raise(): from types import SimpleNamespace - from RestrictedPython.Guards import safer_getattr_raise as ga + from RestrictedPython.Guards import safer_getattr_raise o = SimpleNamespace(a="a") - assert ga(o, "a") == "a" - assert ga(o, "b", None) is None + assert safer_getattr_raise(o, "a") == "a" + assert safer_getattr_raise(o, "b", None) is None with pytest.raises(AttributeError): - ga(o, "b") + safer_getattr_raise(o, "b") def test_call_py3_builtins():