[Zope-Checkins] CVS: Zope2 - restricted_module.py:1.1.2.1 security_in_syntax.py:1.1.2.1 testRestrictions.py:1.1.2.1

shane@digicool.com shane@digicool.com
Tue, 24 Apr 2001 18:47:18 -0400 (EDT)


Update of /cvs-repository/Zope2/lib/python/RestrictedPython/tests
In directory korak:/tmp/cvs-serv10208/tests

Added Files:
      Tag: RestrictedPythonBranch
	restricted_module.py security_in_syntax.py testRestrictions.py 
Log Message:
Created tests for the basic functionality of RestrictedPython.



--- Added File restricted_module.py in package Zope2 ---

def print1():
    print 'Hello, world!',
    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_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['safe'] = 2

def denied_getattr(ob):
    return ob.disallowed

def denied_setattr(ob):
    ob.allowed = -1

def denied_setitem(ob):
    ob['x'] = 2

def denied_setslice(ob):
    ob[0:1] = 'a'

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



--- Added File security_in_syntax.py in package Zope2 ---

# 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 _guard(o): return o

def overrideGuardWithLambda():
    lambda o, _guard=None: o

def overrideGuardWithClass():
    class _guard:
        pass

def overrideGuardWithName():
    _guard = None

def overrideGuardWithArgument():
    def f(_guard=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 testRestrictions.py in package Zope2 ---

from string import rfind
import sys, os

if __name__=='__main__':
    sys.path.append(os.path.join(os.pardir, os.pardir))

import unittest
from RestrictedPython import compile_restricted, PrintCollector
import security_in_syntax

def package_home(globals_dict):
    __name__=globals_dict['__name__']
    if __name__ == '__main__':
        return os.getcwd()
    m=sys.modules[__name__]
    if hasattr(m,'__path__'):
        r=m.__path__[0]
    elif "." in __name__:
        r=sys.modules[__name__[:rfind(__name__,'.')]].__path__[0]
    else:
        r=__name__
    return os.path.join(os.getcwd(), r)

FunctionType = type(package_home)

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(package_home(globals()), '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__':None}
    for name in ('map', 'reduce', 'int', 'pow', 'range', 'filter',
                 'len', 'chr', 'ord',
                 ):
        rmodule[name] = getattr(__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:
            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


class TestGuard:
    '''A guard class'''
    def __init__(self, _ob):
        self.__dict__['_ob'] = _ob

    # Read guard methods
    def __len__(self):
        return len(self.__dict__['_ob'])

    def __getattr__(self, name):
        _ob = self.__dict__['_ob']
        v = getattr(_ob, name)
        if v is DisallowedObject:
            raise AccessDenied
        return v

    def __getitem__(self, index):
        # Can receive an Ellipsis or "slice" instance.
        _ob = self.__dict__['_ob']
        v = _ob[index]
        if v is DisallowedObject:
            raise AccessDenied
        return v

    def __getslice__(self, lo, hi):
        _ob = self.__dict__['_ob']
        return _ob[lo:hi]

    # 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({'_guard_init': TestGuard,
                                  '_print_target_class': PrintCollector})
        return func(*args, **kw)

    def checkPrint(self):
        res = self.execFunc('print1')
        assert res == 'Hello, world!', 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 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(package_home(globals()), '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 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()