Skip to content

Commit

Permalink
moving to satellite
Browse files Browse the repository at this point in the history
  • Loading branch information
Christian Theune committed Jun 3, 2007
1 parent 069f854 commit 311bc76
Show file tree
Hide file tree
Showing 20 changed files with 2,372 additions and 0 deletions.
118 changes: 118 additions & 0 deletions src/RestrictedPython/Eval.py
Original file line number Diff line number Diff line change
@@ -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$
"""

__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)
136 changes: 136 additions & 0 deletions src/RestrictedPython/Guards.py
Original file line number Diff line number Diff line change
@@ -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
46 changes: 46 additions & 0 deletions src/RestrictedPython/Limits.py
Original file line number Diff line number Diff line change
@@ -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
74 changes: 74 additions & 0 deletions src/RestrictedPython/MutatingWalker.py
Original file line number Diff line number Diff line change
@@ -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)
Loading

0 comments on commit 311bc76

Please sign in to comment.