-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Christian Theune
committed
Jun 3, 2007
1 parent
069f854
commit 311bc76
Showing
20 changed files
with
2,372 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
Oops, something went wrong.