Commit af1a81fd authored by Arnaud Fontaine's avatar Arnaud Fontaine

py3: RestrictedPython: Port code and allow Python3/six modules.

parent 90ac1881
...@@ -6734,6 +6734,7 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -6734,6 +6734,7 @@ Business Template is a set of definitions, such as skins, portal types and categ
# Monkey patches or used by monkey patches ; Restricted Python # Monkey patches or used by monkey patches ; Restricted Python
'Products.ERP5Type.Calendar', 'Products.ERP5Type.Calendar',
'Products.ERP5Type.Collections', 'Products.ERP5Type.Collections',
'Products.ERP5Type.Six',
'Products.ERP5Type.Timeout', 'Products.ERP5Type.Timeout',
'Products.ERP5Type.ZipFile', 'Products.ERP5Type.ZipFile',
'Products.ERP5Type.ZopePatch', 'Products.ERP5Type.ZopePatch',
......
##############################################################################
#
# Copyright (c) 2022 Nexedi SA and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
"""
Restricted six module.
From restricted python, use "import six" (see patches/Restricted.py).
"""
import six as _six
PY2 = _six.PY2
PY3 = _six.PY3
moves = _six.moves
text_type = _six.text_type
binary_type = _six.binary_type
string_types = _six.string_types
integer_types = _six.integer_types
# ContainerAssertions cannot be used here (like for dict) because the following
# functions are defined on `six` module directly and ContainerAssertions has a
# type() as key
from Products.ERP5Type.patches.Restricted import SafeIterItems
iteritems = lambda d: SafeIterItems(_six.iteritems(d), d)
from AccessControl.ZopeGuards import SafeIter
iterkeys = lambda d: SafeIter(_six.iterkeys(d), d)
itervalues = lambda d: SafeIter(_six.itervalues(d), d)
from AccessControl.ZopeGuards import safe_builtins as _safe_builtins
_safe_builtins['xrange'] = _six.moves.xrange
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
# #
############################################################################## ##############################################################################
import six
import copy import copy
import sys import sys
import types import types
...@@ -128,34 +129,44 @@ class TypeAccessChecker: ...@@ -128,34 +129,44 @@ class TypeAccessChecker:
return v return v
return factory return factory
def __nonzero__(self): def __bool__(self):
# If Containers(type(x)) is true, ZopeGuard checks will short circuit, # If Containers(type(x)) is true, ZopeGuard checks will short circuit,
# thinking it's a simple type, but we don't want this for type, because # thinking it's a simple type, but we don't want this for type, because
# type(x) is type for classes, being trueish would skip security check on # type(x) is type for classes, being trueish would skip security check on
# classes. # classes.
return False return False
__nonzero__ = __bool__ # six.PY2
ContainerAssertions[type] = TypeAccessChecker() ContainerAssertions[type] = TypeAccessChecker()
class SafeIterItems(SafeIter): class SafeIterItems(SafeIter):
def next(self): def __next__(self):
ob = self._next() ob = self._next()
c = self.container c = self.container
guard(c, ob[0]) guard(c, ob[0])
guard(c, ob[1]) guard(c, ob[1])
return ob return ob
next = __next__ # six.PY2
def get_iteritems(c, name): def get_iteritems(c, name):
return lambda: SafeIterItems(c.iteritems(), c) return lambda: SafeIterItems(six.iteritems(c), c)
_dict_white_list['iteritems'] = get_iteritems _dict_white_list['iteritems'] = get_iteritems
import past.builtins # six.PY2
allow_module('past.builtins')
ModuleSecurityInfo('past.builtins').declarePublic('cmp')
def guarded_sorted(seq, cmp=None, key=None, reverse=False): def guarded_sorted(seq, cmp=None, key=None, reverse=False):
if cmp is not None: # six.PY2
from functools import cmp_to_key
key = cmp_to_key(cmp)
if not isinstance(seq, SafeIter): if not isinstance(seq, SafeIter):
for i, x in enumerate(seq): for i, x in enumerate(seq):
guard(seq, x, i) guard(seq, x, i)
return sorted(seq, cmp=cmp, key=key, reverse=reverse) return sorted(seq, key=key, reverse=reverse)
safe_builtins['sorted'] = guarded_sorted safe_builtins['sorted'] = guarded_sorted
def guarded_reversed(seq): def guarded_reversed(seq):
...@@ -225,8 +236,6 @@ from RestrictedPython.Guards import full_write_guard ...@@ -225,8 +236,6 @@ from RestrictedPython.Guards import full_write_guard
ContainerAssertions[defaultdict] = _check_access_wrapper(defaultdict, _dict_white_list) ContainerAssertions[defaultdict] = _check_access_wrapper(defaultdict, _dict_white_list)
full_write_guard.func_closure[1].cell_contents.__self__[defaultdict] = True 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) ContainerAssertions[OrderedDict] = _check_access_wrapper(OrderedDict, _dict_white_list)
OrderedDict.__guarded_setitem__ = OrderedDict.__setitem__.__func__ OrderedDict.__guarded_setitem__ = OrderedDict.__setitem__.__func__
OrderedDict.__guarded_delitem__ = OrderedDict.__delitem__.__func__ OrderedDict.__guarded_delitem__ = OrderedDict.__delitem__.__func__
...@@ -258,16 +267,18 @@ allow_type(type(re.compile(''))) ...@@ -258,16 +267,18 @@ allow_type(type(re.compile('')))
allow_type(type(re.match('x','x'))) allow_type(type(re.match('x','x')))
allow_type(type(re.finditer('x','x'))) allow_type(type(re.finditer('x','x')))
allow_module('StringIO')
import StringIO
StringIO.StringIO.__allow_access_to_unprotected_subobjects__ = 1
allow_module('cStringIO')
import cStringIO
allow_type(cStringIO.InputType)
allow_type(cStringIO.OutputType)
allow_module('io') allow_module('io')
import io import io
allow_type(io.BytesIO) allow_type(io.BytesIO)
allow_type(io.StringIO)
if six.PY2:
allow_module('StringIO')
import StringIO
StringIO.StringIO.__allow_access_to_unprotected_subobjects__ = 1
allow_module('cStringIO')
import cStringIO
allow_type(cStringIO.InputType)
allow_type(cStringIO.OutputType)
ModuleSecurityInfo('cgi').declarePublic('escape', 'parse_header') ModuleSecurityInfo('cgi').declarePublic('escape', 'parse_header')
allow_module('datetime') allow_module('datetime')
...@@ -307,10 +318,28 @@ import hashlib ...@@ -307,10 +318,28 @@ import hashlib
allow_type(type(hashlib.md5())) allow_type(type(hashlib.md5()))
allow_module('time') allow_module('time')
allow_module('unicodedata') allow_module('unicodedata')
allow_module('urlparse')
import urlparse if six.PY2:
allow_type(urlparse.ParseResult) import urlparse
allow_type(urlparse.SplitResult) allow_module('urlparse')
allow_type(urlparse.ParseResult)
allow_type(urlparse.SplitResult)
ModuleSecurityInfo('urllib').declarePublic(
'urlencode',
'quote', 'unquote',
'quote_plus', 'unquote_plus',
)
import six.moves.urllib.parse
allow_module('six.moves.urllib.parse')
allow_type(six.moves.urllib.parse.ParseResult)
allow_type(six.moves.urllib.parse.SplitResult)
ModuleSecurityInfo('six.moves.urllib.parse').declarePublic(
'urlencode',
'quote', 'unquote',
'quote_plus', 'unquote_plus',
)
allow_module('struct') allow_module('struct')
ModuleSecurityInfo('os.path').declarePublic( ModuleSecurityInfo('os.path').declarePublic(
...@@ -334,14 +363,19 @@ MNAME_MAP = { ...@@ -334,14 +363,19 @@ MNAME_MAP = {
'zipfile': 'Products.ERP5Type.ZipFile', 'zipfile': 'Products.ERP5Type.ZipFile',
'calendar': 'Products.ERP5Type.Calendar', 'calendar': 'Products.ERP5Type.Calendar',
'collections': 'Products.ERP5Type.Collections', 'collections': 'Products.ERP5Type.Collections',
'six': 'Products.ERP5Type.Six',
} }
for alias, real in MNAME_MAP.items(): for alias, real in six.iteritems(MNAME_MAP):
assert '.' not in alias, alias # TODO: support this assert '.' not in alias, alias # TODO: support this
allow_module(real) allow_module(real)
del alias, real del alias, real
orig_guarded_import = safe_builtins['__import__'] orig_guarded_import = safe_builtins['__import__']
try:
from AccessControl.ZopeGuards import import_default_level # zope4py3
except ImportError:
import_default_level = -1
def guarded_import(mname, globals=None, locals=None, fromlist=None, def guarded_import(mname, globals=None, locals=None, fromlist=None,
level=-1): level=import_default_level):
for fromname in fromlist or (): for fromname in fromlist or ():
if fromname[:1] == '_': if fromname[:1] == '_':
raise Unauthorized(fromname) raise Unauthorized(fromname)
...@@ -371,12 +405,6 @@ safe_builtins['__import__'] = guarded_import ...@@ -371,12 +405,6 @@ safe_builtins['__import__'] = guarded_import
ModuleSecurityInfo('transaction').declarePublic('doom') ModuleSecurityInfo('transaction').declarePublic('doom')
ModuleSecurityInfo('urllib').declarePublic(
'urlencode',
'quote', 'unquote',
'quote_plus', 'unquote_plus',
)
import hmac import hmac
allow_module('hmac') allow_module('hmac')
# HMAC does not sub-class object so ContainerAssertions # HMAC does not sub-class object so ContainerAssertions
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment