[Zope-Checkins] CVS: Zope/lib/python/AccessControl/tests - actual_python.py:1.2 testBindings.py:1.2 testImplementation.py:1.2 testZopeGuards.py:1.2 test_safeiter.py:1.2

Tres Seaver tseaver at zope.com
Thu Jan 15 18:09:37 EST 2004


Update of /cvs-repository/Zope/lib/python/AccessControl/tests
In directory cvs.zope.org:/tmp/cvs-serv24317/AccessControl/tests

Added Files:
	actual_python.py testBindings.py testImplementation.py 
	testZopeGuards.py test_safeiter.py 
Log Message:


  - Merge a number of entangled issues from 2.6 / 2.7 audit:

    Iteration over sequences could in some cases fail to check access 
    to an object obtained from the sequence. Subsequent checks (such 
    as for attributes access) of such an object would still be 
    performed, but it should not have been possible to obtain the 
    object in the first place.

    List and dictionary instance methods such as the get method of 
    dictionary objects were not security aware and could return an 
    object without checking access to that object. Subsequent checks 
    (such as for attributes access) of such an object would still be 
    performed, but it should not have been possible to obtain the 
    object in the first place.

    Use of "import as" in Python scripts could potentially rebind 
    names in ways that could be used to avoid appropriate security 
    checks.

    A number of newer built-ins were either unavailable in untrusted 
    code or did not perform adequate security checking.

    Unpacking via function calls, variable assignment, exception 
    variables and other contexts did not perform adequate security 
    checks, potentially allowing access to objects that should have 
    been protected.

    Class security was not properly intialized for PythonScripts, 
    potentially allowing access to variables that should be protected. 
    It turned out that most of the security assertions were in fact 
    activated as a side effect of other code, but this fix is still 
    appropriate to ensure that all security declarations are properly 
    applied.

    DTMLMethods with proxy rights could incorrectly transfer those 
    rights via acquisition when traversing to a parent object.


