diff --git a/src/AccessControl/ImplPython.py b/src/AccessControl/ImplPython.py index 094c8af..ddeb907 100644 --- a/src/AccessControl/ImplPython.py +++ b/src/AccessControl/ImplPython.py @@ -673,8 +673,7 @@ def removeContext(self, anExecutableObject): del stack[-1] else: indexes = range(len(stack)) - indexes.reverse() - for i in indexes: + for i in reversed(indexes): top = stack[i] if top is anExecutableObject: del stack[i:] diff --git a/src/AccessControl/SimpleObjectPolicies.py b/src/AccessControl/SimpleObjectPolicies.py index 41c66ed..4187959 100644 --- a/src/AccessControl/SimpleObjectPolicies.py +++ b/src/AccessControl/SimpleObjectPolicies.py @@ -91,6 +91,7 @@ type(()): 1, type(''): 1, type(u''): 1, + range: 1, } Containers = ContainerAssertions.get diff --git a/src/AccessControl/ZopeGuards.py b/src/AccessControl/ZopeGuards.py index d7dd1cd..f532d16 100644 --- a/src/AccessControl/ZopeGuards.py +++ b/src/AccessControl/ZopeGuards.py @@ -16,6 +16,7 @@ from functools import reduce import math import random +import six import string import sys import warnings @@ -154,19 +155,28 @@ def guarded_pop(index=-1): 'copy': 1, 'fromkeys': 1, 'get': get_dict_get, - 'has_key': 1, 'items': 1, - 'iteritems': 1, - 'keys': 1, - 'iterkeys': get_iter, - 'itervalues': get_iter, 'pop': get_dict_pop, 'popitem': 1, 'setdefault': 1, 'update': 1, - 'values': 1, } +if six.PY3: + _dict_white_list.update({ + 'keys': get_iter, + 'values': get_iter, + }) +else: + _dict_white_list.update({ + 'has_key': 1, + 'iteritems': 1, + 'iterkeys': get_iter, + 'itervalues': get_iter, + 'keys': 1, + 'values': 1, + }) + def _check_dict_access(name, value): # Check whether value is a dict method @@ -257,10 +267,12 @@ def __next__(self): class NullIter(SafeIter): def __init__(self, ob): - self._next = ob.next + self._iter = ob + + def __next__(self): + return next(self._iter) - def next(self): - return self._next() + next = __next__ # Python 2 compat def _error(index): @@ -273,7 +285,7 @@ def guarded_iter(*args): # Don't double-wrap if isinstance(i, SafeIter): return i - if not isinstance(i, xrange): + if not isinstance(i, six.moves.range): return SafeIter(i) # Other call styles / targets don't need to be guarded return NullIter(iter(*args)) @@ -351,7 +363,7 @@ def guarded_map(f, *seqs): for seqno in range(len(seqs)): seq = guarded_getitem(seqs, seqno) safe_seqs.append(guarded_iter(seq)) - return map(f, *safe_seqs) + return list(map(f, *safe_seqs)) safe_builtins['map'] = guarded_map @@ -362,14 +374,20 @@ def guarded_zip(*seqs): for seqno in range(len(seqs)): seq = guarded_getitem(seqs, seqno) safe_seqs.append(guarded_iter(seq)) - return zip(*safe_seqs) + return list(zip(*safe_seqs)) safe_builtins['zip'] = guarded_zip +if six.PY3: + import_default_level = 0 +else: + import_default_level = -1 + + def guarded_import(mname, globals=None, locals=None, fromlist=None, - level=-1): + level=import_default_level): if fromlist is None: fromlist = () if '*' in fromlist: @@ -379,7 +397,7 @@ def guarded_import(mname, globals=None, locals=None, fromlist=None, if locals is None: locals = {} # Refs https://bugs.launchpad.net/zope2/+bug/659968 - if level != -1: + if level != import_default_level: raise Unauthorized("Using import with a level specification isn't " "supported by AccessControl: %s" % mname) diff --git a/src/AccessControl/rolemanager.py b/src/AccessControl/rolemanager.py index 74d4eee..bde7857 100644 --- a/src/AccessControl/rolemanager.py +++ b/src/AccessControl/rolemanager.py @@ -438,8 +438,7 @@ def possible_permissions(self): for p in self.ac_inherited_permissions(1): d[p[0]] = 1 - d = d.keys() - d.sort() + d = sorted(d.keys()) return d diff --git a/src/AccessControl/tests/actual_python.py b/src/AccessControl/tests/actual_python.py index 0fbfabd..7a1c04e 100644 --- a/src/AccessControl/tests/actual_python.py +++ b/src/AccessControl/tests/actual_python.py @@ -34,21 +34,21 @@ def f1(): def f2(): - assert map(lambda x: x + 1, range(3)) == range(1, 4) + assert list(map(lambda x: x + 1, range(3))) == list(range(1, 4)) f2() def f3(): - assert filter(None, range(10)) == range(1, 10) + assert list(filter(None, range(10))) == list(range(1, 10)) f3() def f4(): - assert [i + 1 for i in range(3)] == range(*(1, 4)) + assert [i + 1 for i in range(3)] == list(range(*(1, 4))) f4() @@ -123,21 +123,25 @@ def display(self): def f7(): + d = apply(dict, [((1, 2), (3, 4))]) # {1: 2, 3: 4} + methods = [('keys', 'k'), + ('items', 'i'), + ('values', 'v')] try: - d = apply(dict, [((1, 2), (3, 4))]) # {1: 2, 3: 4} - except NameError: - # Python 3 has no apply - d = dict(*[((1, 2), (3, 4))]) # {1: 2, 3: 4} + {}.iterkeys + except AttributeError: + pass + else: + # Python 2 only: + methods.extend([ + ('iterkeys', 'k'), + ('iteritems', 'i'), + ('itervalues', 'v')]) 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')]: + for meth, kind in methods: access = getattr(d, meth) result = list(access()) result.sort() diff --git a/src/AccessControl/tests/testModuleSecurity.py b/src/AccessControl/tests/testModuleSecurity.py index 3137c74..4547ddd 100644 --- a/src/AccessControl/tests/testModuleSecurity.py +++ b/src/AccessControl/tests/testModuleSecurity.py @@ -11,6 +11,7 @@ # ############################################################################## import unittest +from ..ZopeGuards import import_default_level class ModuleSecurityTests(unittest.TestCase): @@ -33,13 +34,13 @@ def tearDown(self): if module in sys.modules: del sys.modules[module] - def assertUnauth(self, module, fromlist, level=-1): + def assertUnauth(self, module, fromlist, level=import_default_level): from zExceptions import Unauthorized from AccessControl.ZopeGuards import guarded_import self.assertRaises(Unauthorized, guarded_import, module, fromlist=fromlist, level=level) - def assertAuth(self, module, fromlist, level=-1): + def assertAuth(self, module, fromlist, level=import_default_level): from AccessControl.ZopeGuards import guarded_import guarded_import(module, fromlist=fromlist, level=level) @@ -83,7 +84,8 @@ def test_failed_import_keeps_MSI(self): self.failUnless('AccessControl.tests.nonesuch' in MS) def test_level_default(self): - self.assertAuth('AccessControl.tests.public_module', (), level=-1) + self.assertAuth('AccessControl.tests.public_module', (), + level=import_default_level) def test_level_nondefault(self): self.assertUnauth('AccessControl.tests.public_module', (), level=1) diff --git a/src/AccessControl/tests/testZopeGuards.py b/src/AccessControl/tests/testZopeGuards.py index ed0e5df..5f3f9f3 100644 --- a/src/AccessControl/tests/testZopeGuards.py +++ b/src/AccessControl/tests/testZopeGuards.py @@ -14,9 +14,12 @@ """Test Zope Guards """ +from AccessControl.ZopeGuards import guarded_getattr + import doctest import operator import os +import six import sys import unittest @@ -186,7 +189,8 @@ def test_unauthorized(self): def test_hit(self): from AccessControl.ZopeGuards import guarded_hasattr - obj, name = Method(), 'args' + obj = Method(2411) + name = 'args' value = getattr(obj, name) rc = sys.getrefcount(value) self.assertTrue(guarded_hasattr(obj, name)) @@ -245,67 +249,103 @@ def test_pop_validates(self): self.setSecurityManager(old) self.assert_(sm.calls) - if sys.version_info >= (2, 2): - - def test_iterkeys_simple(self): - from AccessControl.ZopeGuards import get_iter - 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): - from AccessControl.ZopeGuards import get_iter - iterkeys = get_iter({}, 'iterkeys') - self.assertEqual(list(iterkeys()), []) - - def test_iterkeys_validates(self): - from AccessControl.ZopeGuards import get_iter - sm = SecurityManager() - old = self.setSecurityManager(sm) - iterkeys = get_iter({GuardTestCase: 1}, 'iterkeys') - try: - next(iterkeys()) - finally: - self.setSecurityManager(old) - self.assert_(sm.calls) - - def test_itervalues_simple(self): - from AccessControl.ZopeGuards import get_iter - 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): - from AccessControl.ZopeGuards import get_iter - itervalues = get_iter({}, 'itervalues') - self.assertEqual(list(itervalues()), []) - - def test_itervalues_validates(self): - from AccessControl.ZopeGuards import get_iter - sm = SecurityManager() - old = self.setSecurityManager(sm) - itervalues = get_iter({GuardTestCase: 1}, 'itervalues') - try: - next(itervalues()) - finally: - self.setSecurityManager(old) - self.assert_(sm.calls) + @unittest.skipIf(six.PY3, "iter... is Python 2 only") + def test_iterkeys_simple(self): + from AccessControl.ZopeGuards import get_iter + 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) + + @unittest.skipIf(six.PY3, "iter... is Python 2 only") + def test_iterkeys_empty(self): + from AccessControl.ZopeGuards import get_iter + iterkeys = get_iter({}, 'iterkeys') + self.assertEqual(list(iterkeys()), []) + + @unittest.skipIf(six.PY2, "keys() is only in Python 3 a generator") + def test_keys_empty(self): + from AccessControl.ZopeGuards import get_iter + keys = get_iter({}, 'keys') + self.assertEqual(list(keys()), []) + + @unittest.skipIf(six.PY3, "iter... is Python 2 only") + def test_iterkeys_validates(self): + sm = SecurityManager() + old = self.setSecurityManager(sm) + iterkeys = guarded_getattr({GuardTestCase: 1}, 'iterkeys') + try: + next(iterkeys()) + finally: + self.setSecurityManager(old) + self.assert_(sm.calls) + + @unittest.skipIf(six.PY2, "keys() is only in Python 3 a generator") + def test_keys_validates(self): + sm = SecurityManager() + old = self.setSecurityManager(sm) + keys = guarded_getattr({GuardTestCase: 1}, 'keys') + try: + next(keys()) + finally: + self.setSecurityManager(old) + self.assert_(sm.calls) + + @unittest.skipIf(six.PY3, "iter... is Python 2 only") + def test_itervalues_simple(self): + from AccessControl.ZopeGuards import get_iter + 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) + + @unittest.skipIf(six.PY3, "iter... is Python 2 only") + def test_itervalues_empty(self): + from AccessControl.ZopeGuards import get_iter + itervalues = get_iter({}, 'itervalues') + self.assertEqual(list(itervalues()), []) + + @unittest.skipIf(six.PY2, "values() is only in Python 3 a generator") + def test_values_empty(self): + from AccessControl.ZopeGuards import get_iter + values = get_iter({}, 'values') + self.assertEqual(list(values()), []) + + @unittest.skipIf(six.PY3, "iter... is Python 2 only") + def test_itervalues_validates(self): + sm = SecurityManager() + old = self.setSecurityManager(sm) + itervalues = guarded_getattr({GuardTestCase: 1}, 'itervalues') + try: + next(itervalues()) + finally: + self.setSecurityManager(old) + self.assert_(sm.calls) + + @unittest.skipIf(six.PY2, "values() is only in Python 3 a generator") + def test_values_validates(self): + sm = SecurityManager() + old = self.setSecurityManager(sm) + values = guarded_getattr({GuardTestCase: 1}, 'values') + try: + next(values()) + finally: + self.setSecurityManager(old) + self.assert_(sm.calls) class TestListGuards(GuardTestCase): @@ -692,10 +732,7 @@ def private_method(self): return _ProtectedBase 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. @@ -722,7 +759,6 @@ def testPythonRealAC(self): def test_derived_class_normal(self): from AccessControl import Unauthorized - from RestrictedPython.tests import verify NORMAL_SCRIPT = """ class Normal(ProtectedBase): @@ -733,7 +769,6 @@ class Normal(ProtectedBase): """ code, its_globals = self._compile_str(NORMAL_SCRIPT, 'normal_script') its_globals['ProtectedBase'] = self._getProtectedBaseClass() - verify.verify(code) self._initPolicyAndManager() @@ -749,7 +784,6 @@ def test_derived_class_sneaky_en_suite(self): # Disallow declaration of security-affecting names in classes # defined in restricted code (compile-time check). - from RestrictedPython.tests import verify SNEAKY_SCRIPT = """ class Sneaky(ProtectedBase): @@ -771,7 +805,6 @@ def test_derived_sneaky_post_facto(self): # Assignment to a class outside its suite fails at # compile time with a SyntaxError. - from RestrictedPython.tests import verify SNEAKY_SCRIPT = """ class Sneaky(ProtectedBase): @@ -794,7 +827,6 @@ def test_derived_sneaky_instance(self): # Assignment of security-sensitive names to an instance # fails at compile time with a SyntaxError. - from RestrictedPython.tests import verify SNEAKY_SCRIPT = """ class Sneaky(ProtectedBase): @@ -813,8 +845,6 @@ class Sneaky(ProtectedBase): self.fail("Didn't raise SyntaxError!") def test_dict_access(self): - from RestrictedPython.tests import verify - SIMPLE_DICT_ACCESS_SCRIPT = """ def foo(text): return text @@ -826,7 +856,6 @@ def foo(text): print(foo(**kw)) """ code, its_globals = self._compile_str(SIMPLE_DICT_ACCESS_SCRIPT, 'x') - verify.verify(code) sm = SecurityManager() old = self.setSecurityManager(sm) @@ -840,13 +869,9 @@ def foo(text): def test_guarded_next__1(self): """There is a `safe_builtin` named `next`.""" - from RestrictedPython.tests import verify - SCRIPT = "result = next(iterator)" code, its_globals = self._compile_str(SCRIPT, 'ignored') - verify.verify(code) - its_globals['iterator'] = iter([2411, 123]) sm = SecurityManager() @@ -860,14 +885,11 @@ def test_guarded_next__1(self): def test_guarded_next__2(self): """It guards the access during iteration.""" - from RestrictedPython.tests import verify from AccessControl import Unauthorized SCRIPT = "next(iterator)" code, its_globals = self._compile_str(SCRIPT, 'ignored') - verify.verify(code) - its_globals['iterator'] = iter([2411, 123]) sm = SecurityManager(reject=True) @@ -881,15 +903,12 @@ def test_guarded_next__2(self): def test_guarded_next__3(self): """It does not double check if using a `SafeIter`.""" - from RestrictedPython.tests import verify from AccessControl import Unauthorized from AccessControl.ZopeGuards import SafeIter SCRIPT = "next(iter([2411, 123]))" code, its_globals = self._compile_str(SCRIPT, 'ignored') - verify.verify(code) - its_globals['iterator'] = SafeIter([2411, 123]) sm = SecurityManager() @@ -928,14 +947,14 @@ def _compile(self, fname): # 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 + # entries in builtins are wrapped in a FuncWrapper, so we can # tell whether they're executed. def _wrap_replaced_dict_callables(self, d): - import __builtin__ + from RestrictedPython.Guards import builtins orig = d.copy() self._wrapped_dicts.append((d, orig)) for k, v in d.items(): - if callable(v) and v is not getattr(__builtin__, k, None): + if callable(v) and v is not getattr(builtins, k, None): d[k] = FuncWrapper(k, v) @@ -953,7 +972,9 @@ def test_inplacevar(): 3 >>> protected_inplacevar('*=', 5, 2) 10 - >>> protected_inplacevar('/=', 6, 2) + >>> protected_inplacevar('/=', 6, 2.0) + 3.0 + >>> protected_inplacevar('//=', 6, 2) 3 >>> protected_inplacevar('%=', 5, 2) 1