[Zope-Checkins] CVS: Zope2 - restricted_module.py:1.6 security_in_syntax.py:1.3 testRestrictions.py:1.7

shane@digicool.com shane@digicool.com
Thu, 21 Jun 2001 13:45:48 -0400 (EDT)


Update of /cvs-repository/Zope2/lib/python/RestrictedPython/tests
In directory korak.digicool.com:/tmp/cvs-serv24737/lib/python/RestrictedPython/tests

Modified Files:
	restricted_module.py security_in_syntax.py testRestrictions.py 
Log Message:
Based on some semi-formal performance tests, read guards turned out to be
slower than the old code.  With this change, we're using simple function
calls again to perform security checks.  But the calling sequence is
intended to be easier to comprehend than the old code.  Now instead of
DT_String.String subclasses having a validate() method attached to them, they
subclass AccessControl.DTML.RestrictedDTML, which provides a guarded_getattr()
method and a guarded_getitem() method.

Note that the functionality of guarded_getattr() used to be implemented
both in C and Python (in cDocumentTemplate and DT_Util), but now it's in
one place, ZopeGuards.py.  Thus it's not only reusable but easy to
optimize.

I ran all the tests and ran the new code through the profiler again.  The
change sped up restricted code a little more than expected, which is
definitely a good thing, but that may indicate that nested scopes
have a hidden speed penalty.

Also, RestrictedPython is now restrictive about printing to targets and
two forms of augmented assignment had to be forbidden.



--- Updated File restricted_module.py in package Zope2 --
--- restricted_module.py	2001/06/08 15:42:07	1.5
+++ restricted_module.py	2001/06/21 17:45:14	1.6
@@ -8,7 +8,7 @@
     print 'world!',
     return printed
 
-def print2():
+def printToNone():
     x = None
     print >>x, 'Hello, world!',
     return printed
@@ -53,21 +53,26 @@
 
 def allowed_write(ob):
     ob.writeable = 1
-    ob.writeable += 1
+    #ob.writeable += 1
     [1 for ob.writeable in 1,2]
     ob['safe'] = 2
-    ob['safe'] += 2
+    #ob['safe'] += 2
     [1 for ob['safe'] in 1,2]
 
+def denied_print(ob):
+    print >> ob, 'Hello, world!',
+
 def denied_getattr(ob):
-    ob.disallowed += 1
+    #ob.disallowed += 1
+    ob.disallowed = 1
     return ob.disallowed
 
 def denied_setattr(ob):
     ob.allowed = -1
 
 def denied_setattr2(ob):
-    ob.allowed += -1
+    #ob.allowed += -1
+    ob.allowed = -1
 
 def denied_setattr3(ob):
     [1 for ob.allowed in 1,2]
@@ -76,13 +81,15 @@
     ob[1]
 
 def denied_getitem2(ob):
-    ob[1] += 1
+    #ob[1] += 1
+    ob[1]
     
 def denied_setitem(ob):
     ob['x'] = 2
 
 def denied_setitem2(ob):
-    ob[0] += 2
+    #ob[0] += 2
+    ob['x'] = 2
 
 def denied_setitem3(ob):
     [1 for ob['x'] in 1,2]
@@ -91,16 +98,17 @@
     ob[0:1] = 'a'
 
 def denied_setslice2(ob):
-    ob[0:1] += 'a'
+    #ob[0:1] += 'a'
+    ob[0:1] = 'a'
 
 def denied_setslice3(ob):
     [1 for ob[0:1] in 1,2]
 
-def strange_attribute():
-    # If a guard has attributes with names that don't start with an
-    # underscore, those attributes appear to be an attribute of
-    # anything.
-    return [].attribute_of_anything
+##def strange_attribute():
+##    # If a guard has attributes with names that don't start with an
+##    # underscore, those attributes appear to be an attribute of
+##    # anything.
+##    return [].attribute_of_anything
 
 def order_of_operations():
     return 3 * 4 * -2 + 2 * 12

