summaryrefslogtreecommitdiffstats
path: root/lib/kross/python/scripts/RestrictedPython
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-01-20 01:29:50 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-01-20 01:29:50 +0000
commit8362bf63dea22bbf6736609b0f49c152f975eb63 (patch)
tree0eea3928e39e50fae91d4e68b21b1e6cbae25604 /lib/kross/python/scripts/RestrictedPython
downloadkoffice-8362bf63dea22bbf6736609b0f49c152f975eb63.tar.gz
koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.zip
Added old abandoned KDE3 version of koffice
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/koffice@1077364 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'lib/kross/python/scripts/RestrictedPython')
-rw-r--r--lib/kross/python/scripts/RestrictedPython/Eval.py118
-rw-r--r--lib/kross/python/scripts/RestrictedPython/Guards.py136
-rw-r--r--lib/kross/python/scripts/RestrictedPython/Limits.py46
-rw-r--r--lib/kross/python/scripts/RestrictedPython/Makefile.am4
-rw-r--r--lib/kross/python/scripts/RestrictedPython/MutatingWalker.py74
-rw-r--r--lib/kross/python/scripts/RestrictedPython/PrintCollector.py23
-rw-r--r--lib/kross/python/scripts/RestrictedPython/RCompile.py235
-rw-r--r--lib/kross/python/scripts/RestrictedPython/RestrictionMutator.py372
-rw-r--r--lib/kross/python/scripts/RestrictedPython/SelectCompiler.py28
-rw-r--r--lib/kross/python/scripts/RestrictedPython/Utilities.py77
-rw-r--r--lib/kross/python/scripts/RestrictedPython/__init__.py19
11 files changed, 1132 insertions, 0 deletions
diff --git a/lib/kross/python/scripts/RestrictedPython/Eval.py b/lib/kross/python/scripts/RestrictedPython/Eval.py
new file mode 100644
index 00000000..841067a1
--- /dev/null
+++ b/lib/kross/python/scripts/RestrictedPython/Eval.py
@@ -0,0 +1,118 @@
+##############################################################################
+#
+# Copyright (c) 2001 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
+#
+##############################################################################
+"""Restricted Python Expressions
+
+$Id: Eval.py 24763 2004-05-17 05:59:28Z philikon $
+"""
+
+__version__='$Revision: 1.6 $'[11:-2]
+
+from RestrictedPython import compile_restricted_eval
+
+from string import translate, strip
+import string
+
+nltosp = string.maketrans('\r\n',' ')
+
+default_guarded_getattr = getattr # No restrictions.
+
+def default_guarded_getitem(ob, index):
+ # No restrictions.
+ return ob[index]
+
+PROFILE = 0
+
+class RestrictionCapableEval:
+ """A base class for restricted code."""
+
+ globals = {'__builtins__': None}
+ rcode = None # restricted
+ ucode = None # unrestricted
+ used = None
+
+ def __init__(self, expr):
+ """Create a restricted expression
+
+ where:
+
+ expr -- a string containing the expression to be evaluated.
+ """
+ expr = strip(expr)
+ self.__name__ = expr
+ expr = translate(expr, nltosp)
+ self.expr = expr
+ self.prepUnrestrictedCode() # Catch syntax errors.
+
+ def prepRestrictedCode(self):
+ if self.rcode is None:
+ if PROFILE:
+ from time import clock
+ start = clock()
+ co, err, warn, used = compile_restricted_eval(
+ self.expr, '<string>')
+ if PROFILE:
+ end = clock()
+ print 'prepRestrictedCode: %d ms for %s' % (
+ (end - start) * 1000, `self.expr`)
+ if err:
+ raise SyntaxError, err[0]
+ self.used = tuple(used.keys())
+ self.rcode = co
+
+ def prepUnrestrictedCode(self):
+ if self.ucode is None:
+ # Use the standard compiler.
+ co = compile(self.expr, '<string>', 'eval')
+ if self.used is None:
+ # Examine the code object, discovering which names
+ # the expression needs.
+ names=list(co.co_names)
+ used={}
+ i=0
+ code=co.co_code
+ l=len(code)
+ LOAD_NAME=101
+ HAVE_ARGUMENT=90
+ while(i < l):
+ c=ord(code[i])
+ if c==LOAD_NAME:
+ name=names[ord(code[i+1])+256*ord(code[i+2])]
+ used[name]=1
+ i=i+3
+ elif c >= HAVE_ARGUMENT: i=i+3
+ else: i=i+1
+ self.used=tuple(used.keys())
+ self.ucode=co
+
+ def eval(self, mapping):
+ # This default implementation is probably not very useful. :-(
+ # This is meant to be overridden.
+ self.prepRestrictedCode()
+ code = self.rcode
+ d = {'_getattr_': default_guarded_getattr,
+ '_getitem_': default_guarded_getitem}
+ d.update(self.globals)
+ has_key = d.has_key
+ for name in self.used:
+ try:
+ if not has_key(name):
+ d[name] = mapping[name]
+ except KeyError:
+ # Swallow KeyErrors since the expression
+ # might not actually need the name. If it
+ # does need the name, a NameError will occur.
+ pass
+ return eval(code, d)
+
+ def __call__(self, **kw):
+ return self.eval(kw)
diff --git a/lib/kross/python/scripts/RestrictedPython/Guards.py b/lib/kross/python/scripts/RestrictedPython/Guards.py
new file mode 100644
index 00000000..4fbdcad1
--- /dev/null
+++ b/lib/kross/python/scripts/RestrictedPython/Guards.py
@@ -0,0 +1,136 @@
+##############################################################################
+#
+# Copyright (c) 2001 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
+#
+##############################################################################
+__version__ = '$Revision: 1.14 $'[11:-2]
+
+import exceptions
+
+# This tiny set of safe builtins is extended by users of the module.
+# AccessControl.ZopeGuards contains a large set of wrappers for builtins.
+# DocumentTemplate.DT_UTil contains a few.
+
+safe_builtins = {}
+
+for name in ['False', 'None', 'True', 'abs', 'basestring', 'bool', 'callable',
+ 'chr', 'cmp', 'complex', 'divmod', 'float', 'hash',
+ 'hex', 'id', 'int', 'isinstance', 'issubclass', 'len',
+ 'long', 'oct', 'ord', 'pow', 'range', 'repr', 'round',
+ 'str', 'tuple', 'unichr', 'unicode', 'xrange', 'zip']:
+
+ safe_builtins[name] = __builtins__[name]
+
+# Wrappers provided by this module:
+# delattr
+# setattr
+
+# Wrappers provided by ZopeGuards:
+# __import__
+# apply
+# dict
+# enumerate
+# filter
+# getattr
+# hasattr
+# iter
+# list
+# map
+# max
+# min
+# sum
+
+# Builtins that are intentionally disabled
+# compile - don't let them produce new code
+# dir - a general purpose introspector, probably hard to wrap
+# execfile - no direct I/O
+# file - no direct I/O
+# globals - uncontrolled namespace access
+# input - no direct I/O
+# locals - uncontrolled namespace access
+# open - no direct I/O
+# raw_input - no direct I/O
+# vars - uncontrolled namespace access
+
+# There are several strings that describe Python. I think there's no
+# point to including these, although they are obviously safe:
+# copyright, credits, exit, help, license, quit
+
+# Not provided anywhere. Do something about these? Several are
+# related to new-style classes, which we are too scared of to support
+# <0.3 wink>. coerce, buffer, and reload are esoteric enough that no
+# one should care.
+
+# buffer
+# classmethod
+# coerce
+# eval
+# intern
+# object
+# property
+# reload
+# slice
+# staticmethod
+# super
+# type
+
+for name in dir(exceptions):
+ if name[0] != "_":
+ safe_builtins[name] = getattr(exceptions, name)
+
+def _write_wrapper():
+ # Construct the write wrapper class
+ def _handler(secattr, error_msg):
+ # Make a class method.
+ def handler(self, *args):
+ try:
+ f = getattr(self.ob, secattr)
+ except AttributeError:
+ raise TypeError, error_msg
+ f(*args)
+ return handler
+ class Wrapper:
+ def __len__(self):
+ # Required for slices with negative bounds.
+ return len(self.ob)
+ def __init__(self, ob):
+ self.__dict__['ob'] = ob
+ __setitem__ = _handler('__guarded_setitem__',
+ 'object does not support item or slice assignment')
+ __delitem__ = _handler('__guarded_delitem__',
+ 'object does not support item or slice assignment')
+ __setattr__ = _handler('__guarded_setattr__',
+ 'attribute-less object (assign or del)')
+ __delattr__ = _handler('__guarded_delattr__',
+ 'attribute-less object (assign or del)')
+ return Wrapper
+
+def _full_write_guard():
+ # Nested scope abuse!
+ # safetype and Wrapper variables are used by guard()
+ safetype = {dict: True, list: True}.has_key
+ Wrapper = _write_wrapper()
+ def guard(ob):
+ # Don't bother wrapping simple types, or objects that claim to
+ # handle their own write security.
+ if safetype(type(ob)) or hasattr(ob, '_guarded_writes'):
+ return ob
+ # Hand the object to the Wrapper instance, then return the instance.
+ return Wrapper(ob)
+ return guard
+full_write_guard = _full_write_guard()
+
+def guarded_setattr(object, name, value):
+ setattr(full_write_guard(object), name, value)
+safe_builtins['setattr'] = guarded_setattr
+
+def guarded_delattr(object, name):
+ delattr(full_write_guard(object), name)
+safe_builtins['delattr'] = guarded_delattr
diff --git a/lib/kross/python/scripts/RestrictedPython/Limits.py b/lib/kross/python/scripts/RestrictedPython/Limits.py
new file mode 100644
index 00000000..3b782e65
--- /dev/null
+++ b/lib/kross/python/scripts/RestrictedPython/Limits.py
@@ -0,0 +1,46 @@
+##############################################################################
+#
+# Copyright (c) 2001 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
+#
+##############################################################################
+
+__version__='$Revision: 1.5 $'[11:-2]
+
+limited_builtins = {}
+
+def limited_range(iFirst, *args):
+ # limited range function from Martijn Pieters
+ RANGELIMIT = 1000
+ if not len(args):
+ iStart, iEnd, iStep = 0, iFirst, 1
+ elif len(args) == 1:
+ iStart, iEnd, iStep = iFirst, args[0], 1
+ elif len(args) == 2:
+ iStart, iEnd, iStep = iFirst, args[0], args[1]
+ else:
+ raise AttributeError, 'range() requires 1-3 int arguments'
+ if iStep == 0: raise ValueError, 'zero step for range()'
+ iLen = int((iEnd - iStart) / iStep)
+ if iLen < 0: iLen = 0
+ if iLen >= RANGELIMIT: raise ValueError, 'range() too large'
+ return range(iStart, iEnd, iStep)
+limited_builtins['range'] = limited_range
+
+def limited_list(seq):
+ if isinstance(seq, str):
+ raise TypeError, 'cannot convert string to list'
+ return list(seq)
+limited_builtins['list'] = limited_list
+
+def limited_tuple(seq):
+ if isinstance(seq, str):
+ raise TypeError, 'cannot convert string to tuple'
+ return tuple(seq)
+limited_builtins['tuple'] = limited_tuple
diff --git a/lib/kross/python/scripts/RestrictedPython/Makefile.am b/lib/kross/python/scripts/RestrictedPython/Makefile.am
new file mode 100644
index 00000000..3d5b3c73
--- /dev/null
+++ b/lib/kross/python/scripts/RestrictedPython/Makefile.am
@@ -0,0 +1,4 @@
+restrictedpythondir = $(kde_datadir)/kross/python/RestrictedPython
+restrictedpython_SCRIPTS = __init__.py Eval.py Guards.py Limits.py \
+ MutatingWalker.py PrintCollector.py RCompile.py RestrictionMutator.py \
+ SelectCompiler.py Utilities.py
diff --git a/lib/kross/python/scripts/RestrictedPython/MutatingWalker.py b/lib/kross/python/scripts/RestrictedPython/MutatingWalker.py
new file mode 100644
index 00000000..b0b8c9ce
--- /dev/null
+++ b/lib/kross/python/scripts/RestrictedPython/MutatingWalker.py
@@ -0,0 +1,74 @@
+##############################################################################
+#
+# Copyright (c) 2001 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
+#
+##############################################################################
+
+__version__='$Revision: 1.6 $'[11:-2]
+
+from SelectCompiler import ast
+
+ListType = type([])
+TupleType = type(())
+SequenceTypes = (ListType, TupleType)
+
+class MutatingWalker:
+
+ def __init__(self, visitor):
+ self.visitor = visitor
+ self._cache = {}
+
+ def defaultVisitNode(self, node, walker=None, exclude=None):
+ for name, child in node.__dict__.items():
+ if exclude is not None and name in exclude:
+ continue
+ v = self.dispatchObject(child)
+ if v is not child:
+ # Replace the node.
+ node.__dict__[name] = v
+ return node
+
+ def visitSequence(self, seq):
+ res = seq
+ for idx in range(len(seq)):
+ child = seq[idx]
+ v = self.dispatchObject(child)
+ if v is not child:
+ # Change the sequence.
+ if type(res) is ListType:
+ res[idx : idx + 1] = [v]
+ else:
+ res = res[:idx] + (v,) + res[idx + 1:]
+ return res
+
+ def dispatchObject(self, ob):
+ '''
+ Expected to return either ob or something that will take
+ its place.
+ '''
+ if isinstance(ob, ast.Node):
+ return self.dispatchNode(ob)
+ elif type(ob) in SequenceTypes:
+ return self.visitSequence(ob)
+ else:
+ return ob
+
+ def dispatchNode(self, node):
+ klass = node.__class__
+ meth = self._cache.get(klass, None)
+ if meth is None:
+ className = klass.__name__
+ meth = getattr(self.visitor, 'visit' + className,
+ self.defaultVisitNode)
+ self._cache[klass] = meth
+ return meth(node, self)
+
+def walk(tree, visitor):
+ return MutatingWalker(visitor).dispatchNode(tree)
diff --git a/lib/kross/python/scripts/RestrictedPython/PrintCollector.py b/lib/kross/python/scripts/RestrictedPython/PrintCollector.py
new file mode 100644
index 00000000..15a1c180
--- /dev/null
+++ b/lib/kross/python/scripts/RestrictedPython/PrintCollector.py
@@ -0,0 +1,23 @@
+##############################################################################
+#
+# Copyright (c) 2001 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
+#
+##############################################################################
+
+__version__='$Revision: 1.4 $'[11:-2]
+
+class PrintCollector:
+ '''Collect written text, and return it when called.'''
+ def __init__(self):
+ self.txt = []
+ def write(self, text):
+ self.txt.append(text)
+ def __call__(self):
+ return ''.join(self.txt)
diff --git a/lib/kross/python/scripts/RestrictedPython/RCompile.py b/lib/kross/python/scripts/RestrictedPython/RCompile.py
new file mode 100644
index 00000000..0a538657
--- /dev/null
+++ b/lib/kross/python/scripts/RestrictedPython/RCompile.py
@@ -0,0 +1,235 @@
+##############################################################################
+#
+# Copyright (c) 2001 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
+#
+##############################################################################
+"""Compiles restricted code using the compiler module from the
+Python standard library.
+"""
+
+__version__='$Revision: 1.6 $'[11:-2]
+
+from compiler import ast, parse, misc, syntax, pycodegen
+from compiler.pycodegen import AbstractCompileMode, Expression, \
+ Interactive, Module, ModuleCodeGenerator, FunctionCodeGenerator, findOp
+
+import MutatingWalker
+from RestrictionMutator import RestrictionMutator
+
+
+def niceParse(source, filename, mode):
+ try:
+ return parse(source, mode)
+ except:
+ # Try to make a clean error message using
+ # the builtin Python compiler.
+ try:
+ compile(source, filename, mode)
+ except SyntaxError:
+ raise
+ # Some other error occurred.
+ raise
+
+class RestrictedCompileMode(AbstractCompileMode):
+ """Abstract base class for hooking up custom CodeGenerator."""
+ # See concrete subclasses below.
+
+ def __init__(self, source, filename):
+ self.rm = RestrictionMutator()
+ AbstractCompileMode.__init__(self, source, filename)
+
+ def parse(self):
+ return niceParse(self.source, self.filename, self.mode)
+
+ def _get_tree(self):
+ tree = self.parse()
+ MutatingWalker.walk(tree, self.rm)
+ if self.rm.errors:
+ raise SyntaxError, self.rm.errors[0]
+ misc.set_filename(self.filename, tree)
+ syntax.check(tree)
+ return tree
+
+ def compile(self):
+ tree = self._get_tree()
+ gen = self.CodeGeneratorClass(tree)
+ self.code = gen.getCode()
+
+
+def compileAndTuplize(gen):
+ try:
+ gen.compile()
+ except SyntaxError, v:
+ return None, (str(v),), gen.rm.warnings, gen.rm.used_names
+ return gen.getCode(), (), gen.rm.warnings, gen.rm.used_names
+
+def compile_restricted_function(p, body, name, filename, globalize=None):
+ """Compiles a restricted code object for a function.
+
+ The function can be reconstituted using the 'new' module:
+
+ new.function(<code>, <globals>)
+
+ The globalize argument, if specified, is a list of variable names to be
+ treated as globals (code is generated as if each name in the list
+ appeared in a global statement at the top of the function).
+ """
+ gen = RFunction(p, body, name, filename, globalize)
+ return compileAndTuplize(gen)
+
+def compile_restricted_exec(s, filename='<string>'):
+ """Compiles a restricted code suite."""
+ gen = RModule(s, filename)
+ return compileAndTuplize(gen)
+
+def compile_restricted_eval(s, filename='<string>'):
+ """Compiles a restricted expression."""
+ gen = RExpression(s, filename)
+ return compileAndTuplize(gen)
+
+def compile_restricted(source, filename, mode):
+ """Replacement for the builtin compile() function."""
+ if mode == "single":
+ gen = RInteractive(source, filename)
+ elif mode == "exec":
+ gen = RModule(source, filename)
+ elif mode == "eval":
+ gen = RExpression(source, filename)
+ else:
+ raise ValueError("compile_restricted() 3rd arg must be 'exec' or "
+ "'eval' or 'single'")
+ gen.compile()
+ return gen.getCode()
+
+class RestrictedCodeGenerator:
+ """Mixin for CodeGenerator to replace UNPACK_SEQUENCE bytecodes.
+
+ The UNPACK_SEQUENCE opcode is not safe because it extracts
+ elements from a sequence without using a safe iterator or
+ making __getitem__ checks.
+
+ This code generator replaces use of UNPACK_SEQUENCE with calls to
+ a function that unpacks the sequence, performes the appropriate
+ security checks, and returns a simple list.
+ """
+
+ # Replace the standard code generator for assignments to tuples
+ # and lists.
+
+ def _gen_safe_unpack_sequence(self, num):
+ # We're at a place where UNPACK_SEQUENCE should be generated, to
+ # unpack num items. That's a security hole, since it exposes
+ # individual items from an arbitrary iterable. We don't remove
+ # the UNPACK_SEQUENCE, but instead insert a call to our _getiter_()
+ # wrapper first. That applies security checks to each item as
+ # it's delivered. codegen is (just) a bit messy because the
+ # iterable is already on the stack, so we have to do a stack swap
+ # to get things in the right order.
+ self.emit('LOAD_GLOBAL', '_getiter_')
+ self.emit('ROT_TWO')
+ self.emit('CALL_FUNCTION', 1)
+ self.emit('UNPACK_SEQUENCE', num)
+
+ def _visitAssSequence(self, node):
+ if findOp(node) != 'OP_DELETE':
+ self._gen_safe_unpack_sequence(len(node.nodes))
+ for child in node.nodes:
+ self.visit(child)
+
+ visitAssTuple = _visitAssSequence
+ visitAssList = _visitAssSequence
+
+ # Call to generate code for unpacking nested tuple arguments
+ # in function calls.
+
+ def unpackSequence(self, tup):
+ self._gen_safe_unpack_sequence(len(tup))
+ for elt in tup:
+ if isinstance(elt, tuple):
+ self.unpackSequence(elt)
+ else:
+ self._nameOp('STORE', elt)
+
+# A collection of code generators that adds the restricted mixin to
+# handle unpacking for all the different compilation modes. They
+# are defined here (at the end) so that can refer to RestrictedCodeGenerator.
+
+class RestrictedFunctionCodeGenerator(RestrictedCodeGenerator,
+ pycodegen.FunctionCodeGenerator):
+ pass
+
+class RestrictedExpressionCodeGenerator(RestrictedCodeGenerator,
+ pycodegen.ExpressionCodeGenerator):
+ pass
+
+class RestrictedInteractiveCodeGenerator(RestrictedCodeGenerator,
+ pycodegen.InteractiveCodeGenerator):
+ pass
+
+class RestrictedModuleCodeGenerator(RestrictedCodeGenerator,
+ pycodegen.ModuleCodeGenerator):
+
+ def initClass(self):
+ ModuleCodeGenerator.initClass(self)
+ self.__class__.FunctionGen = RestrictedFunctionCodeGenerator
+
+
+# These subclasses work around the definition of stub compile and mode
+# attributes in the common base class AbstractCompileMode. If it
+# didn't define new attributes, then the stub code inherited via
+# RestrictedCompileMode would override the real definitions in
+# Expression.
+
+class RExpression(RestrictedCompileMode, Expression):
+ mode = "eval"
+ CodeGeneratorClass = RestrictedExpressionCodeGenerator
+
+class RInteractive(RestrictedCompileMode, Interactive):
+ mode = "single"
+ CodeGeneratorClass = RestrictedInteractiveCodeGenerator
+
+class RModule(RestrictedCompileMode, Module):
+ mode = "exec"
+ CodeGeneratorClass = RestrictedModuleCodeGenerator
+
+class RFunction(RModule):
+ """A restricted Python function built from parts."""
+
+ CodeGeneratorClass = RestrictedModuleCodeGenerator
+
+ def __init__(self, p, body, name, filename, globals):
+ self.params = p
+ self.body = body
+ self.name = name
+ self.globals = globals or []
+ RModule.__init__(self, None, filename)
+
+ def parse(self):
+ # Parse the parameters and body, then combine them.
+ firstline = 'def f(%s): pass' % self.params
+ tree = niceParse(firstline, '<function parameters>', 'exec')
+ f = tree.node.nodes[0]
+ body_code = niceParse(self.body, self.filename, 'exec')
+ # Stitch the body code into the function.
+ f.code.nodes = body_code.node.nodes
+ f.name = self.name
+ # Look for a docstring.
+ stmt1 = f.code.nodes[0]
+ if (isinstance(stmt1, ast.Discard) and
+ isinstance(stmt1.expr, ast.Const) and
+ isinstance(stmt1.expr.value, str)):
+ f.doc = stmt1.expr.value
+ # The caller may specify that certain variables are globals
+ # so that they can be referenced before a local assignment.
+ # The only known example is the variables context, container,
+ # script, traverse_subpath in PythonScripts.
+ if self.globals:
+ f.code.nodes.insert(0, ast.Global(self.globals))
+ return tree
diff --git a/lib/kross/python/scripts/RestrictedPython/RestrictionMutator.py b/lib/kross/python/scripts/RestrictedPython/RestrictionMutator.py
new file mode 100644
index 00000000..a8b3850e
--- /dev/null
+++ b/lib/kross/python/scripts/RestrictedPython/RestrictionMutator.py
@@ -0,0 +1,372 @@
+##############################################################################
+#
+# Copyright (c) 2001 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
+#
+##############################################################################
+"""Modify AST to include security checks.
+
+RestrictionMutator modifies a tree produced by
+compiler.transformer.Transformer, restricting and enhancing the
+code in various ways before sending it to pycodegen.
+
+$Revision: 1.13 $
+"""
+
+from SelectCompiler import ast, parse, OP_ASSIGN, OP_DELETE, OP_APPLY
+
+# These utility functions allow us to generate AST subtrees without
+# line number attributes. These trees can then be inserted into other
+# trees without affecting line numbers shown in tracebacks, etc.
+def rmLineno(node):
+ """Strip lineno attributes from a code tree."""
+ if node.__dict__.has_key('lineno'):
+ del node.lineno
+ for child in node.getChildren():
+ if isinstance(child, ast.Node):
+ rmLineno(child)
+
+def stmtNode(txt):
+ """Make a "clean" statement node."""
+ node = parse(txt).node.nodes[0]
+ rmLineno(node)
+ return node
+
+# The security checks are performed by a set of six functions that
+# must be provided by the restricted environment.
+
+_apply_name = ast.Name("_apply_")
+_getattr_name = ast.Name("_getattr_")
+_getitem_name = ast.Name("_getitem_")
+_getiter_name = ast.Name("_getiter_")
+_print_target_name = ast.Name("_print")
+_write_name = ast.Name("_write_")
+
+# Constants.
+_None_const = ast.Const(None)
+_write_const = ast.Const("write")
+
+_printed_expr = stmtNode("_print()").expr
+_print_target_node = stmtNode("_print = _print_()")
+
+class FuncInfo:
+ print_used = False
+ printed_used = False
+
+class RestrictionMutator:
+
+ def __init__(self):
+ self.warnings = []
+ self.errors = []
+ self.used_names = {}
+ self.funcinfo = FuncInfo()
+
+ def error(self, node, info):
+ """Records a security error discovered during compilation."""
+ lineno = getattr(node, 'lineno', None)
+ if lineno is not None and lineno > 0:
+ self.errors.append('Line %d: %s' % (lineno, info))
+ else:
+ self.errors.append(info)
+
+ def checkName(self, node, name):
+ """Verifies that a name being assigned is safe.
+
+ This is to prevent people from doing things like:
+
+ __metatype__ = mytype (opens up metaclasses, a big unknown
+ in terms of security)
+ __path__ = foo (could this confuse the import machinery?)
+ _getattr = somefunc (not very useful, but could open a hole)
+
+ Note that assigning a variable is not the only way to assign
+ a name. def _badname, class _badname, import foo as _badname,
+ and perhaps other statements assign names. Special case:
+ '_' is allowed.
+ """
+ if name.startswith("_") and name != "_":
+ # Note: "_" *is* allowed.
+ self.error(node, '"%s" is an invalid variable name because'
+ ' it starts with "_"' % name)
+ if name == "printed":
+ self.error(node, '"printed" is a reserved name.')
+
+ def checkAttrName(self, node):
+ """Verifies that an attribute name does not start with _.
+
+ As long as guards (security proxies) have underscored names,
+ this underscore protection is important regardless of the
+ security policy. Special case: '_' is allowed.
+ """
+ name = node.attrname
+ if name.startswith("_") and name != "_":
+ # Note: "_" *is* allowed.
+ self.error(node, '"%s" is an invalid attribute name '
+ 'because it starts with "_".' % name)
+
+ def prepBody(self, body):
+ """Insert code for print at the beginning of the code suite."""
+
+ if self.funcinfo.print_used or self.funcinfo.printed_used:
+ # Add code at top for creating _print_target
+ body.insert(0, _print_target_node)
+ if not self.funcinfo.printed_used:
+ self.warnings.append(
+ "Prints, but never reads 'printed' variable.")
+ elif not self.funcinfo.print_used:
+ self.warnings.append(
+ "Doesn't print, but reads 'printed' variable.")
+
+ def visitFunction(self, node, walker):
+ """Checks and mutates a function definition.
+
+ Checks the name of the function and the argument names using
+ checkName(). It also calls prepBody() to prepend code to the
+ beginning of the code suite.
+ """
+ self.checkName(node, node.name)
+ for argname in node.argnames:
+ if isinstance(argname, str):
+ self.checkName(node, argname)
+ else:
+ for name in argname:
+ self.checkName(node, name)
+ walker.visitSequence(node.defaults)
+
+ former_funcinfo = self.funcinfo
+ self.funcinfo = FuncInfo()
+ node = walker.defaultVisitNode(node, exclude=('defaults',))
+ self.prepBody(node.code.nodes)
+ self.funcinfo = former_funcinfo
+ return node
+
+ def visitLambda(self, node, walker):
+ """Checks and mutates an anonymous function definition.
+
+ Checks the argument names using checkName(). It also calls
+ prepBody() to prepend code to the beginning of the code suite.
+ """
+ for argname in node.argnames:
+ self.checkName(node, argname)
+ return walker.defaultVisitNode(node)
+
+ def visitPrint(self, node, walker):
+ """Checks and mutates a print statement.
+
+ Adds a target to all print statements. 'print foo' becomes
+ 'print >> _print, foo', where _print is the default print
+ target defined for this scope.
+
+ Alternatively, if the untrusted code provides its own target,
+ we have to check the 'write' method of the target.
+ 'print >> ob, foo' becomes
+ 'print >> (_getattr(ob, 'write') and ob), foo'.
+ Otherwise, it would be possible to call the write method of
+ templates and scripts; 'write' happens to be the name of the
+ method that changes them.
+ """
+ node = walker.defaultVisitNode(node)
+ self.funcinfo.print_used = True
+ if node.dest is None:
+ node.dest = _print_target_name
+ else:
+ # Pre-validate access to the "write" attribute.
+ # "print >> ob, x" becomes
+ # "print >> (_getattr(ob, 'write') and ob), x"
+ node.dest = ast.And([
+ ast.CallFunc(_getattr_name, [node.dest, _write_const]),
+ node.dest])
+ return node
+
+ visitPrintnl = visitPrint
+
+ def visitName(self, node, walker):
+ """Prevents access to protected names as defined by checkName().
+
+ Also converts use of the name 'printed' to an expression.
+ """
+ if node.name == 'printed':
+ # Replace name lookup with an expression.
+ self.funcinfo.printed_used = True
+ return _printed_expr
+ self.checkName(node, node.name)
+ self.used_names[node.name] = True
+ return node
+
+ def visitCallFunc(self, node, walker):
+ """Checks calls with *-args and **-args.
+
+ That's a way of spelling apply(), and needs to use our safe
+ _apply_ instead.
+ """
+ walked = walker.defaultVisitNode(node)
+ if node.star_args is None and node.dstar_args is None:
+ # This is not an extended function call
+ return walked
+ # Otherwise transform foo(a, b, c, d=e, f=g, *args, **kws) into a call
+ # of _apply_(foo, a, b, c, d=e, f=g, *args, **kws). The interesting
+ # thing here is that _apply_() is defined with just *args and **kws,
+ # so it gets Python to collapse all the myriad ways to call functions
+ # into one manageable form.
+ #
+ # From there, _apply_() digs out the first argument of *args (it's the
+ # function to call), wraps args and kws in guarded accessors, then
+ # calls the function, returning the value.
+ # Transform foo(...) to _apply(foo, ...)
+ walked.args.insert(0, walked.node)
+ walked.node = _apply_name
+ return walked
+
+ def visitAssName(self, node, walker):
+ """Checks a name assignment using checkName()."""
+ self.checkName(node, node.name)
+ return node
+
+ def visitFor(self, node, walker):
+ # convert
+ # for x in expr:
+ # to
+ # for x in _getiter(expr):
+ #
+ # Note that visitListCompFor is the same thing. Exactly the same
+ # transformation is needed to convert
+ # [... for x in expr ...]
+ # to
+ # [... for x in _getiter(expr) ...]
+ node = walker.defaultVisitNode(node)
+ node.list = ast.CallFunc(_getiter_name, [node.list])
+ return node
+
+ visitListCompFor = visitFor
+
+ def visitGetattr(self, node, walker):
+ """Converts attribute access to a function call.
+
+ 'foo.bar' becomes '_getattr(foo, "bar")'.
+
+ Also prevents augmented assignment of attributes, which would
+ be difficult to support correctly.
+ """
+ self.checkAttrName(node)
+ node = walker.defaultVisitNode(node)
+ if getattr(node, 'in_aug_assign', False):
+ # We're in an augmented assignment
+ # We might support this later...
+ self.error(node, 'Augmented assignment of '
+ 'attributes is not allowed.')
+ return ast.CallFunc(_getattr_name,
+ [node.expr, ast.Const(node.attrname)])
+
+ def visitSubscript(self, node, walker):
+ """Checks all kinds of subscripts.
+
+ 'foo[bar] += baz' is disallowed.
+ 'a = foo[bar, baz]' becomes 'a = _getitem(foo, (bar, baz))'.
+ 'a = foo[bar]' becomes 'a = _getitem(foo, bar)'.
+ 'a = foo[bar:baz]' becomes 'a = _getitem(foo, slice(bar, baz))'.
+ 'a = foo[:baz]' becomes 'a = _getitem(foo, slice(None, baz))'.
+ 'a = foo[bar:]' becomes 'a = _getitem(foo, slice(bar, None))'.
+ 'del foo[bar]' becomes 'del _write(foo)[bar]'.
+ 'foo[bar] = a' becomes '_write(foo)[bar] = a'.
+
+ The _write function returns a security proxy.
+ """
+ node = walker.defaultVisitNode(node)
+ if node.flags == OP_APPLY:
+ # Set 'subs' to the node that represents the subscript or slice.
+ if getattr(node, 'in_aug_assign', False):
+ # We're in an augmented assignment
+ # We might support this later...
+ self.error(node, 'Augmented assignment of '
+ 'object items and slices is not allowed.')
+ if hasattr(node, 'subs'):
+ # Subscript.
+ subs = node.subs
+ if len(subs) > 1:
+ # example: ob[1,2]
+ subs = ast.Tuple(subs)
+ else:
+ # example: ob[1]
+ subs = subs[0]
+ else:
+ # Slice.
+ # example: obj[0:2]
+ lower = node.lower
+ if lower is None:
+ lower = _None_const
+ upper = node.upper
+ if upper is None:
+ upper = _None_const
+ subs = ast.Sliceobj([lower, upper])
+ return ast.CallFunc(_getitem_name, [node.expr, subs])
+ elif node.flags in (OP_DELETE, OP_ASSIGN):
+ # set or remove subscript or slice
+ node.expr = ast.CallFunc(_write_name, [node.expr])
+ return node
+
+ visitSlice = visitSubscript
+
+ def visitAssAttr(self, node, walker):
+ """Checks and mutates attribute assignment.
+
+ 'a.b = c' becomes '_write(a).b = c'.
+ The _write function returns a security proxy.
+ """
+ self.checkAttrName(node)
+ node = walker.defaultVisitNode(node)
+ node.expr = ast.CallFunc(_write_name, [node.expr])
+ return node
+
+ def visitExec(self, node, walker):
+ self.error(node, 'Exec statements are not allowed.')
+
+ def visitYield(self, node, walker):
+ self.error(node, 'Yield statements are not allowed.')
+
+ def visitClass(self, node, walker):
+ """Checks the name of a class using checkName().
+
+ Should classes be allowed at all? They don't cause security
+ issues, but they aren't very useful either since untrusted
+ code can't assign instance attributes.
+ """
+ self.checkName(node, node.name)
+ return walker.defaultVisitNode(node)
+
+ def visitModule(self, node, walker):
+ """Adds prep code at module scope.
+
+ Zope doesn't make use of this. The body of Python scripts is
+ always at function scope.
+ """
+ node = walker.defaultVisitNode(node)
+ self.prepBody(node.node.nodes)
+ return node
+
+ def visitAugAssign(self, node, walker):
+ """Makes a note that augmented assignment is in use.
+
+ Note that although augmented assignment of attributes and
+ subscripts is disallowed, augmented assignment of names (such
+ as 'n += 1') is allowed.
+
+ This could be a problem if untrusted code got access to a
+ mutable database object that supports augmented assignment.
+ """
+ node.node.in_aug_assign = True
+ return walker.defaultVisitNode(node)
+
+ def visitImport(self, node, walker):
+ """Checks names imported using checkName()."""
+ for name, asname in node.names:
+ self.checkName(node, name)
+ if asname:
+ self.checkName(node, asname)
+ return node
+
diff --git a/lib/kross/python/scripts/RestrictedPython/SelectCompiler.py b/lib/kross/python/scripts/RestrictedPython/SelectCompiler.py
new file mode 100644
index 00000000..5f9da698
--- /dev/null
+++ b/lib/kross/python/scripts/RestrictedPython/SelectCompiler.py
@@ -0,0 +1,28 @@
+##############################################################################
+#
+# Copyright (c) 2001 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
+#
+##############################################################################
+"""Compiler selector.
+
+$Id: SelectCompiler.py 24763 2004-05-17 05:59:28Z philikon $
+"""
+
+# Use the compiler from the standard library.
+import compiler
+from compiler import ast
+from compiler.transformer import parse
+from compiler.consts import OP_ASSIGN, OP_DELETE, OP_APPLY
+
+from RCompile import \
+ compile_restricted, \
+ compile_restricted_function, \
+ compile_restricted_exec, \
+ compile_restricted_eval
diff --git a/lib/kross/python/scripts/RestrictedPython/Utilities.py b/lib/kross/python/scripts/RestrictedPython/Utilities.py
new file mode 100644
index 00000000..87eef69b
--- /dev/null
+++ b/lib/kross/python/scripts/RestrictedPython/Utilities.py
@@ -0,0 +1,77 @@
+##############################################################################
+#
+# Copyright (c) 2001 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
+#
+##############################################################################
+
+__version__='$Revision: 1.7 $'[11:-2]
+
+import string, math, random
+import DocumentTemplate.sequence
+
+utility_builtins = {}
+
+utility_builtins['string'] = string
+utility_builtins['math'] = math
+utility_builtins['random'] = random
+utility_builtins['whrandom'] = random
+utility_builtins['sequence'] = DocumentTemplate.sequence
+
+try:
+ import DateTime
+ utility_builtins['DateTime']= DateTime.DateTime
+except: pass
+
+def same_type(arg1, *args):
+ '''Compares the class or type of two or more objects.'''
+ t = getattr(arg1, '__class__', type(arg1))
+ for arg in args:
+ if getattr(arg, '__class__', type(arg)) is not t:
+ return 0
+ return 1
+utility_builtins['same_type'] = same_type
+
+def test(*args):
+ l=len(args)
+ for i in range(1, l, 2):
+ if args[i-1]: return args[i]
+
+ if l%2: return args[-1]
+utility_builtins['test'] = test
+
+def reorder(s, with=None, without=()):
+ # s, with, and without are sequences treated as sets.
+ # The result is subtract(intersect(s, with), without),
+ # unless with is None, in which case it is subtract(s, without).
+ if with is None: with=s
+ d={}
+ tt=type(())
+ for i in s:
+ if type(i) is tt and len(i)==2: k, v = i
+ else: k= v = i
+ d[k]=v
+ r=[]
+ a=r.append
+ h=d.has_key
+
+ for i in without:
+ if type(i) is tt and len(i)==2: k, v = i
+ else: k= v = i
+ if h(k): del d[k]
+
+ for i in with:
+ if type(i) is tt and len(i)==2: k, v = i
+ else: k= v = i
+ if h(k):
+ a((k,d[k]))
+ del d[k]
+
+ return r
+utility_builtins['reorder'] = reorder
diff --git a/lib/kross/python/scripts/RestrictedPython/__init__.py b/lib/kross/python/scripts/RestrictedPython/__init__.py
new file mode 100644
index 00000000..0c70947e
--- /dev/null
+++ b/lib/kross/python/scripts/RestrictedPython/__init__.py
@@ -0,0 +1,19 @@
+##############################################################################
+#
+# Copyright (c) 2001 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
+#
+##############################################################################
+'''
+RestrictedPython package.
+$Id: __init__.py 24763 2004-05-17 05:59:28Z philikon $
+'''
+
+from SelectCompiler import *
+from PrintCollector import PrintCollector