=== Zope/lib/python/AccessControl/tests/actual_python.py 1.1 => 1.2 ===
--- /dev/null	Thu Jan 15 18:09:37 2004
+++ Zope/lib/python/AccessControl/tests/actual_python.py	Thu Jan 15 18:09:06 2004
@@ -0,0 +1,157 @@
+# The code in this file is executed after being compiled as restricted code,
+# and given a globals() dict with our idea of safe builtins, and the
+# Zope production implementations of the special restricted-Python functions
+# (like _getitem_ and _getiter_, etc).
+#
+# This isn't trying to provoke security problems, it's just trying to verify
+# that Python code continues to work as intended after all the transformations,
+# and with all the special wrappers we supply.
+
+def f1():
+    next = iter(xrange(3)).next
+    assert next() == 0
+    assert next() == 1
+    assert next() == 2
+    try:
+        next()
+    except StopIteration:
+        pass
+    else:
+        assert 0, "expected StopIteration"
+f1()
+
+def f2():
+    assert map(lambda x: x+1, range(3)) == range(1, 4)
+f2()
+
+def f3():
+    assert filter(None, range(10)) == range(1, 10)
+f3()
+
+def f4():
+    assert [i+1 for i in range(3)] == range(*(1, 4))
+f4()
+
+def f5():
+    x = range(5)
+    def add(a, b):
+        return a+b
+    assert sum(x) == reduce(add, x, 0)
+f5()
+
+def f6():
+    class C:
+       def display(self):
+            return str(self.value)
+    c1 = C()
+    c2 = C()
+    # XXX Oops -- it's apparently against the rules to create a new
+    # XXX attribute.  Trying to yields
+    # XXX    TypeError: attribute-less object (assign or del)
+    ## c1.value = 12
+    ## assert getattr(c1, 'value') == 12
+    ## assert c1.display() == '12'
+    assert not hasattr(c2, 'value')
+    ## setattr(c2, 'value', 34)
+    ## assert c2.value == 34
+    ## assert hasattr(c2, 'value')
+    ## del c2.value
+    assert not hasattr(c2, 'value')
+
+    # OK, if we can't set new attributes, at least verify that we can't.
+    try:
+        c1.value = 12
+    except TypeError:
+        pass
+    else:
+        assert 0, "expected direct attribute creation to fail"
+
+    try:
+        setattr(c1, 'value', 12)
+    except TypeError:
+        pass
+    else:
+        assert 0, "expected indirect attribute creation to fail"
+
+    assert getattr(C, "display", None) == getattr(C, "display")
+
+    try:
+        setattr(C, "display", lambda self: "replaced")
+    except TypeError:
+        pass
+    else:
+        assert 0, "expected setattr() attribute replacement to fail"
+
+    try:
+        delattr(C, "display")
+    except TypeError:
+        pass
+    else:
+        assert 0, "expected delattr() attribute deletion to fail"
+f6()
+
+def f7():
+    d = apply(dict, [((1, 2), (3, 4))]) # {1: 2, 3: 4}
+    expected = {'k': [1, 3],
+                'v': [2, 4],
+                'i': [(1, 2), (3, 4)]}
+    for meth, kind in [('iterkeys', 'k'),
+                       ('iteritems', 'i'),
+                       ('itervalues', 'v'),
+                       ('keys', 'k'),
+                       ('items', 'i'),
+                       ('values', 'v')]:
+        access = getattr(d, meth)
+        result = list(access())
+        result.sort()
+        assert result == expected[kind], (meth, kind, result, expected[kind])
+f7()
+
+def f8():
+    import math
+    ceil = getattr(math, 'ceil')
+    smallest = 1e100
+    smallest_index = None
+    largest = -1e100
+    largest_index = None
+    all = []
+    for i, x in enumerate((2.2, 1.1, 3.3, 5.5, 4.4)):
+        all.append(x)
+        effective = ceil(x)
+        if effective < smallest:
+            assert min(effective, smallest) == effective
+            smallest = effective
+            smallest_index = i
+        if effective > largest:
+            assert max(effective, largest) == effective
+            largest = effective
+            largest_index = i
+    assert smallest == 2
+    assert smallest_index == 1
+    assert largest == 6
+    assert largest_index == 3
+
+    assert min([ceil(x) for x in all]) == smallest
+    assert max(map(ceil, all)) == largest
+f8()
+
+# After all the above, these wrappers were still untouched:
+#     ['DateTime', '_print_', 'reorder', 'same_type', 'test']
+# So do something to touch them.
+def f9():
+    d = DateTime()
+    print d # this one provoked _print_
+
+    # Funky.  This probably isn't an intended use of reorder, but I'm
+    # not sure why it exists.
+    assert reorder('edcbaxyz', 'abcdef', 'c') == zip('abde', 'abde')
+
+    assert test(0, 'a', 0, 'b', 1, 'c', 0, 'd') == 'c'
+    assert test(0, 'a', 0, 'b', 0, 'c', 0, 'd', 'e') == 'e'
+    # Unclear that the next one is *intended* to return None (it falls off
+    # the end of test's implementation without explicitly returning anything).
+    assert test(0, 'a', 0, 'b', 0, 'c', 0, 'd') == None
+
+    assert same_type(3, 2, 1), 'expected same type'
+    assert not same_type(3, 2, 'a'), 'expected not same type'
+f9()


