[Zope-Checkins] CVS: Zope3/lib/python/Zope/RestrictedPython/tests - __init__.py:1.1.2.1 restricted_module.py:1.1.2.1 security_in_syntax.py:1.1.2.1 testRestrictions.py:1.1.2.1
Fred L. Drake, Jr.
fdrake@acm.org
Wed, 27 Feb 2002 17:29:57 -0500
Update of /cvs-repository/Zope3/lib/python/Zope/RestrictedPython/tests
In directory cvs.zope.org:/tmp/cvs-serv17295/tests
Added Files:
Tag: Zope-3x-branch
__init__.py restricted_module.py security_in_syntax.py
testRestrictions.py
Log Message:
Python 2.2 version of RestrictedPython (mostly cleanup).
=== Added File Zope3/lib/python/Zope/RestrictedPython/tests/__init__.py ===
'''Python package.'''
=== Added File Zope3/lib/python/Zope/RestrictedPython/tests/restricted_module.py ===
from __future__ import nested_scopes
def print0():
print 'Hello, world!',
return printed
def print1():
print 'Hello,',
print 'world!',
return printed
def printStuff():
print 'a', 'b', 'c',
return printed
def printToNone():
x = None
print >>x, 'Hello, world!',
return printed
def printLines():
# This failed before Zope 2.4.0a2
r = range(3)
for n in r:
for m in r:
print m + n * len(r),
print
return printed
def primes():
# Somewhat obfuscated code on purpose
print filter(None,map(lambda y:y*reduce(lambda x,y:x*y!=0,
map(lambda x,y=y:y%x,range(2,int(pow(y,0.5)+1))),1),range(2,20))),
return printed
def allowed_read(ob):
print ob.allowed
print ob.s
print ob[0]
print ob[2]
print ob[3:-1]
print len(ob)
return printed
def allowed_default_args(ob):
def f(a=ob.allowed, s=ob.s):
return a, s
def allowed_simple():
q = {'x':'a'}
q['y'] = 'b'
q.update({'z': 'c'})
r = ['a']
r.append('b')
r[2:2] = ['c']
s = 'a'
s = s[:100] + 'b'
s += 'c'
_ = q
return q['x'] + q['y'] + q['z'] + r[0] + r[1] + r[2] + s
def allowed_write(ob):
ob.writeable = 1
#ob.writeable += 1
[1 for ob.writeable in 1,2]
ob['safe'] = 2
#ob['safe'] += 2
[1 for ob['safe'] in 1,2]
def denied_print(ob):
print >> ob, 'Hello, world!',
def denied_getattr(ob):
#ob.disallowed += 1
ob.disallowed = 1
return ob.disallowed
def denied_default_args(ob):
def f(d=ob.disallowed):
return d
def denied_setattr(ob):
ob.allowed = -1
def denied_setattr2(ob):
#ob.allowed += -1
ob.allowed = -1
def denied_setattr3(ob):
[1 for ob.allowed in 1,2]
def denied_getitem(ob):
ob[1]
def denied_getitem2(ob):
#ob[1] += 1
ob[1]
def denied_setitem(ob):
ob['x'] = 2
def denied_setitem2(ob):
#ob[0] += 2
ob['x'] = 2
def denied_setitem3(ob):
[1 for ob['x'] in 1,2]
def denied_setslice(ob):
ob[0:1] = 'a'
def denied_setslice2(ob):
#ob[0:1] += 'a'
ob[0:1] = 'a'
def denied_setslice3(ob):
[1 for ob[0:1] in 1,2]
##def strange_attribute():
## # If a guard has attributes with names that don't start with an
## # underscore, those attributes appear to be an attribute of
## # anything.
## return [].attribute_of_anything
def order_of_operations():
return 3 * 4 * -2 + 2 * 12
def rot13(ss):
mapping = {}
orda = ord('a')
ordA = ord('A')
for n in range(13):
c1 = chr(orda + n)
c2 = chr(orda + n + 13)
c3 = chr(ordA + n)
c4 = chr(ordA + n + 13)
mapping[c1] = c2
mapping[c2] = c1
mapping[c3] = c4
mapping[c4] = c3
del c1, c2, c3, c4, orda, ordA
res = ''
for c in ss:
res = res + mapping.get(c, c)
return res
def nested_scopes_1():
# Fails if 'a' is consumed by the first function.
a = 1
def f1():
return a
def f2():
return a
return f1() + f2()
=== Added File Zope3/lib/python/Zope/RestrictedPython/tests/security_in_syntax.py ===
# These are all supposed to raise a SyntaxError when using
# compile_restricted() but not when using compile().
# Each function in this module is compiled using compile_restricted().
def overrideGuardWithFunction():
def _getattr(o): return o
def overrideGuardWithLambda():
lambda o, _getattr=None: o
def overrideGuardWithClass():
class _getattr:
pass
def overrideGuardWithName():
_getattr = None
def overrideGuardWithArgument():
def f(_getattr=None):
pass
def reserved_names():
printed = ''
def bad_name():
__ = 12
def bad_attr():
some_ob._some_attr = 15
def no_exec():
exec 'q = 1'
=== Added File Zope3/lib/python/Zope/RestrictedPython/tests/testRestrictions.py ===
import sys, os
import unittest
from string import rfind
from types import FunctionType
from Zope.RestrictedPython import compile_restricted
from Zope.RestrictedPython.PrintCollector import PrintCollector
from Zope.RestrictedPython.Eval import RestrictionCapableEval
from Zope.RestrictedPython.tests import restricted_module, security_in_syntax
if __name__=='__main__':
here = os.path.dirname(sys.argv[0])
else:
here = os.path.dirname(__file__)
def _getindent(line):
"""Returns the indentation level of the given line."""
indent = 0
for c in line:
if c == ' ': indent = indent + 1
elif c == '\t': indent = indent + 8
else: break
return indent
def find_source(fn, func):
"""Given a func_code object, this function tries to find and return
the python source code of the function. Originally written by
Harm van der Heijden (H.v.d.Heijden@phys.tue.nl)"""
f = open(fn,"r")
for i in range(func.co_firstlineno):
line = f.readline()
ind = _getindent(line)
msg = ""
while line:
msg = msg + line
line = f.readline()
# the following should be <= ind, but then we get
# confused by multiline docstrings. Using == works most of
# the time... but not always!
if _getindent(line) == ind: break
f.close()
return fn, msg
def create_rmodule():
global rmodule
fn = os.path.join(here, 'restricted_module.py')
f = open(fn, 'r')
source = f.read()
f.close()
# Sanity check
compile(source, fn, 'exec')
# Now compile it for real
code = compile_restricted(source, fn, 'exec')
rmodule = {'__builtins__':{'__import__':__import__, 'None':None}}
builtins = getattr(__builtins__, '__dict__', __builtins__)
for name in ('map', 'reduce', 'int', 'pow', 'range', 'filter',
'len', 'chr', 'ord',
):
rmodule[name] = builtins[name]
exec code in rmodule
create_rmodule()
class AccessDenied (Exception): pass
DisallowedObject = []
class RestrictedObject:
disallowed = DisallowedObject
allowed = 1
_ = 2
__ = 3
_some_attr = 4
__some_other_attr__ = 5
s = 'Another day, another test...'
__writeable_attrs__ = ('writeable',)
def __getitem__(self, idx):
if idx == 'protected':
raise AccessDenied
elif idx == 0 or idx == 'safe':
return 1
elif idx == 1:
return DisallowedObject
else:
return self.s[idx]
def __getslice__(self, lo, hi):
return self.s[lo:hi]
def __len__(self):
return len(self.s)
def __setitem__(self, idx, v):
if idx == 'safe':
self.safe = v
else:
raise AccessDenied
def __setslice__(self, lo, hi, value):
raise AccessDenied
write = DisallowedObject
def guarded_getattr(ob, name):
v = getattr(ob, name)
if v is DisallowedObject:
raise AccessDenied
return v
SliceType = type(slice(0))
def guarded_getitem(ob, index):
if type(index) is SliceType and index.step is None:
start = index.start
stop = index.stop
if start is None:
start = 0
if stop is None:
v = ob[start:]
else:
v = ob[start:stop]
else:
v = ob[index]
if v is DisallowedObject:
raise AccessDenied
return v
class TestGuard:
'''A guard class'''
def __init__(self, _ob, write=None):
self.__dict__['_ob'] = _ob
# Write guard methods
def __setattr__(self, name, value):
_ob = self.__dict__['_ob']
writeable = getattr(_ob, '__writeable_attrs__', ())
if name not in writeable:
raise AccessDenied
if name[:5] == 'func_':
raise AccessDenied
setattr(_ob, name, value)
def __setitem__(self, index, value):
_ob = self.__dict__['_ob']
_ob[index] = value
def __setslice__(self, lo, hi, value):
_ob = self.__dict__['_ob']
_ob[lo:hi] = value
## attribute_of_anything = 98.6
class RestrictionTests(unittest.TestCase):
def execFunc(self, name, *args, **kw):
func = rmodule[name]
func.func_globals.update({'_getattr_': guarded_getattr,
'_getitem_': guarded_getitem,
'_write_': TestGuard,
'_print_': PrintCollector})
return func(*args, **kw)
def checkPrint(self):
for i in range(2):
res = self.execFunc('print%s' % i)
assert res == 'Hello, world!', res
def checkPrintToNone(self):
try:
res = self.execFunc('printToNone')
except AttributeError:
# Passed. "None" has no "write" attribute.
pass
else:
assert 0, res
def checkPrintStuff(self):
res = self.execFunc('printStuff')
assert res == 'a b c', res
def checkPrintLines(self):
res = self.execFunc('printLines')
assert res == '0 1 2\n3 4 5\n6 7 8\n', res
def checkPrimes(self):
res = self.execFunc('primes')
assert res == '[2, 3, 5, 7, 11, 13, 17, 19]', res
def checkAllowedSimple(self):
res = self.execFunc('allowed_simple')
assert res == 'abcabcabc', res
def checkAllowedRead(self):
self.execFunc('allowed_read', RestrictedObject())
def checkAllowedWrite(self):
self.execFunc('allowed_write', RestrictedObject())
def checkAllowedArgs(self):
self.execFunc('allowed_default_args', RestrictedObject())
def checkDenied(self):
for k in rmodule.keys():
if k[:6] == 'denied':
try:
self.execFunc(k, RestrictedObject())
except AccessDenied:
# Passed the test
pass
else:
raise AssertionError, '%s() did not trip security' % k
def checkSyntaxSecurity(self):
# Ensures that each of the functions in security_in_syntax.py
# throws a SyntaxError when using compile_restricted.
fn = os.path.join(here, 'security_in_syntax.py')
f = open(fn, 'r')
source = f.read()
f.close()
# Unrestricted compile.
code = compile(source, fn, 'exec')
m = {'__builtins__':None}
exec code in m
for k, v in m.items():
if hasattr(v, 'func_code'):
filename, source = find_source(fn, v.func_code)
# Now compile it with restrictions
try:
code = compile_restricted(source, filename, 'exec')
except SyntaxError:
# Passed the test.
pass
else:
raise AssertionError, '%s should not have compiled' % k
## def checkStrangeAttribute(self):
## res = self.execFunc('strange_attribute')
## assert res == 98.6, res
def checkOrderOfOperations(self):
res = self.execFunc('order_of_operations')
assert (res == 0), res
def checkRot13(self):
res = self.execFunc('rot13', 'Zope is k00l')
assert (res == 'Mbcr vf x00y'), res
def checkNestedScopes1(self):
res = self.execFunc('nested_scopes_1')
assert (res == 2), res
def checkUnrestrictedEval(self):
expr = RestrictionCapableEval("{'a':[m.pop()]}['a'] + [m[0]]")
v = [12, 34]
expect = v[:]
expect.reverse()
res = expr.eval({'m':v})
assert res == expect, res
v = [12, 34]
res = expr(m=v)
assert res == expect
def checkStackSize(self):
for k, rfunc in rmodule.items():
if not k.startswith('_') and hasattr(rfunc, 'func_code'):
rss = rfunc.func_code.co_stacksize
ss = getattr(restricted_module, k).func_code.co_stacksize
self.failUnless(
rss >= ss, 'The stack size estimate for %s() '
'should have been at least %d, but was only %d'
% (k, ss, rss))
def test_suite():
return unittest.makeSuite(RestrictionTests, 'check')
def main():
alltests=test_suite()
runner = unittest.TextTestRunner()
runner.run(alltests)
def debug():
test_suite().debug()
def pdebug():
import pdb
pdb.run('debug()')
if __name__=='__main__':
if len(sys.argv) > 1:
globals()[sys.argv[1]]()
else:
main()