--- a/src/reportlab/__init__.py Thu Oct 31 11:17:14 2019 +0000
+++ b/src/reportlab/__init__.py Tue Jan 14 09:08:36 2020 +0000
@@ -1,9 +1,9 @@
#Copyright ReportLab Europe Ltd. 2000-2018
#see license.txt for license details
__doc__="""The Reportlab PDF generation library."""
-Version = "3.5.33"
+Version = "3.5.34"
__version__=Version
-__date__='20191029'
+__date__='20200114'
import sys, os
--- a/src/reportlab/lib/PyFontify.py Thu Oct 31 11:17:14 2019 +0000
+++ b/src/reportlab/lib/PyFontify.py Tue Jan 14 09:08:36 2020 +0000
@@ -49,7 +49,8 @@
"break", "else", "if", "or", "while",
"class", "except", "import", "pass",
"continue", "finally", "in", "print",
- "def", "for", "is", "raise", "yield"]
+ "def", "for", "is", "raise", "yield",
+ "with"]
# Build up a regular expression which will match anything
# interesting, including multi-line triple-quoted strings.
--- a/src/reportlab/lib/colors.py Thu Oct 31 11:17:14 2019 +0000
+++ b/src/reportlab/lib/colors.py Tue Jan 14 09:08:36 2020 +0000
@@ -42,7 +42,7 @@
import math, re, functools
from reportlab import isPy3, cmp
from reportlab.lib.rl_accel import fp_str
-from reportlab.lib.utils import asNative, isStr, safer_globals
+from reportlab.lib.utils import asNative, isStr, rl_safe_eval
import collections
from ast import literal_eval
@@ -836,6 +836,7 @@
cssParse=cssParse()
class toColor:
+ _G = {} #globals we like (eventually)
def __init__(self):
self.extraColorsNS = {} #used for overriding/adding to existing color names
@@ -860,8 +861,18 @@
C = getAllNamedColors()
s = arg.lower()
if s in C: return C[s]
+ G = C.copy()
+ G.update(self.extraColorsNS)
+ if not self._G:
+ C = globals()
+ self._G = {s:C[s] for s in '''Blacker CMYKColor CMYKColorSep Color ColorType HexColor PCMYKColor PCMYKColorSep Whiter
+ _chooseEnforceColorSpace _enforceCMYK _enforceError _enforceRGB _enforceSEP _enforceSEP_BLACK
+ _enforceSEP_CMYK _namedColors _re_css asNative cmyk2rgb cmykDistance color2bw colorDistance
+ cssParse describe fade fp_str getAllNamedColors hsl2rgb hue2rgb isStr linearlyInterpolatedColor
+ literal_eval obj_R_G_B opaqueColor rgb2cmyk setColors toColor toColorOrNone'''.split()}
+ G.update(self._G)
try:
- return toColor(eval(arg,safer_globals()))
+ return toColor(rl_safe_eval(arg,g=G,l={}))
except:
pass
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/reportlab/lib/rl_safe_eval.py Tue Jan 14 09:08:36 2020 +0000
@@ -0,0 +1,1343 @@
+#this code is copied/stolen/borrowed/modified from various sources including
+#https://github.com/zopefoundation/AccessControl
+#https://github.com/zopefoundation/RestrictedPython
+#https://github.com/danthedeckie/simpleeval
+#hopefully we are standing on giants' shoulders
+import sys, os, ast, re, weakref, time, copy, math
+from collections import Mapping as collectionsMapping
+from reportlab import isPy3
+isPy2 = not isPy3
+eval_debug = int(os.environ.get('EVAL_DEBUG','0'))
+strTypes = basestring if isPy2 else (bytes,str)
+
+haveNameConstant = hasattr(ast,'NameConstant')
+haveMatMult = haveMultiStarred = hasattr(ast,'MatMult')
+import textwrap
+
+class BadCode(ValueError):
+ pass
+
+# For AugAssign the operator must be converted to a string.
+augOps = {
+ # Shared by python2 and python3
+ ast.Add: '+=',
+ ast.Sub: '-=',
+ ast.Mult: '*=',
+ ast.Div: '/=',
+ ast.Mod: '%=',
+ ast.Pow: '**=',
+ ast.LShift: '<<=',
+ ast.RShift: '>>=',
+ ast.BitOr: '|=',
+ ast.BitXor: '^=',
+ ast.BitAnd: '&=',
+ ast.FloorDiv: '//='
+}
+
+if haveMatMult:
+ augOps[ast.MatMult] = '@='
+
+
+# For creation allowed magic method names. See also
+# https://docs.python.org/3/reference/datamodel.html#special-method-names
+__allowed_magic_methods__ = frozenset([
+ '__init__',
+ '__contains__',
+ '__lt__',
+ '__le__',
+ '__eq__',
+ '__ne__',
+ '__gt__',
+ '__ge__',
+ ])
+
+__rl_unsafe__ = frozenset('''builtins breakpoint __annotations__ co_argcount co_cellvars co_code co_consts
+ __code__ co_filename co_firstlineno co_flags co_freevars co_kwonlyargcount
+ co_lnotab co_name co_names co_nlocals co_posonlyargcount co_stacksize
+ co_varnames cr_await cr_code cr_frame cr_origin cr_running __defaults__
+ f_back f_builtins f_code f_exc_traceback f_exc_type f_exc_value f_globals
+ f_lasti f_lineno f_locals f_restricted f_trace __func__ func_code func_defaults
+ func_doc func_globals func_name gi_code gi_frame gi_running gi_yieldfrom
+ __globals__ im_class im_func im_self __iter__ __kwdefaults__ __module__
+ __name__ next __qualname__ __self__ tb_frame tb_lasti tb_lineno tb_next
+ globals vars locals'''.split()
+ )
+__rl_unsafe_re__ = re.compile(r'\b(?:%s)' % '|'.join(__rl_unsafe__),re.M)
+
+def copy_locations(new_node, old_node):
+ new_node.lineno = old_node.lineno
+ new_node.col_offset = old_node.col_offset
+ ast.fix_missing_locations(new_node)
+
+class UntrustedAstTransformer(ast.NodeTransformer):
+
+ def __init__(self, names_seen=None, nameIsAllowed=None):
+ super(UntrustedAstTransformer, self).__init__()
+ self.names_seen = {} if names_seen is None else names_seen
+ self.nameIsAllowed = nameIsAllowed
+
+ # Global counter to construct temporary variable names.
+ self._tmp_idx = 0
+ self._tmp_pfx = '_tmp%s' % repr(time.time()).replace('.','')
+
+ @property
+ def tmpName(self):
+ name = '%s%s' % (self._tmp_pfx,self._tmp_idx)
+ self._tmp_idx += 1
+ return name
+
+ def error(self, node, msg):
+ raise BadCode('Line %s: %s' % (getattr(node, 'lineno', '??'), msg))
+
+ def guard_iter(self, node):
+ """
+ Converts:
+ for x in expr
+ to
+ for x in __rl_getiter__(expr)
+
+ Also used for
+ * list comprehensions
+ * dict comprehensions
+ * set comprehensions
+ * generator expresions
+ """
+ node = self.visit_children(node)
+
+ if isinstance(node.target, ast.Tuple):
+ spec = self.gen_unpack_spec(node.target)
+ new_iter = ast.Call(
+ func=ast.Name('__rl_iter_unpack_sequence__', ast.Load()),
+ args=[node.iter, spec, ast.Name('__rl_getiter__', ast.Load())],
+ keywords=[])
+ else:
+ new_iter = ast.Call(
+ func=ast.Name('__rl_getiter__', ast.Load()),
+ args=[node.iter],
+ keywords=[])
+
+ copy_locations(new_iter, node.iter)
+ node.iter = new_iter
+ return node
+
+ def is_starred(self, ob):
+ if isPy3:
+ return isinstance(ob, ast.Starred)
+ else:
+ return False
+
+ def gen_unpack_spec(self, tpl):
+ """Generate a specification for '__rl_unpack_sequence__'.
+
+ This spec is used to protect sequence unpacking.
+ The primary goal of this spec is to tell which elements in a sequence
+ are sequences again. These 'child' sequences have to be protected
+ again.
+
+ For example there is a sequence like this:
+ (a, (b, c), (d, (e, f))) = g
+
+ On a higher level the spec says:
+ - There is a sequence of len 3
+ - The element at index 1 is a sequence again with len 2
+ - The element at index 2 is a sequence again with len 2
+ - The element at index 1 in this subsequence is a sequence again
+ with len 2
+
+ With this spec '__rl_unpack_sequence__' does something like this for
+ protection (len checks are omitted):
+
+ t = list(__rl_getiter__(g))
+ t[1] = list(__rl_getiter__(t[1]))
+ t[2] = list(__rl_getiter__(t[2]))
+ t[2][1] = list(__rl_getiter__(t[2][1]))
+ return t
+
+ The 'real' spec for the case above is then:
+ spec = {
+ 'min_len': 3,
+ 'childs': (
+ (1, {'min_len': 2, 'childs': ()}),
+ (2, {
+ 'min_len': 2,
+ 'childs': (
+ (1, {'min_len': 2, 'childs': ()})
+ )
+ }
+ )
+ )
+ }
+
+ So finally the assignment above is converted into:
+ (a, (b, c), (d, (e, f))) = __rl_unpack_sequence__(g, spec)
+ """
+ spec = ast.Dict(keys=[], values=[])
+
+ spec.keys.append(ast.Str('childs'))
+ spec.values.append(ast.Tuple([], ast.Load()))
+
+ # starred elements in a sequence do not contribute into the min_len.
+ # For example a, b, *c = g
+ # g must have at least 2 elements, not 3. 'c' is empyt if g has only 2.
+ min_len = len([ob for ob in tpl.elts if not self.is_starred(ob)])
+ offset = 0
+
+ for idx, val in enumerate(tpl.elts):
+ # After a starred element specify the child index from the back.
+ # Since it is unknown how many elements from the sequence are
+ # consumed by the starred element.
+ # For example a, *b, (c, d) = g
+ # Then (c, d) has the index '-1'
+ if self.is_starred(val):
+ offset = min_len + 1
+
+ elif isinstance(val, ast.Tuple):
+ el = ast.Tuple([], ast.Load())
+ el.elts.append(ast.Num(idx - offset))
+ el.elts.append(self.gen_unpack_spec(val))
+ spec.values[0].elts.append(el)
+
+ spec.keys.append(ast.Str('min_len'))
+ spec.values.append(ast.Num(min_len))
+
+ return spec
+
+ def protect_unpack_sequence(self, target, value):
+ spec = self.gen_unpack_spec(target)
+ return ast.Call(
+ func=ast.Name('__rl_unpack_sequence__', ast.Load()),
+ args=[value, spec, ast.Name('__rl_getiter__', ast.Load())],
+ keywords=[])
+
+ def gen_unpack_wrapper(self, node, target, ctx='store'):
+ """Helper function to protect tuple unpacks.
+
+ node: used to copy the locations for the new nodes.
+ target: is the tuple which must be protected.
+ ctx: Defines the context of the returned temporary node.
+
+ It returns a tuple with two element.
+
+ Element 1: Is a temporary name node which must be used to
+ replace the target.
+ The context (store, param) is defined
+ by the 'ctx' parameter..
+
+ Element 2: Is a try .. finally where the body performs the
+ protected tuple unpack of the temporary variable
+ into the original target.
+ """
+
+ # Generate a tmp name to replace the tuple with.
+ tnam = self.tmpName
+
+ # Generates an expressions which protects the unpack.
+ # converter looks like 'wrapper(tnam)'.
+ # 'wrapper' takes care to protect sequence unpacking with __rl_getiter__.
+ converter = self.protect_unpack_sequence(
+ target,
+ ast.Name(tnam, ast.Load()))
+
+ # Assign the expression to the original names.
+ # Cleanup the temporary variable.
+ # Generates:
+ # try:
+ # # converter is 'wrapper(tnam)'
+ # arg = converter
+ # finally:
+ # del tmp_arg
+ try_body = [ast.Assign(targets=[target], value=converter)]
+ finalbody = [self.gen_del_stmt(tnam)]
+
+ if isPy2:
+ cleanup = ast.TryFinally(body=try_body, finalbody=finalbody)
+ else:
+ cleanup = ast.Try(
+ body=try_body, finalbody=finalbody, handlers=[], orelse=[])
+
+ if ctx == 'store':
+ ctx = ast.Store()
+ elif ctx == 'param':
+ ctx = ast.Param()
+ else: # pragma: no cover
+ # Only store and param are defined ctx.
+ raise NotImplementedError('bad ctx "%s"' % type(ctx))
+
+ # This node is used to catch the tuple in a tmp variable.
+ tmp_target = ast.Name(tnam, ctx)
+
+ copy_locations(tmp_target, node)
+ copy_locations(cleanup, node)
+
+ return (tmp_target, cleanup)
+
+ def gen_none_node(self):
+ return ast.NameConstant(value=None) if hasNameConstant else ast.Name(id='None', ctx=ast.Load())
+
+ def gen_lambda(self, args, body):
+ return ast.Lambda(
+ args=ast.arguments(
+ args=args, vararg=None, kwarg=None, defaults=[]),
+ body=body)
+
+ def gen_del_stmt(self, name_to_del):
+ return ast.Delete(targets=[ast.Name(name_to_del, ast.Del())])
+
+ def transform_slice(self, slice_):
+ """Transform slices into function parameters.
+
+ ast.Slice nodes are only allowed within a ast.Subscript node.
+ To use a slice as an argument of ast.Call it has to be converted.
+ Conversion is done by calling the 'slice' function from builtins
+ """
+
+ if isinstance(slice_, ast.Index):
+ return slice_.value
+
+ elif isinstance(slice_, ast.Slice):
+ # Create a python slice object.
+ args = []
+
+ if slice_.lower:
+ args.append(slice_.lower)
+ else:
+ args.append(self.gen_none_node())
+
+ if slice_.upper:
+ args.append(slice_.upper)
+ else:
+ args.append(self.gen_none_node())
+
+ if slice_.step:
+ args.append(slice_.step)
+ else:
+ args.append(self.gen_none_node())
+
+ return ast.Call(
+ func=ast.Name('slice', ast.Load()),
+ args=args,
+ keywords=[])
+
+ elif isinstance(slice_, ast.ExtSlice):
+ dims = ast.Tuple([], ast.Load())
+ for item in slice_.dims:
+ dims.elts.append(self.transform_slice(item))
+ return dims
+
+ else: # pragma: no cover
+ # Index, Slice and ExtSlice are only defined Slice types.
+ raise NotImplementedError("Unknown slice type: %s" % slice_)
+
+ def isAllowedName(self, node, name):
+ if name is None: return
+ self.nameIsAllowed(name)
+
+ def check_function_argument_names(self, node):
+ # In python3 arguments are always identifiers.
+ # In python2 the 'Python.asdl' specifies expressions, but
+ # the python grammer allows only identifiers or a tuple of
+ # identifiers. If its a tuple 'tuple parameter unpacking' is used,
+ # which is gone in python3.
+ # See https://www.python.org/dev/peps/pep-3113/
+
+ if isPy2:
+ # Needed to handle nested 'tuple parameter unpacking'.
+ # For example 'def foo((a, b, (c, (d, e)))): pass'
+ to_check = list(node.args.args)
+ while to_check:
+ item = to_check.pop()
+ if isinstance(item, ast.Tuple):
+ to_check.extend(item.elts)
+ else:
+ self.isAllowedName(node, item.id)
+
+ self.isAllowedName(node, node.args.vararg)
+ self.isAllowedName(node, node.args.kwarg)
+
+ else:
+ for arg in node.args.args:
+ self.isAllowedName(node, arg.arg)
+
+ if node.args.vararg:
+ self.isAllowedName(node, node.args.vararg.arg)
+
+ if node.args.kwarg:
+ self.isAllowedName(node, node.args.kwarg.arg)
+
+ for arg in node.args.kwonlyargs:
+ self.isAllowedName(node, arg.arg)
+
+ def check_import_names(self, node):
+ """Check the names being imported.
+
+ This is a protection against rebinding dunder names like
+ __rl_getitem__,__rl_set__ via imports.
+
+ => 'from _a import x' is ok, because '_a' is not added to the scope.
+ """
+ for name in node.names:
+ if '*' in name.name:
+ self.error(node, '"*" imports are not allowed.')
+ self.isAllowedName(node, name.name)
+ if name.asname:
+ self.isAllowedName(node, name.asname)
+
+ return self.visit_children(node)
+
+ def gen_attr_check(self, node, attr_name):
+ """Check if 'attr_name' is allowed on the object in node.
+
+ It generates (_getattr_(node, attr_name) and node).
+ """
+
+ call_getattr = ast.Call(
+ func=ast.Name('__rl_getattr__', ast.Load()),
+ args=[node, ast.Str(attr_name)],
+ keywords=[])
+
+ return ast.BoolOp(op=ast.And(), values=[call_getattr, node])
+
+ def visit_Constant(self, node):
+ """Allow constant literals with restriction for Ellipsis.
+
+ Constant replaces Num, Str, Bytes, NameConstant and Ellipsis in
+ Python 3.8+.
+ :see: https://docs.python.org/dev/whatsnew/3.8.html#deprecated
+ """
+ if node.value is Ellipsis:
+ # Deny using `...`.
+ # Special handling necessary as ``self.not_allowed(node)``
+ # would return the Error Message:
+ # 'Constant statements are not allowed.'
+ # which is only partial true.
+ self.error(node, 'Ellipsis statements are not allowed.')
+ return
+ return self.visit_children(node)
+
+ # ast for Variables
+ def visit_Name(self, node):
+ node = self.visit_children(node)
+
+ if isinstance(node.ctx, ast.Load):
+ if node.id == 'print':
+ self.error(node,'print function is not allowed')
+ self.names_seen[node.id] = True
+
+ self.isAllowedName(node, node.id)
+ return node
+
+ def visit_Call(self, node):
+ """Checks calls with '*args' and '**kwargs'.
+
+ Note: The following happens only if '*args' or '**kwargs' is used.
+
+ Transfroms 'foo(<all the possible ways of args>)' into
+ __rl_apply__(foo, <all the possible ways for args>)
+
+ The thing is that '__rl_apply__' has only '*args', '**kwargs', so it gets
+ Python to collapse all the myriad ways to call functions
+ into one manageable from.
+
+ From there, '__rl_apply__()' wraps args and kws in guarded accessors,
+ then calls the function, returning the value.
+ """
+
+ if isinstance(node.func, ast.Name):
+ if node.func.id == 'exec':
+ self.error(node, 'Exec calls are not allowed.')
+ elif node.func.id == 'eval':
+ self.error(node, 'Eval calls are not allowed.')
+
+ needs_wrap = False
+
+ # In python2.7 till python3.4 '*args', '**kwargs' have dedicated
+ # attributes on the ast.Call node.
+ # In python 3.5 and greater this has changed due to the fact that
+ # multiple '*args' and '**kwargs' are possible.
+ # '*args' can be detected by 'ast.Starred' nodes.
+ # '**kwargs' can be deteced by 'keyword' nodes with 'arg=None'.
+
+ if haveMultiStarred:
+ for pos_arg in node.args:
+ if isinstance(pos_arg, ast.Starred):
+ needs_wrap = True
+
+ for keyword_arg in node.keywords:
+ if keyword_arg.arg is None:
+ needs_wrap = True
+ else:
+ if (node.starargs is not None) or (node.kwargs is not None):
+ needs_wrap = True
+
+ node = self.visit_children(node)
+
+ #if not needs_wrap:
+ # return node
+
+ node.args.insert(0, node.func)
+ node.func = ast.Name('__rl_apply__', ast.Load())
+ copy_locations(node.func, node.args[0])
+ return node
+
+ def visit_Attribute(self, node):
+ """Checks and mutates attribute access/assignment.
+
+ 'a.b' becomes '__rl_getattr__(a, "b")'
+ """
+ if node.attr.startswith('__') and node.attr != '__':
+ self.error(node, '"%s" is an invalid attribute'%node.attr)
+
+ if isinstance(node.ctx, ast.Load):
+ node = self.visit_children(node)
+ new_node = ast.Call(
+ func=ast.Name('__rl_getattr__', ast.Load()),
+ args=[node.value, ast.Str(node.attr)],
+ keywords=[])
+
+ copy_locations(new_node, node)
+ return new_node
+
+ elif isinstance(node.ctx, (ast.Store, ast.Del)):
+ node = self.visit_children(node)
+ new_value = ast.Call(
+ func=ast.Name('__rl_sd__', ast.Load()),
+ args=[node.value],
+ keywords=[])
+
+ copy_locations(new_value, node.value)
+ node.value = new_value
+ return node
+
+ else: # pragma: no cover
+ # Impossible Case only ctx Load, Store and Del are defined in ast.
+ raise NotImplementedError("Unknown ctx type: %s" % type(node.ctx))
+
+ # Subscripting
+ def visit_Subscript(self, node):
+ """Transforms all kinds of subscripts.
+
+ 'v[a]' becomes '__rl_getitem__(foo, a)'
+ 'v[:b]' becomes '__rl_getitem__(foo, slice(None, b, None))'
+ 'v[a:]' becomes '__rl_getitem__(foo, slice(a, None, None))'
+ 'v[a:b]' becomes '__rl_getitem__(foo, slice(a, b, None))'
+ 'v[a:b:c]' becomes '__rl_getitem__(foo, slice(a, b, c))'
+ 'v[a,b:c] becomes '__rl_getitem__(foo, (a, slice(b, c, None)))'
+ #'v[a] = c' becomes '_rl_write__(v)[a] = c'
+ #'del v[a]' becomes 'del __rl_sd__(v)[a]'
+ """
+ node = self.visit_children(node)
+
+ # 'AugStore' and 'AugLoad' are defined in 'Python.asdl' as possible
+ # 'expr_context'. However, according to Python/ast.c
+ # they are NOT used by the implementation => No need to worry here.
+ # Instead ast.c creates 'AugAssign' nodes, which can be visit_ed.
+
+ if isinstance(node.ctx, ast.Load):
+ new_node = ast.Call(
+ func=ast.Name('__rl_getitem__', ast.Load()),
+ args=[node.value, self.transform_slice(node.slice)],
+ keywords=[])
+
+ copy_locations(new_node, node)
+ return new_node
+
+ elif isinstance(node.ctx, (ast.Del, ast.Store)):
+ #new_value = ast.Call(
+ # func=ast.Name('__rl_sd__', ast.Load()),
+ # args=[node.value],
+ # keywords=[])
+
+ #copy_locations(new_value, node)
+ #node.value = new_value
+ return node
+
+ else: # pragma: no cover
+ # Impossible Case only ctx Load, Store and Del are defined in ast.
+ raise NotImplementedError("Unknown ctx type: %s" % type(node.ctx))
+
+ # Statements
+ def visit_Assign(self, node):
+ node = self.visit_children(node)
+
+ if not any(isinstance(t, ast.Tuple) for t in node.targets):
+ return node
+
+ # Handle sequence unpacking.
+ # For briefness this example omits cleanup of the temporary variables.
+ # Check 'transform_tuple_assign' how its done.
+ #
+ # - Single target (with nested support)
+ # (a, (b, (c, d))) = <exp>
+ # is converted to
+ # (a, t1) = __rl_getiter__(<exp>)
+ # (b, t2) = __rl_getiter__(t1)
+ # (c, d) = __rl_getiter__(t2)
+ #
+ # - Multi targets
+ # (a, b) = (c, d) = <exp>
+ # is converted to
+ # (c, d) = __rl_getiter__(<exp>)
+ # (a, b) = __rl_getiter__(<exp>)
+ # Why is this valid ? The original bytecode for this multi targets
+ # behaves the same way.
+
+ # ast.NodeTransformer works with list results.
+ # He injects it at the rightplace of the node's parent statements.
+ new_nodes = []
+
+ # python fills the right most target first.
+ for target in reversed(node.targets):
+ if isinstance(target, ast.Tuple):
+ wrapper = ast.Assign(
+ targets=[target],
+ value=self.protect_unpack_sequence(target, node.value))
+ new_nodes.append(wrapper)
+ else:
+ new_node = ast.Assign(targets=[target], value=node.value)
+ new_nodes.append(new_node)
+
+ for new_node in new_nodes:
+ copy_locations(new_node, node)
+
+ return new_nodes
+
+ def visit_AugAssign(self, node):
+ """Forbid certain kinds of AugAssign
+
+ According to the language reference (and ast.c) the following nodes
+ are are possible:
+ Name, Attribute, Subscript
+
+ Note that although augmented assignment of attributes and
+ subscripts is disallowed, augmented assignment of names (such
+ as 'n += 1') is allowed.
+ 'n += 1' becomes 'n = __rl_augAssign__("+=", n, 1)'
+ """
+
+ node = self.visit_children(node)
+
+ if isinstance(node.target, ast.Attribute):
+ self.error(node, "Augmented assignment of attributes is not allowed.")
+
+ elif isinstance(node.target, ast.Subscript):
+ self.error(node, "Augmented assignment of object items and slices is not allowed.")
+
+ elif isinstance(node.target, ast.Name):
+ new_node = ast.Assign(
+ targets=[node.target],
+ value=ast.Call(
+ func=ast.Name('__rl_augAssign__', ast.Load()),
+ args=[
+ ast.Str(augOps[type(node.op)]),
+ ast.Name(node.target.id, ast.Load()),
+ node.value
+ ],
+ keywords=[]))
+
+ copy_locations(new_node, node)
+ return new_node
+ else: # pragma: no cover
+ # Impossible Case - Only Node Types:
+ # * Name
+ # * Attribute
+ # * Subscript
+ # defined, those are checked before.
+ raise NotImplementedError("Unknown target type: %s" % type(node.target))
+
+ def visit_While(node):
+ self.visit_children(node)
+ return node
+
+ def visit_ExceptHandler(self, node):
+ """Protect tuple unpacking on exception handlers.
+
+ try:
+ .....
+ except Exception as (a, b):
+ ....
+
+ becomes
+
+ try:
+ .....
+ except Exception as tmp:
+ try:
+ (a, b) = __rl_getiter__(tmp)
+ finally:
+ del tmp
+ """
+ node = self.visit_children(node)
+
+ if isPy3:
+ self.isAllowedName(node, node.name)
+ return node
+
+ if not isinstance(node.name, ast.Tuple):
+ return node
+
+ tmp_target, unpack = self.gen_unpack_wrapper(node, node.name)
+
+ # Replace the tuple with the temporary variable.
+ node.name = tmp_target
+
+ # Insert the unpack code within the body of the except clause.
+ node.body.insert(0, unpack)
+
+ return node
+
+ def visit_With(self, node):
+ """Protect tuple unpacking on with statements."""
+ node = self.visit_children(node)
+
+ if isPy2:
+ items = [node]
+ else:
+ items = node.items
+
+ for item in reversed(items):
+ if isinstance(item.optional_vars, ast.Tuple):
+ tmp_target, unpack = self.gen_unpack_wrapper(
+ node,
+ item.optional_vars)
+
+ item.optional_vars = tmp_target
+ node.body.insert(0, unpack)
+
+ return node
+
+ # Function and class definitions
+ def visit_FunctionDef(self, node):
+ """Allow function definitions (`def`) with some restrictions."""
+ self.isAllowedName(node, node.name)
+ self.check_function_argument_names(node)
+
+ if isPy3:
+ return node
+
+ # Protect 'tuple parameter unpacking' with '__rl_getiter__'.
+
+ unpacks = []
+ for index, arg in enumerate(list(node.args.args)):
+ if isinstance(arg, ast.Tuple):
+ tmp_target, unpack = self.gen_unpack_wrapper(
+ node, arg, 'param')
+
+ # Replace the tuple with a single (temporary) parameter.
+ node.args.args[index] = tmp_target
+ unpacks.append(unpack)
+
+ # Add the unpacks at the front of the body.
+ # Keep the order, so that tuple one is unpacked first.
+ node.body[0:0] = unpacks
+ return node
+
+ def visit_Lambda(self, node):
+ """Allow lambda with some restrictions."""
+ self.check_function_argument_names(node)
+
+ node = self.visit_children(node)
+
+ if isPy3:
+ # Implicit Tuple unpacking is not anymore available in Python3
+ return node
+
+ # Check for tuple parameters which need __rl_getiter__ protection
+ if not any(isinstance(arg, ast.Tuple) for arg in node.args.args):
+ return node
+
+ # Wrap this lambda function with another. Via this wrapping it is
+ # possible to protect the 'tuple arguments' with __rl_getiter__
+ outer_params = []
+ inner_args = []
+
+ for arg in node.args.args:
+ if isinstance(arg, ast.Tuple):
+ tnam = self.tmpName
+ converter = self.protect_unpack_sequence(
+ arg,
+ ast.Name(tnam, ast.Load()))
+
+ outer_params.append(ast.Name(tnam, ast.Param()))
+ inner_args.append(converter)
+
+ else:
+ outer_params.append(arg)
+ inner_args.append(ast.Name(arg.id, ast.Load()))
+
+ body = ast.Call(func=node, args=inner_args, keywords=[])
+ new_node = self.gen_lambda(outer_params, body)
+
+ if node.args.vararg:
+ new_node.args.vararg = node.args.vararg
+ body.starargs = ast.Name(node.args.vararg, ast.Load())
+
+ if node.args.kwarg:
+ new_node.args.kwarg = node.args.kwarg
+ body.kwargs = ast.Name(node.args.kwarg, ast.Load())
+
+ copy_locations(new_node, node)
+ return new_node
+
+ def visit_ClassDef(self, node):
+ """Check the name of a class definition."""
+ self.isAllowedName(node, node.name)
+ node = self.visit_children(node)
+ if isPy2:
+ new_class_node = node
+ else:
+ if any(keyword.arg == 'metaclass' for keyword in node.keywords):
+ self.error(node, 'The keyword argument "metaclass" is not allowed.')
+ CLASS_DEF = textwrap.dedent('''\
+ class %s(metaclass=__metaclass__):
+ pass
+ ''' % node.name)
+ new_class_node = ast.parse(CLASS_DEF).body[0]
+ new_class_node.body = node.body
+ new_class_node.bases = node.bases
+ new_class_node.decorator_list = node.decorator_list
+ return new_class_node
+
+ # Imports
+ def visit_Import(self, node):
+ return self.check_import_names(node)
+
+ node = self.visit_children(node)
+ new_node = ast.Call(
+ func=ast.Name('__rl_add__', ast.Load()),
+ args=[node.left, node.right],
+ keywords=[])
+ copy_locations(new_node, node)
+ return new_node
+
+ def visit_BinOp(self,node):
+ node = self.visit_children(node)
+ op = node.op
+ if isinstance(op,(ast.Mult,ast.Add,ast.Pow)):
+ opf = ('__rl_mult__' if isinstance(op,ast.Mult)
+ else '__rl_add__' if isinstance(op,ast.Add)
+ else '__rl_pow__')
+ new_node = ast.Call(
+ func=ast.Name(opf, ast.Load()),
+ args=[node.left, node.right],
+ keywords=[])
+ copy_locations(new_node, node)
+ return new_node
+ return node
+
+ visit_ImportFrom = visit_Import
+ visit_For = guard_iter
+ visit_comprehension = guard_iter
+
+ def generic_visit(self, node):
+ """Reject nodes which do not have a corresponding `visit` method."""
+ self.not_allowed(node)
+
+ def not_allowed(self, node):
+ self.error(node, '%s statements are not allowed.'%node.__class__.__name__)
+
+ def visit_children(self, node):
+ """Visit the contents of a node."""
+ return super(UntrustedAstTransformer, self).generic_visit(node)
+
+ if eval_debug>=2:
+ def visit(self, node):
+ method = 'visit_' + node.__class__.__name__
+ visitor = getattr(self, method, self.generic_visit)
+ print('visitor=%s=%r node=%r' % (method,visitor,node))
+ return visitor(node)
+
+ visit_Ellipsis = not_allowed
+ visit_MatMult = not_allowed
+ visit_Exec = not_allowed
+ visit_Nonlocal = not_allowed
+ visit_AsyncFunctionDef = not_allowed
+ visit_Await = not_allowed
+ visit_AsyncFor = not_allowed
+ visit_AsyncWith = not_allowed
+ visit_Print = not_allowed
+
+ visit_Num = visit_children
+ visit_Str = visit_children
+ visit_Bytes = visit_children
+ visit_List = visit_children
+ visit_Tuple = visit_children
+ visit_Set = visit_children
+ visit_Dict = visit_children
+ visit_FormattedValue = visit_children
+ visit_JoinedStr = visit_children
+ visit_NameConstant = visit_children
+ visit_Load = visit_children
+ visit_Store = visit_children
+ visit_Del = visit_children
+ visit_Starred = visit_children
+ visit_Expression = visit_children
+ visit_Expr = visit_children
+ visit_UnaryOp = visit_children
+ visit_UAdd = visit_children
+ visit_USub = visit_children
+ visit_Not = visit_children
+ visit_Invert = visit_children
+ visit_Add = visit_children
+ visit_Sub = visit_children
+ visit_Mult = visit_children
+ visit_Div = visit_children
+ visit_FloorDiv = visit_children
+ visit_Pow = visit_children
+ visit_Mod = visit_children
+ visit_LShift = visit_children
+ visit_RShift = visit_children
+ visit_BitOr = visit_children
+ visit_BitXor = visit_children
+ visit_BitAnd = visit_children
+ visit_BoolOp = visit_children
+ visit_And = visit_children
+ visit_Or = visit_children
+ visit_Compare = visit_children
+ visit_Eq = visit_children
+ visit_NotEq = visit_children
+ visit_Lt = visit_children
+ visit_LtE = visit_children
+ visit_Gt = visit_children
+ visit_GtE = visit_children
+ visit_Is = visit_children
+ visit_IsNot = visit_children
+ visit_In = visit_children
+ visit_NotIn = visit_children
+ visit_keyword = visit_children
+ visit_IfExp = visit_children
+ visit_Index = visit_children
+ visit_Slice = visit_children
+ visit_ExtSlice = visit_children
+ visit_ListComp = visit_children
+ visit_SetComp = visit_children
+ visit_GeneratorExp = visit_children
+ visit_DictComp = visit_children
+ visit_Raise = visit_children
+ visit_Assert = visit_children
+ visit_Delete = visit_children
+ visit_Pass = visit_children
+ visit_alias = visit_children
+ visit_If = visit_children
+ visit_Break = visit_children
+ visit_Continue = visit_children
+ visit_Try = visit_children
+ visit_TryFinally = visit_children
+ visit_TryExcept = visit_children
+ visit_withitem = visit_children
+ visit_arguments = visit_children
+ visit_arg = visit_children
+ visit_Return = visit_children
+ visit_Yield = visit_children
+ visit_YieldFrom = visit_children
+ visit_Global = visit_children
+ visit_Module = visit_children
+ visit_Param = visit_children
+
+def astFormat(node):
+ return ast.dump(copy.deepcopy(node),annotate_fields=True, include_attributes=True)
+
+class __rl_SafeIter__(object):
+ def __init__(self, it, owner):
+ self.__rl_iter__ = owner().__rl_real_iter__(it)
+ self.__rl_owner__ = owner
+
+ def __iter__(self):
+ return self
+
+ def __next__(self):
+ self.__rl_owner__().__rl_check__()
+ return next(self.__rl_iter__)
+
+ next = __next__ # Python 2 compat
+
+__rl_safe_builtins__ = {} #constructed below
+def safer_globals(g=None):
+ if g is None:
+ g = sys._getframe(1).f_globals.copy()
+ for name in ('__annotations__', '__doc__', '__loader__', '__name__', '__package__', '__spec__'):
+ if name in g:
+ del g[name]
+ g['__builtins__'] = __rl_safe_builtins__.copy()
+ return g
+
+math_log10 = math.log10
+__rl_undef__ = object()
+class __RL_SAFE_ENV__(object):
+ __time_time__ = time.time
+ __weakref_ref__ = weakref.ref
+ __slicetype__ = type(slice(0))
+ def __init__(self, timeout=None, allowed_magic_methods=None):
+ self.timeout = timeout if timeout is not None else self.__rl_tmax__
+ self.allowed_magic_methods = (__allowed_magic_methods__ if allowed_magic_methods==True
+ else allowed_magic_methods) if allowed_magic_methods else []
+ if isPy3:
+ import builtins
+ self.__rl_gen_range__ = builtins.range
+ else:
+ import __builtin__ as builtins
+ self.__rl_gen_range__ = builtins.xrange
+
+ self.__rl_real_iter__ = builtins.iter
+
+ class __rl_dict__(dict):
+ def __new__(cls, *args,**kwds):
+ if len(args)==1 and not isinstance(args[0],collectionsMapping):
+ try:
+ it = self.__real_iter__(args[0])
+ except TypeError:
+ pass
+ else:
+ args = (self.__rl_getiter__(it),)
+ return dict.__new__(cls,*args,**kwds)
+
+ class __rl_missing_func__(object):
+ def __init__(self,name):
+ self.__name__ = name
+ def __call__(self,*args,**kwds):
+ raise BadCode('missing global %s' % self.__name__)
+
+ self.real_bi = builtins
+ self.bi_replace = (
+ ('open',__rl_missing_func__('open')),
+ ('iter',self.__rl_getiter__),
+ ) + ((
+ ) if isPy3 else (
+ ('file',__rl_missing_func__('file')),
+ ))
+
+ __rl_safe_builtins__.update({_:getattr(builtins,_) for _ in
+ ('''None False True abs bool callable chr complex divmod float hash hex id int
+ isinstance issubclass len oct ord range repr round slice str tuple setattr
+ classmethod staticmethod property divmod next object getattr dict iter pow list
+ type max min sum enumerate zip hasattr filter map any all sorted reversed range
+ set frozenset
+
+ ArithmeticError AssertionError AttributeError BaseException BufferError BytesWarning
+ DeprecationWarning EOFError EnvironmentError Exception FloatingPointError FutureWarning
+ GeneratorExit IOError ImportError ImportWarning IndentationError IndexError KeyError
+ KeyboardInterrupt LookupError MemoryError NameError NotImplementedError OSError
+ OverflowError PendingDeprecationWarning ReferenceError RuntimeError RuntimeWarning
+ StopIteration SyntaxError SyntaxWarning SystemError SystemExit TabError TypeError
+ UnboundLocalError UnicodeDecodeError UnicodeEncodeError UnicodeError UnicodeTranslateError
+ UnicodeWarning UserWarning ValueError Warning ZeroDivisionError
+ ''' + ('__build_class__' if isPy3
+ else 'basestring cmp long unichr unicode xrange StandardError reduce apply')).split()})
+
+ self.__rl_builtins__ = __rl_builtins__ = {_:__rl_missing_func__(_) for _ in dir(builtins) if callable(getattr(builtins,_))}
+ __rl_builtins__.update(__rl_safe_builtins__)
+
+ #these are used in the tree visitor
+ __rl_builtins__['__rl_add__'] = self.__rl_add__
+ __rl_builtins__['__rl_mult__'] = self.__rl_mult__
+ __rl_builtins__['__rl_pow__'] = self.__rl_pow__
+ __rl_builtins__['__rl_sd__'] = self.__rl_sd__
+ __rl_builtins__['__rl_augAssign__'] = self.__rl_augAssign__
+ __rl_builtins__['__rl_getitem__'] = self.__rl_getitem__
+ __rl_builtins__['__rl_getattr__'] = self.__rl_getattr__
+ __rl_builtins__['__rl_getiter__'] = self.__rl_getiter__
+ __rl_builtins__['__rl_max_len__'] = self.__rl_max_len__
+ __rl_builtins__['__rl_max_pow_digits__'] = self.__rl_max_pow_digits__
+ __rl_builtins__['__rl_iter_unpack_sequence__'] = self.__rl_iter_unpack_sequence__
+ __rl_builtins__['__rl_unpack_sequence__'] = self.__rl_unpack_sequence__
+ __rl_builtins__['__rl_apply__'] = lambda func,*args,**kwds: self.__rl_apply__(func,args,kwds)
+ __rl_builtins__['__rl_SafeIter__'] = __rl_SafeIter__
+
+ #these are tested builtins
+ __rl_builtins__['getattr'] = self.__rl_getattr__
+ __rl_builtins__['dict'] = __rl_dict__
+ __rl_builtins__['iter'] = self.__rl_getiter__
+ __rl_builtins__['pow'] = self.__rl_pow__
+ __rl_builtins__['list'] = self.__rl_list__
+ __rl_builtins__['type'] = self.__rl_type__
+ __rl_builtins__['max'] = self.__rl_max__
+ __rl_builtins__['min'] = self.__rl_min__
+ __rl_builtins__['sum'] = self.__rl_sum__
+ __rl_builtins__['enumerate'] = self.__rl_enumerate__
+ __rl_builtins__['zip'] = self.__rl_zip__
+ __rl_builtins__['hasattr'] = self.__rl_hasattr__
+ __rl_builtins__['filter'] = self.__rl_filter__
+ __rl_builtins__['map'] = self.__rl_map__
+ __rl_builtins__['any'] = self.__rl_any__
+ __rl_builtins__['all'] = self.__rl_all__
+ __rl_builtins__['sorted'] = self.__rl_sorted__
+ __rl_builtins__['reversed'] = self.__rl_reversed__
+ __rl_builtins__['range'] = self.__rl_range__
+ __rl_builtins__['set'] = self.__rl_set__
+ __rl_builtins__['frozenset'] = self.__rl_frozenset__
+ if not isPy3:
+ __rl_builtins__['reduce'] = self.__rl_reduce__
+ __rl_builtins__['xrange'] = self.__rl_xrange__
+ __rl_builtins__['apply'] = self.__rl_apply__
+
+ def __rl_type__(self,*args):
+ if len(args)==1: return type(*args)
+ raise BadCode('type call error')
+
+ def __rl_check__(self):
+ if self.__time_time__() >= self.__rl_limit__:
+ raise BadCode('Resources exceeded')
+
+ def __rl_sd__(self,obj):
+ return obj
+
+ def __rl_getiter__(self,it):
+ return __rl_SafeIter__(it,owner=self.__weakref_ref__(self))
+
+ def __rl_max__(self,arg,*args,**kwds):
+ if args:
+ arg = [arg]
+ arg.extend(args)
+ return max(self.__rl_args_iter__(arg),**kwds)
+
+ def __rl_min__(self,arg,*args,**kwds):
+ if args:
+ arg = [arg]
+ arg.extend(args)
+ return min(self.__rl_args_iter__(arg),**kwds)
+
+ def __rl_sum__(self, sequence, start=0):
+ return sum(self.__rl_args_iter__(sequence), start)
+
+ def __rl_enumerate__(self, seq):
+ return enumerate(self.__rl_args_iter__(seq))
+
+ def __rl_zip__(self,*args):
+ return zip(*[self.__rl_args_iter__(self.__rl_getitem__(args, i)) for i in range(len(args))])
+
+ def __rl_hasattr__(self, obj, name):
+ try:
+ self.__rl_getattr__(obj, name)
+ except (AttributeError, BadCode, TypeError):
+ return False
+ return True
+
+ def __rl_filter__(self, f, seq):
+ return filter(f,self.__rl_args_iter__(seq))
+
+ def __rl_map__(self, f, seq):
+ return map(f,self.__rl_args_iter__(seq))
+
+ def __rl_any__(self, seq):
+ return any(self.__rl_args_iter__(seq))
+
+ def __rl_all__(self, seq):
+ return all(self.__rl_args_iter__(seq))
+
+ def __rl_sorted__(self, seq, **kwds):
+ return sorted(self.__rl_args_iter__(seq),**kwds)
+
+ def __rl_reversed__(self, seq):
+ return self.__rl_args_iter__(reversed(seq))
+
+ if not isPy3:
+ def __rl_reduce__(self, f, seq, initial=__rl_undef__):
+ if initial is __rl_undef__:
+ return reduce(f, self.__rl_args_iter__(seq))
+ else:
+ return reduce(f, self.__rl_args_iter__(seq), initial)
+ def __rl_range__(self,start,*args):
+ return list(self.__rl_getiter__(range(start,*args)))
+ def __rl_xrange__(self,start,*args):
+ return self.__rl_getiter__(xrange(start,*args))
+ else:
+ def __rl_range__(self,start,*args):
+ return self.__rl_getiter__(range(start,*args))
+
+ def __rl_set__(self, it):
+ return set(self.__rl_args_iter__(it))
+
+ def __rl_frozenset__(self, it):
+ return frozenset(self.__rl_args_iter__(it))
+
+ def __rl_iter_unpack_sequence__(self, it, spec, _getiter_):
+ """Protect sequence unpacking of targets in a 'for loop'.
+
+ The target of a for loop could be a sequence.
+ For example "for a, b in it"
+ => Each object from the iterator needs guarded sequence unpacking.
+ """
+ # The iteration itself needs to be protected as well.
+ for ob in _getiter_(it):
+ yield self.__rl_unpack_sequence__(ob, spec, _getiter_)
+
+ def __rl_unpack_sequence__(self, it, spec, _getiter_):
+ """Protect nested sequence unpacking.
+
+ Protect the unpacking of 'it' by wrapping it with '_getiter_'.
+ Furthermore for each child element, defined by spec,
+ __rl_unpack_sequence__ is called again.
+
+ Have a look at transformer.py 'gen_unpack_spec' for a more detailed
+ explanation.
+ """
+ # Do the guarded unpacking of the sequence.
+ ret = list(self.__rl__getiter__(it))
+
+ # If the sequence is shorter then expected the interpreter will raise
+ # 'ValueError: need more than X value to unpack' anyway
+ # => No childs are unpacked => nothing to protect.
+ if len(ret) < spec['min_len']:
+ return ret
+
+ # For all child elements do the guarded unpacking again.
+ for (idx, child_spec) in spec['childs']:
+ ret[idx] = self.__rl_unpack_sequence__(ret[idx], child_spec, _getiter_)
+ return ret
+
+ def __rl_is_allowed_name__(self, name):
+ """Check names if they are allowed.
+ If ``allow_magic_methods is True`` names in `__allowed_magic_methods__`
+ are additionally allowed although their names start with `_`.
+ """
+ if isinstance(name,strTypes):
+ if name in __rl_unsafe__ or (name.startswith('__')
+ and name!='__'
+ and name not in self.allowed_magic_methods):
+ raise BadCode('unsafe access of %s' % name)
+
+ def __rl_getattr__(self, obj, a, *args):
+ if isinstance(obj, strTypes) and a=='format':
+ raise BadCode('%s.format is not implemented' % type(obj))
+ self.__rl_is_allowed_name__(a)
+ return getattr(obj,a,*args)
+
+ def __rl_getitem__(self, obj, a):
+ if type(a) is self.__slicetype__:
+ if a.step is not None:
+ v = obj[a]
+ else:
+ start = a.start
+ stop = a.stop
+ if start is None:
+ start = 0
+ if stop is None:
+ v = obj[start:]
+ else:
+ v = obj[start:stop]
+ return v
+ elif isinstance(a,strTypes):
+ self.__rl_is_allowed_name__(a)
+ return obj[a]
+ return obj[a]
+
+ __rl_tmax__ = 5
+ __rl_max_len__ = 100000
+ __rl_max_pow_digits__ = 100
+
+ def __rl_add__(self, a, b):
+ if (hasattr(a, '__len__') and hasattr(b, '__len__')
+ and (len(a) + len(b)) > self.__rl_max_len__):
+ raise BadCode("excessive length")
+ return a + b
+
+ def __rl_mult__(self, a, b):
+ if ((hasattr(a, '__len__') and b * len(a) > self.__rl_max_len__)
+ or (hasattr(b, '__len__') and a * len(b) > self.__rl_max_len__)):
+ raise BadCode("excessive length")
+ return a * b
+
+ def __rl_pow__(self, a, b):
+ try:
+ if b>0:
+ if int(b*math_log10(a)+1)>self.__rl_max_pow_digits__:
+ raise BadCode
+ except:
+ raise BadCode('%r**%r invalid or too large' % (a,b))
+ return a ** b
+
+ def __rl_augAssign__(self,op,v,i):
+ if op=='+=': return self.__rl_add__(v,i)
+ if op=='-=': return v-i
+ if op=='*=': return self.__rl_mult__(v,i)
+ if op=='/=': return v/i
+ if op=='%=': return v%i
+ if op=='**=': return self.__rl_pow__(v,i)
+ if op=='<<=': return v<<i
+ if op=='>>=': return v>>i
+ if op=='|=': return v|i
+ if op=='^=': return v^i
+ if op=='&=': return v&i
+ if op=='//=': return v//i
+
+ def __rl_apply__(self, func, args, kwds):
+ obj = getattr(func,'__self__',None)
+ if obj:
+ if isinstance(obj,dict) and func.__name__ in ('pop','setdefault','get', 'popitem'):
+ self.__rl_is_allowed_name__(args[0])
+ return func(*[a for a in self.__rl_getiter__(args)], **{k:v for k,v in kwds.items()})
+
+ def __rl_args_iter__(self,*args):
+ if len(args) == 1:
+ i = args[0]
+ # Don't double-wrap
+ if isinstance(i, __rl_SafeIter__):
+ return i
+ if not isinstance(i,self.__rl_gen_range__):
+ return self.__rl_getiter__(i)
+ return self.__rl_getiter__(iter(*args))
+
+ def __rl_list__(self,it):
+ return list(self.__rl_getiter__(it))
+
+ def __rl_compile__(self, src, fname='<string>', mode="eval", flags=0, inherit=True, visit=None):
+ names_seen = {}
+ if not visit:
+ bcode = compile(src, fname, mode=mode, flags=flags, dont_inherit=not inherit)
+ else:
+ astc = ast.parse(src, fname, mode)
+ if eval_debug>0:
+ print('pre:\n%s\n'%astFormat(astc))
+ astc = visit(astc)
+ if eval_debug>0:
+ print('post:\n%s\n'%astFormat(astc))
+ bcode = compile(astc, fname, mode=mode)
+ return bcode, names_seen
+
+ def __rl_safe_eval__(self, expr, g, l, mode, timeout=None, allowed_magic_methods=None, __frame_depth__=3):
+ bcode, ns = self.__rl_compile__(expr, fname='<string>', mode=mode, flags=0, inherit=True,
+ visit=UntrustedAstTransformer(nameIsAllowed=self.__rl_is_allowed_name__).visit)
+ if None in (l,g):
+ G = sys._getframe(__frame_depth__)
+ L = G.f_locals.copy() if l is None else l
+ G = G.f_globals.copy() if g is None else g
+ else:
+ G = g
+ L = l
+ obi = (G['__builtins__'],) if '__builtins__' in G else False
+ G['__builtins__'] = self.__rl_builtins__
+ self.__rl_limit__ = self.__time_time__() + (timeout if timeout is not None else self.timeout)
+ if allowed_magic_methods is not None:
+ self.allowed_magic_methods = ( __allowed_magic_methods__ if allowed_magic_methods==True
+ else allowed_magic_methods) if allowed_magic_methods else []
+ sbi = [].append
+ bi = self.real_bi
+ bir = self.bi_replace
+ for n, r in bir:
+ sbi(getattr(bi,n))
+ setattr(bi,n,r)
+ try:
+ return eval(bcode,G,L)
+ finally:
+ sbi = sbi.__self__
+ for i, (n, r) in enumerate(bir):
+ setattr(bi,n,sbi[i])
+ if obi:
+ G['__builtins__'] = obi[0]
+
+class __rl_safe_eval__(object):
+ '''creates one environment and re-uses it'''
+ mode = 'eval'
+ def __init__(self):
+ self.env = None
+
+ def __call__(self, expr, g=None, l=None, timeout=None, allowed_magic_methods=None):
+ if not self.env: self.env = __RL_SAFE_ENV__(timeout=timeout, allowed_magic_methods=allowed_magic_methods)
+ return self.env.__rl_safe_eval__(expr, g, l, self.mode, timeout=timeout,
+ allowed_magic_methods=allowed_magic_methods,
+ __frame_depth__=2)
+
+class __rl_safe_exec__(__rl_safe_eval__):
+ mode = 'exec'
+
+rl_safe_exec = __rl_safe_exec__()
+rl_safe_eval = __rl_safe_eval__()
--- a/src/reportlab/lib/testutils.py Thu Oct 31 11:17:14 2019 +0000
+++ b/src/reportlab/lib/testutils.py Tue Jan 14 09:08:36 2020 +0000
@@ -154,7 +154,7 @@
val = value.replace('\n', '')
if self.pat.match(val):
- return eval(val,{})
+ return eval(val,{__builtins__:None})
else:
return value
--- a/src/reportlab/lib/utils.py Thu Oct 31 11:17:14 2019 +0000
+++ b/src/reportlab/lib/utils.py Tue Jan 14 09:08:36 2020 +0000
@@ -1,7 +1,7 @@
-#Copyright ReportLab Europe Ltd. 2000-2017
+#Copyright ReportLab Europe Ltd. 2000-2019
#see license.txt for license details
# $URI:$
-__version__='3.3.0'
+__version__='3.5.34'
__doc__='''Gazillions of miscellaneous internal utility functions'''
import os, sys, time, types, datetime, ast
@@ -11,6 +11,7 @@
from reportlab import isPy3
from reportlab.lib.logger import warnOnce
from reportlab.lib.rltempfile import get_rl_tempfile, get_rl_tempdir, _rl_getuid
+from . rl_safe_eval import rl_safe_exec, rl_safe_eval, safer_globals
try:
import cPickle as pickle
@@ -234,6 +235,7 @@
return s.encode('latin1') if isinstance(s,unicode) else s
def rl_exec(obj, G=None, L=None):
+ '''this is unsafe'''
if G is None:
frame = sys._getframe(1)
G = frame.f_globals
@@ -1503,14 +1505,6 @@
a = ' '.join(a)
return a
-def safer_globals(g=None):
- if g is None:
- g = sys._getframe(1).f_globals.copy()
- for name in ('__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__'):
- if name in g:
- del g[name]
- return g
-
###############################################################
#the following code has been (thanks to MIT license)
#freely adapted from https://github.com/frmdstryr/magicattr
--- a/src/reportlab/platypus/flowables.py Thu Oct 31 11:17:14 2019 +0000
+++ b/src/reportlab/platypus/flowables.py Tue Jan 14 09:08:36 2020 +0000
@@ -30,7 +30,7 @@
from reportlab.lib.rl_accel import fp_str
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT, TA_JUSTIFY
from reportlab.lib.styles import _baseFontName
-from reportlab.lib.utils import strTypes, safer_globals
+from reportlab.lib.utils import strTypes, rl_safe_exec
from reportlab.lib.abag import ABag
from reportlab.pdfbase import pdfutils
from reportlab.pdfbase.pdfmetrics import stringWidth
@@ -755,7 +755,7 @@
def wrap(self, availWidth, availHeight):
return (0,0)
def draw(self):
- exec(self.command, safer_globals(), {'canvas':self.canv})
+ rl_safe_exec(self.command, g=None, l={'canvas':self.canv})
def _nullCallable(*args,**kwds):
pass
--- a/tests/test_lib_colors.py Thu Oct 31 11:17:14 2019 +0000
+++ b/tests/test_lib_colors.py Tue Jan 14 09:08:36 2020 +0000
@@ -33,6 +33,13 @@
class ColorTestCase(unittest.TestCase):
""
+ def ctAssertRaisesRegex(self,ex,regex,func,*args,**kwds):
+ try:
+ a = self.assertRaisesRegex
+ except AttributeError:
+ a = self.assertRaisesRegexp
+ return a(ex,regex,func,*args,**kwds)
+
def test0(self):
"Test color2bw function on all named colors."
@@ -63,6 +70,15 @@
for thing in allRed:
assert colors.toColor(thing) == colors.red,"colors.toColor(%s)-->%s != colors.red(%s)" % (ascii(thing),ascii(colors.toColor(thing)),colors.red)
+ def test2a(self):
+ '''attempt to test toColor against simple attacks'''
+ ofn = outputfile('dumbo.txt')
+ self.assertRaises(ValueError,colors.toColor,"open(%s,'w').write('dumber and dumber')" % repr(ofn))
+ self.assertFalse(os.path.isfile(ofn),"toColor managed to create a file %s :("% repr(ofn))
+ self.assertRaises(ValueError,colors.toColor,"red.__class__.__bases__[0].__subclasses__()")
+ self.assertRaises(ValueError,colors.toColor,
+ '''(lambda fc=(lambda n: [c for c in ().__class__.__bases__[0].__subclasses__() if c.__name__ == n][0]): fc("function")(fc("code")(0,0,0,0,"KABOOM",(), (),(),"","",0,""),{})())()''')
+ self.assertEqual(colors.Blacker(colors.red,0.5),colors.toColor("Blacker(red,0.5)"))
def test3(self):
"Test roundtrip RGB to CMYK conversion."
@@ -159,19 +175,19 @@
self.assertEqual(HexColor(b'0xffffff'),Color(1,1,1,1))
self.assertEqual(HexColor(b'0xFFFFFF'),Color(1,1,1,1))
self.assertEqual(HexColor(b'16777215'),Color(1,1,1,1))
- self.assertRaisesRegexp(ValueError,r"invalid literal for int\(\) with base 10:.*ffffff",HexColor,b'ffffff')
+ self.ctAssertRaisesRegex(ValueError,r"invalid literal for int\(\) with base 10:.*ffffff",HexColor,b'ffffff')
self.assertEqual(HexColor(b'#FFFFFF', htmlOnly=True),Color(1,1,1,1))
- self.assertRaisesRegexp(ValueError,"not a hex string",HexColor,b'0xffffff',htmlOnly=True)
- self.assertRaisesRegexp(ValueError,"not a hex string",HexColor,b'16777215',htmlOnly=True)
+ self.ctAssertRaisesRegex(ValueError,"not a hex string",HexColor,b'0xffffff',htmlOnly=True)
+ self.ctAssertRaisesRegex(ValueError,"not a hex string",HexColor,b'16777215',htmlOnly=True)
self.assertEqual(HexColor(u'#ffffff'),Color(1,1,1,1))
self.assertEqual(HexColor(u'#FFFFFF'),Color(1,1,1,1))
self.assertEqual(HexColor(u'0xffffff'),Color(1,1,1,1))
self.assertEqual(HexColor(u'0xFFFFFF'),Color(1,1,1,1))
self.assertEqual(HexColor(u'16777215'),Color(1,1,1,1))
- self.assertRaisesRegexp(ValueError,r"invalid literal for int\(\) with base 10:.*ffffff",HexColor,u'ffffff')
+ self.ctAssertRaisesRegex(ValueError,r"invalid literal for int\(\) with base 10:.*ffffff",HexColor,u'ffffff')
self.assertEqual(HexColor(u'#FFFFFF', htmlOnly=True),Color(1,1,1,1))
- self.assertRaisesRegexp(ValueError,"not a hex string",HexColor,u'0xffffff',htmlOnly=True)
- self.assertRaisesRegexp(ValueError,"not a hex string",HexColor,u'16777215',htmlOnly=True)
+ self.ctAssertRaisesRegex(ValueError,"not a hex string",HexColor,u'0xffffff',htmlOnly=True)
+ self.ctAssertRaisesRegex(ValueError,"not a hex string",HexColor,u'16777215',htmlOnly=True)
def makeSuite():
return makeSuiteForClasses(ColorTestCase)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test_lib_rl_safe_eval.py Tue Jan 14 09:08:36 2020 +0000
@@ -0,0 +1,167 @@
+#Copyright ReportLab Europe Ltd. 2000-2017
+#see license.txt for license details
+"""Tests for reportlab.lib.rl_eval
+"""
+__version__='3.5.33'
+from reportlab.lib.testutils import setOutDir,makeSuiteForClasses, printLocation
+setOutDir(__name__)
+import os, time, sys
+import reportlab
+from reportlab import rl_config
+import unittest
+from reportlab.lib import colors
+from reportlab.lib.utils import rl_safe_eval, rl_safe_exec, isPy3, annotateException
+from reportlab.lib.rl_safe_eval import BadCode
+
+testObj = [1,('a','b',2),{'A':1,'B':2.0},"32"]
+class TestClass(object):
+ a = 1
+ format = 3
+testInst = TestClass()
+def testFunc(bad=False):
+ return open('/tmp/myfile','r') if bad else testObj
+
+class SafeEvalTestSequenceMeta(type):
+ def __new__(cls, name, bases, cdict):
+ def genTest(kind, expr,**kwds):
+ def test(self):
+ getattr(self,kind+'s')(expr,**kwds)
+ return test
+
+ for kind, _data in (
+ (
+ 'work',
+ (
+ '[i for i in range(10)]',
+ '3**4',
+ '3*"A"',
+ '3+4',
+ '(3,4)',
+ 'SafeEvalTestCase',
+ ("testObj",dict(g=dict(testObj=testObj))),
+ "(lambda x,y:[x,y])('a',2)",
+ "(lambda x:[x])(2)",
+ "(lambda *args:[args])(2)",
+ "(lambda y: (lambda f:f(y))(lambda x: x+1))(2)",
+ "(lambda f: lambda n: (1,(1,(1,(1,f(n-1))))) if n else 1)(lambda x:x)(5)",
+ "((lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args)))) (lambda f: lambda n: (1,(1,(1,(1,f(n-1))))) if n else 1)(30))",
+ 'list(range(1000))[1:1000:100]',
+ 'tuple(range(1000))[1:1000:100]',
+ 'dict(a=1)["a"]',
+ 'dict(a=1).setdefault("a",2)',
+ 'dict(a=1).get("a",2)',
+ 'dict(a=1).pop("a",2)',
+ '{"_":1+_ for _ in (1,2)}.pop(1,None)',
+ '(type(1),type(str),type(testObj),type(TestClass))',
+ '1 if True else "a"',
+ '1 if False else "a"',
+ 'testFunc(bad=False)',
+ '(min([1]),min(1,2),max([1]),max(1,2))',
+ '(sum((1,2,3)),sum((1,2,3),-1))',
+ 'list(enumerate((1,2,3)))',
+ 'list(zip((1,2,3),("a","b","c")))',
+ '(hasattr(testInst,"b"),hasattr(testInst,"a"))',
+ (None if isPy3 else '(reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]),reduce(lambda x, y: x+y, [[1], [2]],[0]))'),
+ 'list(map(lambda x: (x+13,chr(x)),(1,2,3,4)))',
+ '(any([1]),any([]),all([]),all([1,None]),all([1,2]))',
+ '(getattr(testInst,"a"),getattr(testInst,"a",12),getattr(testInst,"xxx",13))',
+ 'list(sorted([3,4,1,2,0],reverse=True))',
+ 'list(reversed([3,4,1,2,0]))',
+ 'list(range(1,10,3))',
+ None if isPy3 else 'list(xrange(1,10,3))',
+ '({1,2,3},set([4,5,6]),frozenset([7,8,9]),{i for i in range(1,10,3)})',
+ '"%s%s" % (1,2)',
+ None if isPy3 else 'apply(lambda x,y,a="a",b="b": (x,y,a,b), (1,2),dict(a="x",b="y"))',
+ )
+ ),
+ (
+ 'fail',
+ (
+ 'open("/tmp/myfile")',
+ None if isPy3 else 'file("/tmp/myfile")',
+ 'SafeEvalTestCase.__module__',
+ ("testInst.__class__.__bases__[0].__subclasses__()",dict(g=dict(testInst=testInst))),
+ "10**200**200",
+ "pow(10,200)",
+ '__import__("reportlab")',
+ ('(lambda i: [i for i in ((i, 1) for j in range(1000000))][-1])(1)',dict(timeout=0.1)),
+ ('[i for i in ((j, 1) for j in range(1000000))][-1]',dict(timeout=0.1)),
+ ('''((lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args)))) (lambda f: lambda n: (1,(1,(1,(1,f(n-1))))) if n else 1)(300))''',
+ dict(timeout=0.1,exception=RuntimeError)),
+ 'dict(__class__=1)["__class__"]',
+ 'dict(a=1).setdefault("__class__",2)',
+ 'dict(a=1).get("__class__",2)',
+ 'dict(a=1).pop("__class__",2)',
+ '{"__class__":1}["__class__"]',
+ '{"__class__":1}.setdefault("__class__",2)',
+ '{"__class__":1}.get("__class__",2)',
+ '{"__class__":1}.pop("__class__",2)',
+ '{"_":1 for _ in (1,2)}.pop("__class__",2)',
+ 'type("Devil",[dict],{__init__:lambda self:self.__class__})',
+ 'testFunc(bad=True)',
+ 'getattr(testInst,"__class__",14)',
+ '"{1}{2}".format(1,2)',
+ )
+ ),
+ ):
+ tfmt = 'test_ExpectedTo%s_%%02d' % kind.capitalize()
+ for i, expr in enumerate(_data):
+ if expr is None:
+ test = genTest('skip','')
+ else:
+ expr, kwds = expr if isinstance(expr,tuple) else (expr,{})
+ test = genTest(kind, expr,**kwds)
+ cdict[tfmt%i] = test
+ return type.__new__(cls, name, bases, cdict)
+
+def addMeta(mcs):
+ def wrap(cls):
+ ov = cls.__dict__.copy()
+ sl = ov.get('__slots__')
+ if sl is not None:
+ if isinstance(sl, str):
+ sl = [sl]
+ for slv in sl:
+ ov.pop(slv)
+ ov.pop('__dict__', None)
+ ov.pop('__weakref__', None)
+ if hasattr(cls, '__qualname__'):
+ ov['__qualname__'] = cls.__qualname__
+ return mcs(cls.__name__, cls.__bases__, ov)
+ return wrap
+
+@addMeta(SafeEvalTestSequenceMeta)
+class SafeEvalTestCase(unittest.TestCase):
+ def works(self, expr, g=None, l=None):
+ try:
+ answer = eval(expr,g,l)
+ result = rl_safe_eval(expr,g,l)
+ except:
+ print('expr=%r' % expr)
+ annotateException('\nexpr=%r\n' % expr)
+ self.assertEqual(answer,result,"rl_safe_eval(%r) = %r not expected %r" % (expr,result,answer))
+ def skips(self,*args,**kwds):
+ raise unittest.SkipTest
+ def fails(self, expr, g=None, l=None, timeout=None, exception=BadCode):
+ try:
+ result = rl_safe_eval(expr,g,l,timeout=timeout)
+ self.assertEqual(True,False,"rl_safe_eval(%r)=%r did not raise %s" % (expr,result,exception.__name__))
+ except exception:
+ return
+ except:
+ self.assertEqual(True,False,"rl_safe_eval(%r) raised %s: %s instead of %s" % (expr,sys.exc_info()[0].__name__,str(sys.exc_info()[1]),exception.__name__))
+
+GA = 'ga'
+class SafeEvalTestBasics(unittest.TestCase):
+ def test_001(self):
+ A=3
+ self.assertTrue(rl_safe_eval("A==3"))
+ def test_002(self):
+ self.assertTrue(rl_safe_eval("GA=='ga'"))
+
+def makeSuite():
+ return makeSuiteForClasses(SafeEvalTestCase,SafeEvalTestBasics)
+
+if __name__ == "__main__": #noruntests
+ unittest.TextTestRunner().run(makeSuite())
+ printLocation()
--- a/tests/test_paragraphs.py Thu Oct 31 11:17:14 2019 +0000
+++ b/tests/test_paragraphs.py Tue Jan 14 09:08:36 2020 +0000
@@ -5,7 +5,7 @@
from reportlab import xrange
from reportlab.lib.testutils import setOutDir,makeSuiteForClasses, outputfile, printLocation
setOutDir(__name__)
-import unittest
+import unittest, os
from reportlab.platypus import Paragraph, SimpleDocTemplate, XBox, Indenter, XPreformatted, PageBreak, Spacer
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.units import inch
@@ -17,6 +17,7 @@
from reportlab.pdfbase import ttfonts
from reportlab.pdfbase import pdfmetrics
from reportlab.lib.fonts import addMapping, tt2ps
+from reportlab.pdfgen.canvas import Canvas
(PAGE_WIDTH, PAGE_HEIGHT) = defaultPageSize
@@ -258,6 +259,18 @@
showBoundary=1)
template.build(story,
onFirstPage=myFirstPage, onLaterPages=myLaterPages)
+
+ def testMalColor(self):
+ '''attempt to test may inputs via span etc etc'''
+ styNormal = ParagraphStyle('normal')
+ ofn = outputfile('dumbo.txt')
+ canv = Canvas(outputfile('testMalColor.pdf'))
+ self.assertRaises(ValueError,Paragraph, '''<span color="toColor(open(%s,'w').write('dumber and dumber'))">AAA</span>''' % ofn, styNormal)
+ self.assertFalse(os.path.isfile(ofn),"toColor managed to create a file %s :("% repr(ofn))
+ self.assertRaises(ValueError,Paragraph,
+ '''<span color="(lambda fc=(lambda n: [c for c in ().__class__.__bases__[0].__subclasses__() if c.__name__ == n][0]): fc('function')(fc('code')(0,0,0,0,'KABOOM',(), (),(),'','',0,''),{})())()">AAA</span>''',styNormal)
+ #w, h = p.wrap(5*72,7*72)
+ #p.drawOn(canv,36,6.5*72)
if rtlSupport:
def testBidi(self):
@@ -440,7 +453,7 @@
styBI = ParagraphStyle('BI',fontName=fontNameBI)
self.assertRaises(ValueError,Paragraph,'aaaa <b><i>bibibi</b></i> ccccc',stySTD)
self.assertRaises(ValueError,Paragraph,'AAAA <b><i>BIBIBI</b></i> CCCCC',styBI)
-
+
def makeSuite():
return makeSuiteForClasses(ParagraphTestCase)