=== Zope/lib/python/AccessControl/tests/testBindings.py 1.1 => 1.2 ===
--- /dev/null	Thu Jan 15 18:09:37 2004
+++ Zope/lib/python/AccessControl/tests/testBindings.py	Thu Jan 15 18:09:06 2004
@@ -0,0 +1,131 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Test Bindings
+
+$Id$
+"""
+
+import unittest
+import Zope
+import AccessControl.SecurityManagement
+from AccessControl import Unauthorized
+from Testing.makerequest import makerequest
+from Products.PythonScripts.PythonScript import PythonScript
+
+
+class TransactionalTest( unittest.TestCase ):
+
+    def setUp( self ):
+        if hasattr(Zope, 'startup'):
+            Zope.startup()
+        get_transaction().begin()
+        self.connection = Zope.DB.open()
+        self.root =  self.connection.root()[ 'Application' ]
+
+    def tearDown( self ):
+        get_transaction().abort()
+        self.connection.close()
+
+
+class RequestTest( TransactionalTest ):
+
+    def setUp(self):
+        TransactionalTest.setUp(self)
+        root = self.root = makerequest(self.root)
+        self.REQUEST  = root.REQUEST
+        self.RESPONSE = root.REQUEST.RESPONSE
+
+
+class SecurityManager:
+
+    def __init__(self, reject=0):
+        self.calls = []
+        self.reject = reject
+
+    def validate(self, *args):
+        self.calls.append(('validate', args))
+        if self.reject:
+            raise Unauthorized
+        return 1
+
+    def validateValue(self, *args):
+        self.calls.append(('validateValue', args))
+        if self.reject:
+            raise Unauthorized
+        return 1
+
+    def checkPermission(self, *args):
+        self.calls.append(('checkPermission', args))
+        return not self.reject
+        
+    def addContext(self, *args):
+        self.calls.append(('addContext', args))
+        return 1
+        
+    def removeContext(self, *args):
+        self.calls.append(('removeContext', args))
+        return 1
+        
+class GuardTestCase(RequestTest):
+
+    def setSecurityManager(self, manager):
+        key = AccessControl.SecurityManagement.get_ident()
+        old = AccessControl.SecurityManagement._managers.get(key)
+        if manager is None:
+            del AccessControl.SecurityManagement._managers[key]
+        else:
+            AccessControl.SecurityManagement._managers[key] = manager
+
+        return old
+        
+        
+class TestBindings(GuardTestCase):
+
+    def setUp(self):
+        RequestTest.setUp(self)
+        self.sm = SecurityManager(reject=1)
+        self.old = self.setSecurityManager(self.sm)
+
+    def tearDown(self):
+        self.setSecurityManager(self.old)
+        TransactionalTest.tearDown(self)
+
+    def _newPS(self, txt, bind=None):
+        ps = PythonScript('ps')
+        #ps.ZBindings_edit(bind or {})
+        ps.write(txt)
+        ps._makeFunction()
+        return ps
+    
+    def test_fail_container(self):
+        container_ps = self._newPS('return container')
+        self.root._setOb('container_ps', container_ps)
+        container_ps = self.root._getOb('container_ps')
+        self.assertRaises(Unauthorized, container_ps)
+
+    def test_fail_context(self):
+        context_ps = self._newPS('return context')
+        self.root._setOb('context_ps', context_ps)
+        context_ps = self.root._getOb('context_ps')
+        self.assertRaises(Unauthorized, context_ps)
+    
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(TestBindings))
+    return suite
+
+
+if __name__ == '__main__':
+    unittest.main()


=== Zope/lib/python/AccessControl/tests/testImplementation.py 1.1 => 1.2 ===
--- /dev/null	Thu Jan 15 18:09:37 2004
+++ Zope/lib/python/AccessControl/tests/testImplementation.py	Thu Jan 15 18:09:06 2004
@@ -0,0 +1,62 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Test of the implementation selection support."""
+
+import unittest
+
+from AccessControl.Implementation import getImplementationName
+from AccessControl.Implementation import setImplementation
+
+
+class AccessControlImplementationTest(unittest.TestCase):
+
+    have_cAccessControl = None
+
+    def setUp(self):
+        if self.have_cAccessControl is None:
+            try:
+                import AccessControl.cAccessControl
+            except ImportError:
+                v = False
+            else:
+                v = True
+            self.__class__.have_cAccessControl = v
+        self.original = getImplementationName()
+
+    def tearDown(self):
+        setImplementation(self.original)
+
+    def test_setImplemenationC(self):
+
+        # XXX:  'C' ZSP is not yet working
+        self.assertRaises( NotImplementedError, setImplementation, "C")
+        return
+
+        setImplementation("C")
+        name = getImplementationName()
+        if self.have_cAccessControl:
+            self.assertEqual(name, "C")
+        else:
+            self.assertEqual(name, "PYTHON")
+
+    def test_setImplemenationPython(self):
+        setImplementation("Python")
+        self.assertEqual(getImplementationName(), "PYTHON")
+
+
+def test_suite():
+    return unittest.makeSuite(AccessControlImplementationTest)
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="test_suite")


