[Zope-Checkins] SVN: Zope/branches/Zope-2_8-branch/lib/python/ Added protection against the (small) risk that someone could mitate an

Jim Fulton jim at zope.com
Wed Oct 26 13:12:35 EDT 2005


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
+



More information about the Zope-Checkins mailing list