[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