=== Zope/lib/python/AccessControl/tests/testZopeGuards.py 1.1 => 1.2 ===
--- /dev/null	Thu Jan 15 18:09:37 2004
+++ Zope/lib/python/AccessControl/tests/testZopeGuards.py	Thu Jan 15 18:09:06 2004
@@ -0,0 +1,449 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Test Zope Guards
+
+Well, at least begin testing some of the functionality
+
+$Id$
+"""
+
+import os, sys
+import unittest
+import ZODB
+import AccessControl.SecurityManagement
+from AccessControl.SimpleObjectPolicies import ContainerAssertions
+from AccessControl import Unauthorized
+from AccessControl.ZopeGuards \
+    import guarded_getattr, get_dict_get, get_dict_pop, get_list_pop, \
+    get_iter, guarded_min, guarded_max, safe_builtins, guarded_enumerate, \
+    guarded_sum, guarded_apply
+
+try:
+    __file__
+except NameError:
+    __file__ = os.path.abspath(sys.argv[1])
+_FILEPATH = os.path.abspath( __file__ )
+_HERE = os.path.dirname( _FILEPATH )
+
+class SecurityManager:
+
+    def __init__(self, reject=0):
+        self.calls = []
+        self.reject = reject
+
+    def validate(self, *args):
+        self.calls.append(('validate', args))
+        if self.reject:
+            raise Unauthorized
+        return 1
+
+    def validateValue(self, *args):
+        self.calls.append(('validateValue', args))
+        if self.reject:
+            raise Unauthorized
+        return 1
+
+    def checkPermission(self, *args):
+        self.calls.append(('checkPermission', args))
+        return not self.reject
+
+
+class GuardTestCase(unittest.TestCase):
+
+    def setSecurityManager(self, manager):
+        key = AccessControl.SecurityManagement.get_ident()
+        old = AccessControl.SecurityManagement._managers.get(key)
+        if manager is None:
+            del AccessControl.SecurityManagement._managers[key]
+        else:
+            AccessControl.SecurityManagement._managers[key] = manager
+
+        return old
+
+
+class Method:
+
+    def __init__(self, *args):
+        self.args = args
+
+
+class TestGuardedGetattr(GuardTestCase):
+
+    def setUp(self):
+        self.__sm = SecurityManager()
+        self.__old = self.setSecurityManager(self.__sm)
+
+    def tearDown(self):
+        self.setSecurityManager(self.__old)
+
+    def test_calls_validate_for_unknown_type(self):
+        guarded_getattr(self, 'test_calls_validate_for_unknown_type')
+        self.assert_(self.__sm.calls)
+
+    def test_attr_handler_table(self):
+        d = {}
+        _dict = type(d)
+        old = ContainerAssertions.get(_dict)
+
+        mytable = {'keys': 1,
+                   'values': Method,
+                   }
+        ContainerAssertions[_dict] = mytable
+        try:
+            guarded_getattr(d, 'keys')
+            self.assertEqual(len(self.__sm.calls), 0)
+            values = guarded_getattr(d, 'values')
+            self.assertEqual(values.__class__, Method)
+            self.assertEqual(values.args, (d, 'values'))
+            self.assertRaises(Unauthorized, guarded_getattr, d, 'items')
+        finally:
+            ContainerAssertions[_dict] = old
+
+
+class TestDictGuards(GuardTestCase):
+
+    def test_get_simple(self):
+        get = get_dict_get({'foo': 'bar'}, 'get')
+        self.assertEqual(get('foo'), 'bar')
+
+    def test_get_default(self):
+        get = get_dict_get({'foo': 'bar'}, 'get')
+        self.failUnless(get('baz') is None)
+        self.assertEqual(get('baz', 'splat'), 'splat')
+
+    def test_get_validates(self):
+        sm = SecurityManager()
+        old = self.setSecurityManager(sm)
+        get = get_dict_get({'foo':GuardTestCase}, 'get')
+        try:
+            get('foo')
+        finally:
+            self.setSecurityManager(old)
+        self.assert_(sm.calls)
+
+    def test_pop_simple(self):
+        pop = get_dict_pop({'foo': 'bar'}, 'pop')
+        self.assertEqual(pop('foo'), 'bar')
+
+    def test_pop_raises(self):
+        pop = get_dict_pop({'foo': 'bar'}, 'pop')
+        self.assertRaises(KeyError, pop, 'baz')
+
+    def test_pop_default(self):
+        pop = get_dict_pop({'foo': 'bar'}, 'pop')
+        self.assertEqual(pop('baz', 'splat'), 'splat')
+
+    def test_pop_validates(self):
+        sm = SecurityManager()
+        old = self.setSecurityManager(sm)
+        pop = get_dict_get({'foo':GuardTestCase}, 'pop')
+        try:
+            pop('foo')
+        finally:
+            self.setSecurityManager(old)
+        self.assert_(sm.calls)
+
+    if sys.version_info >= (2, 2):
+
+        def test_iterkeys_simple(self):
+            d = {'foo':1, 'bar':2, 'baz':3}
+            iterkeys = get_iter(d, 'iterkeys')
+            keys = d.keys()
+            keys.sort()
+            ikeys = list(iterkeys())
+            ikeys.sort()
+            self.assertEqual(keys, ikeys)
+
+        def test_iterkeys_empty(self):
+            iterkeys = get_iter({}, 'iterkeys')
+            self.assertEqual(list(iterkeys()), [])
+
+        def test_iterkeys_validates(self):
+            sm = SecurityManager()
+            old = self.setSecurityManager(sm)
+            iterkeys = get_iter({GuardTestCase: 1}, 'iterkeys')
+            try:
+                iterkeys().next()
+            finally:
+                self.setSecurityManager(old)
+            self.assert_(sm.calls)
+
+        def test_itervalues_simple(self):
+            d = {'foo':1, 'bar':2, 'baz':3}
+            itervalues = get_iter(d, 'itervalues')
+            values = d.values()
+            values.sort()
+            ivalues = list(itervalues())
+            ivalues.sort()
+            self.assertEqual(values, ivalues)
+
+        def test_itervalues_empty(self):
+            itervalues = get_iter({}, 'itervalues')
+            self.assertEqual(list(itervalues()), [])
+
+        def test_itervalues_validates(self):
+            sm = SecurityManager()
+            old = self.setSecurityManager(sm)
+            itervalues = get_iter({GuardTestCase: 1}, 'itervalues')
+            try:
+                itervalues().next()
+            finally:
+                self.setSecurityManager(old)
+            self.assert_(sm.calls)
+
+class TestListGuards(GuardTestCase):
+
+    def test_pop_simple(self):
+        pop = get_list_pop(['foo', 'bar', 'baz'], 'pop')
+        self.assertEqual(pop(), 'baz')
+        self.assertEqual(pop(0), 'foo')
+
+    def test_pop_raises(self):
+        pop = get_list_pop([], 'pop')
+        self.assertRaises(IndexError, pop)
+
+    def test_pop_validates(self):
+        sm = SecurityManager()
+        old = self.setSecurityManager(sm)
+        pop = get_list_pop([GuardTestCase], 'pop')
+        try:
+            pop()
+        finally:
+            self.setSecurityManager(old)
+        self.assert_(sm.calls)
+
+
+class TestBuiltinFunctionGuards(GuardTestCase):
+
+    def test_min_fails(self):
+        sm = SecurityManager(1) # rejects
+        old = self.setSecurityManager(sm)
+        self.assertRaises(Unauthorized, guarded_min, [1,2,3])
+        self.assertRaises(Unauthorized, guarded_min, 1,2,3)
+        self.setSecurityManager(old)
+
+    def test_max_fails(self):
+        sm = SecurityManager(1) # rejects
+        old = self.setSecurityManager(sm)
+        self.assertRaises(Unauthorized, guarded_max, [1,2,3])
+        self.assertRaises(Unauthorized, guarded_max, 1,2,3)
+        self.setSecurityManager(old)
+
+    def test_enumerate_fails(self):
+        sm = SecurityManager(1) # rejects
+        old = self.setSecurityManager(sm)
+        enum = guarded_enumerate([1,2,3])
+        self.assertRaises(Unauthorized, enum.next)
+        self.setSecurityManager(old)
+
+    def test_sum_fails(self):
+        sm = SecurityManager(1) # rejects
+        old = self.setSecurityManager(sm)
+        self.assertRaises(Unauthorized, guarded_sum, [1,2,3])
+        self.setSecurityManager(old)
+
+    def test_min_succeeds(self):
+        sm = SecurityManager() # accepts
+        old = self.setSecurityManager(sm)
+        self.assertEqual(guarded_min([1,2,3]), 1)
+        self.assertEqual(guarded_min(1,2,3), 1)
+        self.setSecurityManager(old)
+
+    def test_max_succeeds(self):
+        sm = SecurityManager() # accepts
+        old = self.setSecurityManager(sm)
+        self.assertEqual(guarded_max([1,2,3]), 3)
+        self.assertEqual(guarded_max(1,2,3), 3)
+        self.setSecurityManager(old)
+
+    def test_enumerate_succeeds(self):
+        sm = SecurityManager() # accepts
+        old = self.setSecurityManager(sm)
+        enum = guarded_enumerate([1,2,3])
+        self.assertEqual(enum.next(), (0,1))
+        self.assertEqual(enum.next(), (1,2))
+        self.assertEqual(enum.next(), (2,3))
+        self.assertRaises(StopIteration, enum.next)
+        self.setSecurityManager(old)
+
+    def test_sum_succeeds(self):
+        sm = SecurityManager() # accepts
+        old = self.setSecurityManager(sm)
+        self.assertEqual(guarded_sum([1,2,3]), 6)
+        self.assertEqual(guarded_sum([1,2,3], start=36), 42)
+        self.setSecurityManager(old)
+
+    def test_apply(self):
+        sm = SecurityManager(1) # rejects
+        old = self.setSecurityManager(sm)
+        gapply = safe_builtins['apply']
+        def f(a=1, b=2):
+            return a+b
+        # This one actually succeeds, because apply isn't given anything
+        # to unpack.
+        self.assertEqual(gapply(f), 3)
+        # Likewise, because the things passed are empty.
+        self.assertEqual(gapply(f, (), {}), 3)
+
+        self.assertRaises(Unauthorized, gapply, f, [1])
+        self.assertRaises(Unauthorized, gapply, f, (), {'a': 2})
+        self.assertRaises(Unauthorized, gapply, f, [1], {'a': 2})
+
+        sm = SecurityManager(0) # accepts
+        self.setSecurityManager(sm)
+        self.assertEqual(gapply(f), 3)
+        self.assertEqual(gapply(f, (), {}), 3)
+        self.assertEqual(gapply(f, [0]), 2)
+        self.assertEqual(gapply(f, [], {'b': 18}), 19)
+        self.assertEqual(gapply(f, [10], {'b': 1}), 11)
+
+        self.setSecurityManager(old)
+
+class TestGuardedDictListTypes(unittest.TestCase):
+
+    def testDictCreation(self):
+        d = safe_builtins['dict']
+        self.assertEquals(d(), {})
+        self.assertEquals(d({1:2}), {1:2})
+        self.assertEquals(d(((1,2),)), {1:2})
+        self.assertEquals(d(foo=1), {"foo":1})
+        self.assertEquals(d.fromkeys((1,2,3)), {1:None, 2:None, 3:None})
+        self.assertEquals(d.fromkeys((1,2,3), 'f'), {1:'f', 2:'f', 3:'f'})
+
+    def testListCreation(self):
+        l = safe_builtins['list']
+        self.assertEquals(l(), [])
+        self.assertEquals(l([1,2,3]), [1,2,3])
+        x = [3,2,1]
+        self.assertEquals(l(x), [3,2,1])
+        if sys.version_info >= (2, 4):
+            self.assertEquals(l.sorted(x), [1,2,3])
+
+class TestRestrictedPythonApply(GuardTestCase):
+
+    def test_apply(self):
+        sm = SecurityManager(1) # rejects
+        old = self.setSecurityManager(sm)
+        gapply = guarded_apply
+        def f(a=1, b=2):
+            return a+b
+        # This one actually succeeds, because apply isn't given anything
+        # to unpack.
+        self.assertEqual(gapply(*(f,)), 3)
+        # Likewise, because the things passed are empty.
+        self.assertEqual(gapply(*(f,), **{}), 3)
+
+        self.assertRaises(Unauthorized, gapply, *(f, 1))
+        self.assertRaises(Unauthorized, gapply, *(f,), **{'a': 2})
+        self.assertRaises(Unauthorized, gapply, *(f, 1), **{'a': 2})
+
+        sm = SecurityManager(0) # accepts
+        self.setSecurityManager(sm)
+        self.assertEqual(gapply(*(f,)), 3)
+        self.assertEqual(gapply(*(f,), **{}), 3)
+        self.assertEqual(gapply(*(f, 0)), 2)
+        self.assertEqual(gapply(*(f,), **{'b': 18}), 19)
+        self.assertEqual(gapply(*(f, 10), **{'b': 1}), 11)
+
+        self.setSecurityManager(old)
+
+
+# Map function name to the # of times it's been called.
+wrapper_count = {}
+class FuncWrapper:
+    def __init__(self, funcname, func):
+        self.funcname = funcname
+        wrapper_count[funcname] = 0
+        self.func = func
+
+    def __call__(self, *args, **kws):
+        wrapper_count[self.funcname] += 1
+        return self.func(*args, **kws)
+
+    def __repr__(self):
+        return "<FuncWrapper around %r>" % self.func
+
+# Given the high wall between AccessControl and RestrictedPython, I suppose
+# the next one could be called an integration test.  But we're simply
+# trying to run restricted Python with the *intended* implementations of
+# the special wrappers here, so no apologies.
+class TestActualPython(GuardTestCase):
+    def testPython(self):
+        from RestrictedPython.tests import verify
+
+        code, its_globals = self._compile("actual_python.py")
+        verify.verify(code)
+
+        # Fiddle the global and safe-builtins dicts to count how many times
+        # the special functions are called.
+        self._wrap_replaced_dict_callables(its_globals)
+        self._wrap_replaced_dict_callables(its_globals['__builtins__'])
+
+        sm = SecurityManager()
+        old = self.setSecurityManager(sm)
+        try:
+            exec code in its_globals
+        finally:
+            self.setSecurityManager(old)
+
+        # Use wrapper_count to determine coverage.
+        ## print wrapper_count # uncomment to see wrapper names & counts
+        untouched = [k for k, v in wrapper_count.items() if v == 0]
+        if untouched:
+            untouched.sort()
+            self.fail("Unexercised wrappers: %r" % untouched)
+
+    # Compile code in fname, as restricted Python.  Return the
+    # compiled code, and a safe globals dict for running it in.
+    # fname is the string name of a Python file; it must be found
+    # in the same directory as this file.
+    def _compile(self, fname):
+        from RestrictedPython import compile_restricted
+        from AccessControl.ZopeGuards import get_safe_globals, guarded_getattr
+
+        fn = os.path.join( _HERE, fname)
+        code = compile_restricted(open(fn).read(), fn, 'exec')
+
+        g = get_safe_globals()
+        g['_getattr_'] = guarded_getattr
+        g['__debug__'] = 1  # so assert statements are active
+        g['__name__'] = __name__ # so classes can be defined in the script
+        return code, g
+
+    # d is a dict, the globals for execution or our safe builtins.
+    # The callable values which aren't the same as the corresponding
+    # entries in __builtin__ are wrapped in a FuncWrapper, so we can
+    # tell whether they're executed.
+    def _wrap_replaced_dict_callables(self, d):
+        import __builtin__
+        for k, v in d.items():
+            if callable(v) and v is not getattr(__builtin__, k, None):
+                d[k] = FuncWrapper(k, v)
+
+def test_suite():
+    suite = unittest.TestSuite()
+    for cls in (TestGuardedGetattr,
+                TestDictGuards,
+                TestBuiltinFunctionGuards,
+                TestListGuards,
+                TestGuardedDictListTypes,
+                TestRestrictedPythonApply,
+                TestActualPython,
+                ):
+        suite.addTest(unittest.makeSuite(cls))
+    return suite
+
+
+if __name__ == '__main__':
+    unittest.main()


=== Zope/lib/python/AccessControl/tests/test_safeiter.py 1.1 => 1.2 ===
--- /dev/null	Thu Jan 15 18:09:37 2004
+++ Zope/lib/python/AccessControl/tests/test_safeiter.py	Thu Jan 15 18:09:06 2004
@@ -0,0 +1,68 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Tests for the guarded iterartor.
+
+$Id$
+"""
+
+import unittest
+
+# Persistence system must be initialized.
+import ZODB
+
+from AccessControl import ZopeGuards
+
+
+class SafeIterTestCase(unittest.TestCase):
+
+    # XXX these tests replace the global guard() function in
+    # AccessControl.ZopeGuards; this is not the nicest way to check
+    # that things work, but avoids making the SafeIter unit tests from
+    # testing things other than the guarded iterator itself.  In
+    # particular, it avoids testing the actual guard checks, which
+    # should be tested separately.
+
+    def setUp(self):
+        self.original_guard = ZopeGuards.guard
+        ZopeGuards.guard = self.guard
+        self.checks = []
+
+    def tearDown(self):
+        ZopeGuards.guard = self.original_guard
+
+    def guard(self, container, value, index=None):
+        self.checks.append((id(container), value))
+
+    def test_iteration(self):
+        seq = [1, 2, 3]
+        seqid = id(seq)
+        it = ZopeGuards.SafeIter(seq)
+        self.assertEqual(list(it), seq)
+        self.assertEqual(self.checks, [(seqid, 1),
+                                       (seqid, 2),
+                                       (seqid, 3)])
+
+    def test_iteration_with_container(self):
+        seq = [1, 2, 3]
+        container = object()
+        contid = id(container)
+        it = ZopeGuards.SafeIter(seq, container)
+        self.assertEqual(list(it), seq)
+        self.assertEqual(self.checks, [(contid, 1),
+                                       (contid, 2),
+                                       (contid, 3)])
+
+
+def test_suite():
+    return unittest.makeSuite(SafeIterTestCase)




More information about the Zope-Checkins mailing list