diff --git a/product/ERP5Type/patches/Restricted.py b/product/ERP5Type/patches/Restricted.py index 95067096eed5a042b0edb286376e68268df7f7dd..a490c1a16ef466180ca6edd1494a3761cba802bd 100644 --- a/product/ERP5Type/patches/Restricted.py +++ b/product/ERP5Type/patches/Restricted.py @@ -23,6 +23,8 @@ RestrictionMutator.checkName = RestrictionMutator.checkAttrName = \ from Acquisition import aq_acquire from AccessControl import getSecurityManager +from AccessControl import allow_module, allow_class, allow_type +from AccessControl import ModuleSecurityInfo from AccessControl.ZopeGuards import (safe_builtins, _marker, Unauthorized, aq_validate, guard, guarded_getattr, guarded_iter, SafeIter, NullIter, ContainerAssertions, GuardedDictType, _dict_white_list) @@ -113,34 +115,51 @@ def get_set_pop(s, name): return v return guarded_pop -_set_white_get = { - 'add': 1, 'clear': 1, 'copy': 1, 'difference': 1, 'difference_update': 1, - 'discard': 1, 'intersection': 1, 'intersection_update': 1, 'isdisjoint': 1, - 'issubset': 1, 'issuperset': 1, 'pop': get_set_pop, 'remove': 1, - 'symmetric_difference': 1, 'symmetric_difference_update': 1, 'union': 1, - 'update': 1}.get - -def _check_set_access(name, value): - # Check whether value is a set method +def _check_access_wrapper(expected_type, white_list_dict): + def _check_access(name, value): + # Check whether value is a method of expected type self = getattr(value, '__self__', None) if self is None: # item return 1 # Disallow spoofing - if type(self) is not set: + if type(self) is not expected_type: return 0 if getattr(value, '__name__', None) != name: return 0 - return _set_white_get(name, 0) + return white_list_dict.get(name, 0) + + return _check_access + +_set_white_dict = { + 'add': 1, 'clear': 1, 'copy': 1, 'difference': 1, 'difference_update': 1, + 'discard': 1, 'intersection': 1, 'intersection_update': 1, 'isdisjoint': 1, + 'issubset': 1, 'issuperset': 1, 'pop': get_set_pop, 'remove': 1, + 'symmetric_difference': 1, 'symmetric_difference_update': 1, 'union': 1, + 'update': 1} -ContainerAssertions[set] = _check_set_access +ContainerAssertions[set] = _check_access_wrapper(set, _set_white_dict) ContainerAssertions[frozenset] = 1 from collections import OrderedDict -OrderedDict.__allow_access_to_unprotected_subobjects__ = 1 +ModuleSecurityInfo('collections').declarePublic('OrderedDict') -from AccessControl import allow_module, allow_class, allow_type -from AccessControl import ModuleSecurityInfo +from collections import defaultdict +ModuleSecurityInfo('collections').declarePublic('defaultdict') + +from AccessControl.ZopeGuards import _dict_white_list + +# Attributes cannot be set on defaultdict, thus modify 'safetype' dict +# (closure) directly to ignore defaultdict like dict/list +from RestrictedPython.Guards import full_write_guard +ContainerAssertions[defaultdict] = _check_access_wrapper(defaultdict, _dict_white_list) +full_write_guard.func_closure[1].cell_contents.__self__[defaultdict] = True + +# In contrary to builtins such as dict/defaultdict, it is possible to set +# attributes on OrderedDict instances, so only allow setitem/delitem +ContainerAssertions[OrderedDict] = _check_access_wrapper(OrderedDict, _dict_white_list) +OrderedDict.__guarded_setitem__ = OrderedDict.__setitem__.__func__ +OrderedDict.__guarded_delitem__ = OrderedDict.__delitem__.__func__ # given as example in Products.PythonScripts.module_access_examples allow_module('base64')