--- Updated File security_in_syntax.py in package Zope2 --
--- security_in_syntax.py	2001/04/27 20:27:54	1.2
+++ security_in_syntax.py	2001/06/21 17:45:14	1.3
@@ -4,20 +4,20 @@
 # Each function in this module is compiled using compile_restricted().
 
 def overrideGuardWithFunction():
-    def _guard(o): return o
+    def _getattr(o): return o
 
 def overrideGuardWithLambda():
-    lambda o, _guard=None: o
+    lambda o, _getattr=None: o
 
 def overrideGuardWithClass():
-    class _guard:
+    class _getattr:
         pass
 
 def overrideGuardWithName():
-    _guard = None
+    _getattr = None
 
 def overrideGuardWithArgument():
-    def f(_guard=None):
+    def f(_getattr=None):
         pass
 
 def reserved_names():

--- Updated File testRestrictions.py in package Zope2 --
--- testRestrictions.py	2001/06/08 15:42:07	1.6
+++ testRestrictions.py	2001/06/21 17:45:14	1.7
@@ -106,35 +106,38 @@
     def __setslice__(self, lo, hi, value):
         raise AccessDenied
 
+    write = DisallowedObject
 
+
+def guarded_getattr(ob, name):
+    v = getattr(ob, name)
+    if v is DisallowedObject:
+        raise AccessDenied
+    return v
+
+SliceType = type(slice(0))
+def guarded_getitem(ob, index):
+    if type(index) is SliceType and index.step is None:
+        start = index.start
+        stop = index.stop
+        if start is None:
+            start = 0
+        if stop is None:
+            v = ob[start:]
+        else:
+            v = ob[start:stop]
+    else:
+        v = ob[index]
+    if v is DisallowedObject:
+        raise AccessDenied
+    return v
+
+
 class TestGuard:
     '''A guard class'''
     def __init__(self, _ob, write=None):
         self.__dict__['_ob'] = _ob
 
-    # Read guard methods
-    def __len__(self):
-        return len(self.__dict__['_ob'])
-
-    def __getattr__(self, name):
-        _ob = self.__dict__['_ob']
-        v = getattr(_ob, name)
-        if v is DisallowedObject:
-            raise AccessDenied
-        return v
-
-    def __getitem__(self, index):
-        # Can receive an Ellipsis or "slice" instance.
-        _ob = self.__dict__['_ob']
-        v = _ob[index]
-        if v is DisallowedObject:
-            raise AccessDenied
-        return v
-
-    def __getslice__(self, lo, hi):
-        _ob = self.__dict__['_ob']
-        return _ob[lo:hi]
-
     # Write guard methods
 
     def __setattr__(self, name, value):
@@ -154,21 +157,31 @@
         _ob = self.__dict__['_ob']
         _ob[lo:hi] = value
 
-    attribute_of_anything = 98.6
+##    attribute_of_anything = 98.6
 
 class RestrictionTests(unittest.TestCase):
     def execFunc(self, name, *args, **kw):
         func = rmodule[name]
-        func.func_globals.update({'_read_': TestGuard,
+        func.func_globals.update({'_getattr_': guarded_getattr,
+                                  '_getitem_': guarded_getitem,
                                   '_write_': TestGuard,
                                   '_print_': PrintCollector})
         return func(*args, **kw)
 
     def checkPrint(self):
-        for i in range(3):
+        for i in range(2):
             res = self.execFunc('print%s' % i)
             assert res == 'Hello, world!', res
 
+    def checkPrintToNone(self):
+        try:
+            res = self.execFunc('printToNone')
+        except AttributeError:
+            # Passed.  "None" has no "write" attribute.
+            pass
+        else:
+            assert 0, res
+
     def checkPrintLines(self):
         res = self.execFunc('printLines')
         assert res == '0 1 2\n3 4 5\n6 7 8\n', res
@@ -221,9 +234,9 @@
                 else:
                     raise AssertionError, '%s should not have compiled' % k
 
-    def checkStrangeAttribute(self):
-        res = self.execFunc('strange_attribute')
-        assert res == 98.6, res
+##    def checkStrangeAttribute(self):
+##        res = self.execFunc('strange_attribute')
+##        assert res == 98.6, res
 
     def checkOrderOfOperations(self):
         res = self.execFunc('order_of_operations')