Log message for revision 39645: Added protection against the (small) risk that someone could mitate an object through an augmented assignment (aka inplace) operator.
Changed: U Zope/branches/Zope-2_8-branch/lib/python/AccessControl/ZopeGuards.py U Zope/branches/Zope-2_8-branch/lib/python/AccessControl/tests/actual_python.py U Zope/branches/Zope-2_8-branch/lib/python/AccessControl/tests/testZopeGuards.py U Zope/branches/Zope-2_8-branch/lib/python/RestrictedPython/tests/before_and_after.py U Zope/branches/Zope-2_8-branch/lib/python/RestrictedPython/tests/restricted_module.py U Zope/branches/Zope-2_8-branch/lib/python/RestrictedPython/tests/security_in_syntax.py -=- Modified: Zope/branches/Zope-2_8-branch/lib/python/AccessControl/ZopeGuards.py =================================================================== --- Zope/branches/Zope-2_8-branch/lib/python/AccessControl/ZopeGuards.py 2005-10-26 17:12:32 UTC (rev 39644) +++ Zope/branches/Zope-2_8-branch/lib/python/AccessControl/ZopeGuards.py 2005-10-26 17:12:35 UTC (rev 39645) @@ -375,6 +375,112 @@ ob._guarded_writes = 1 return ob +try: + valid_inplace_types = list, set +except NameError: + # Python 2.3 + valid_inplace_types = list + +inplace_slots = { + '+=': '__iadd__', + '-=': '__isub__', + '*=': '__imul__', + '/=': (1/2 == 0) and '__idiv__' or '__itruediv__', + '//=': '__ifloordiv__', + '%=': '__imod__', + '**=': '__ipow__', + '<<=': '__ilshift__', + '>>=': '__irshift__', + '&=': '__iand__', + '^=': '__ixor__', + '|=': '__ior_', + } + + +def __iadd__(x, y): + x += y + return x + +def __isub__(x, y): + x -= y + return x + +def __imul__(x, y): + x *= y + return x + +def __idiv__(x, y): + x /= y + return x + +def __ifloordiv__(x, y): + x //= y + return x + +def __imod__(x, y): + x %= y + return x + +def __ipow__(x, y): + x **= y + return x + +def __ilshift__(x, y): + x <<= y + return x + +def __irshift__(x, y): + x >>= y + return x + +def __iand__(x, y): + x &= y + return x + +def __ixor__(x, y): + x ^= y + return x + +def __ior__(x, y): + x |= y + return x + + +inplace_ops = { + '+=': __iadd__, + '-=': __isub__, + '*=': __imul__, + '/=': __idiv__, + '//=': __ifloordiv__, + '%=': __imod__, + '**=': __ipow__, + '<<=': __ilshift__, + '>>=': __irshift__, + '&=': __iand__, + '^=': __ixor__, + '|=': __ior__, + } + + +def protected_inplacevar(op, var, expr): + """Do an inplace operation + + If the var has an inplace slot, then disallow the operation + unless the var is a list. + """ + if (hasattr(var, inplace_slots[op]) + and not isinstance(var, valid_inplace_types) + ): + try: + cls = var.__class__ + except AttributeError: + cls = type(var) + raise TypeError( + "Augmented assignment to %s objects is not allowed" + " in untrusted code" % cls.__name__ + ) + return inplace_ops[op](var, expr) + # AccessControl clients generally need to set up a safe globals dict for # use by restricted code. The get_safe_globals() function returns such # a dict, containing '__builtins__' mapped to our safe bulitins, and @@ -394,6 +500,7 @@ '_getiter_': guarded_iter, '_print_': RestrictedPython.PrintCollector, '_write_': full_write_guard, + '_inplacevar_': protected_inplacevar, # The correct implementation of _getattr_, aka # guarded_getattr, isn't known until # AccessControl.Implementation figures that out, then Modified: Zope/branches/Zope-2_8-branch/lib/python/AccessControl/tests/actual_python.py =================================================================== --- Zope/branches/Zope-2_8-branch/lib/python/AccessControl/tests/actual_python.py 2005-10-26 17:12:32 UTC (rev 39644) +++ Zope/branches/Zope-2_8-branch/lib/python/AccessControl/tests/actual_python.py 2005-10-26 17:12:35 UTC (rev 39645) @@ -157,3 +157,9 @@ def f10(): assert iter(enumerate(iter(iter(range(9))))).next() == (0, 0) f10() + +def f11(): + x = 1 + x += 1 +f11() + Modified: Zope/branches/Zope-2_8-branch/lib/python/AccessControl/tests/testZopeGuards.py =================================================================== --- Zope/branches/Zope-2_8-branch/lib/python/AccessControl/tests/testZopeGuards.py 2005-10-26 17:12:32 UTC (rev 39644) +++ Zope/branches/Zope-2_8-branch/lib/python/AccessControl/tests/testZopeGuards.py 2005-10-26 17:12:35 UTC (rev 39645) @@ -20,6 +20,7 @@ import os, sys import unittest +from zope.testing import doctest import ZODB import AccessControl.SecurityManagement from AccessControl.SimpleObjectPolicies import ContainerAssertions @@ -671,8 +672,90 @@ if callable(v) and v is not getattr(__builtin__, k, None): d[k] = FuncWrapper(k, v) +def test_inplacevar(): + """ +Verify the correct behavior of protected_inplacevar. + + >>> from AccessControl.ZopeGuards import protected_inplacevar + +Basic operations on objects without inplace slots work as expected: + + >>> protected_inplacevar('+=', 1, 2) + 3 + >>> protected_inplacevar('-=', 5, 2) + 3 + >>> protected_inplacevar('*=', 5, 2) + 10 + >>> protected_inplacevar('/=', 6, 2) + 3 + >>> protected_inplacevar('%=', 5, 2) + 1 + >>> protected_inplacevar('**=', 5, 2) + 25 + >>> protected_inplacevar('<<=', 5, 2) + 20 + >>> protected_inplacevar('>>=', 5, 2) + 1 + >>> protected_inplacevar('&=', 5, 2) + 0 + >>> protected_inplacevar('^=', 7, 2) + 5 + >>> protected_inplacevar('|=', 5, 2) + 7 + +Inplace operations are allowed on lists: + + >>> protected_inplacevar('+=', [1], [2]) + [1, 2] + + >>> protected_inplacevar('*=', [1], 2) + [1, 1] + +But not on custom objects: + + >>> class C: + ... def __iadd__(self, other): + ... return 42 + >>> protected_inplacevar('+=', C(), 2) # doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + TypeError: Augmented assignment to C objects is not allowed in + untrusted code +""" + +if sys.version_info[:2] >= (2, 4): + def test_inplacevar_for_py24(): + """ +protected_inplacevar allows inplce ops on sets: + + >>> from AccessControl.ZopeGuards import protected_inplacevar + >>> s = set((1,2,3,4)) + >>> sorted(protected_inplacevar('-=', s, set((1, 3)))) + [2, 4] + >>> sorted(s) + [2, 4] + + >>> sorted(protected_inplacevar('|=', s, set((1, 3, 9)))) + [1, 2, 3, 4, 9] + >>> sorted(s) + [1, 2, 3, 4, 9] + + >>> sorted(protected_inplacevar('&=', s, set((1, 2, 3, 9)))) + [1, 2, 3, 9] + >>> sorted(s) + [1, 2, 3, 9] + + >>> sorted(protected_inplacevar('^=', s, set((1, 3, 7, 8)))) + [2, 7, 8, 9] + >>> sorted(s) + [2, 7, 8, 9] + +""" + def test_suite(): - suite = unittest.TestSuite() + suite = unittest.TestSuite([ + doctest.DocTestSuite(), + ]) for cls in (TestGuardedGetattr, TestGuardedGetitem, TestDictGuards, Modified: Zope/branches/Zope-2_8-branch/lib/python/RestrictedPython/tests/before_and_after.py =================================================================== --- Zope/branches/Zope-2_8-branch/lib/python/RestrictedPython/tests/before_and_after.py 2005-10-26 17:12:32 UTC (rev 39644) +++ Zope/branches/Zope-2_8-branch/lib/python/RestrictedPython/tests/before_and_after.py 2005-10-26 17:12:35 UTC (rev 39645) @@ -75,7 +75,7 @@ def nested_list_comprehension_after(): x = [x**2 + y**2 for x in _getiter_(whatever1) if x >= 0 for y in _getiter_(whatever2) if y >= x] - + # print def simple_print_before(): @@ -244,3 +244,18 @@ def lambda_with_getattr_in_defaults_after(): f = lambda x=_getattr_(y, "z"): x + + +# augmented operators +# Note that we don't have to worry about item, attr, or slice assignment, +# as they are disallowed. Yay! + +## def inplace_id_add_before(): +## x += y+z + +## def inplace_id_add_after(): +## x = _inplacevar_('+=', x, y+z) + + + + Modified: Zope/branches/Zope-2_8-branch/lib/python/RestrictedPython/tests/restricted_module.py =================================================================== --- Zope/branches/Zope-2_8-branch/lib/python/RestrictedPython/tests/restricted_module.py 2005-10-26 17:12:32 UTC (rev 39644) +++ Zope/branches/Zope-2_8-branch/lib/python/RestrictedPython/tests/restricted_module.py 2005-10-26 17:12:35 UTC (rev 39645) @@ -40,6 +40,10 @@ print f(*(300, 20), **{'z': 1}), return printed +def try_inplace(): + x = 1 + x += 3 + def primes(): # Somewhat obfuscated code on purpose print filter(None,map(lambda y:y*reduce(lambda x,y:x*y!=0, Modified: Zope/branches/Zope-2_8-branch/lib/python/RestrictedPython/tests/security_in_syntax.py =================================================================== --- Zope/branches/Zope-2_8-branch/lib/python/RestrictedPython/tests/security_in_syntax.py 2005-10-26 17:12:32 UTC (rev 39644) +++ Zope/branches/Zope-2_8-branch/lib/python/RestrictedPython/tests/security_in_syntax.py 2005-10-26 17:12:35 UTC (rev 39645) @@ -54,3 +54,16 @@ def keyword_arg_with_bad_name(): def f(okname=1, __badname=2): pass + +def no_augmeneted_assignment_to_sub(): + a[b] += c + +def no_augmeneted_assignment_to_attr(): + a.b += c + +def no_augmeneted_assignment_to_slice(): + a[x:y] += c + +def no_augmeneted_assignment_to_slice2(): + a[x:y:z] += c + _______________________________________________ Zope-Checkins maillist - Zope-Checkins@zope.org http://mail.zope.org/mailman/listinfo/zope-checkins