Safe eval critique (homework done)

Babar K. Zafar babar.zafar at gmail.com
Sat May 27 02:41:16 CEST 2006


Hi guys!

I know this subject has been beaten to death and I am not going to
whine about lacking features for proper restricted execution in the
Python runtime. It's the OS job, I get it.

Anyways, I thought about using a restricted *subset* of the language
for simple configuration scripts and storing data in a user-friendly
way. I'm fully aware about the dangers of introducing "eval" into the
picture so I took different route and hacked together the following
module:

http://www.zafar.se/dump/safe.py

Could some of you perhaps give some feedback on the implementation?

By default the module imposes the following restrictions:

* importing modules is disabled
* unsafe builtins are disabled
* timeout limit ('while 1:pass' can't block forever)
* getattr, setattr, delattr are disabled
* lowlevel attributes like __subclasses__ are disabled
* enviroment passed to 'exec' can't contain modules or builtins

Is there some obvious security hole I'm missing?
How easily could one compromise the restricted enviroment?

Thanks,
Babar K. Zafar

PS. Here are some simple unittests to give you a feel for the module:

class TestSafeEval(unittest.TestCase):
    def test_builtin(self):
        # attempt to access a unsafe builtin
        self.assertRaises(SafeEvalException,
            safe_eval, "open('test.txt', 'w')")

    def test_getattr(self):
        # attempt to get arround direct attr access
        self.assertRaises(SafeEvalException, \
            safe_eval, "getattr(int, '__abs__')")

    def test_func_globals(self):
        # attempt to access global enviroment where fun was defined
        self.assertRaises(SafeEvalException, \
            safe_eval, "def x(): pass; print x.func_globals")

    def test_lowlevel(self):
        # lowlevel tricks to access 'object'
        self.assertRaises(SafeEvalException, \
            safe_eval, "().__class__.mro()[1].__subclasses__()")

    def test_timeout_ok(self):
        # attempt to exectute slow code which finishes within timelimit
        def test(): time.sleep(2)
        env = {'test':test}
        safe_eval("test()", env, timeout_secs = 5)

    def test_timeout_exceed(self):
        # attempt to exectute code which never teminates
        self.assertRaises(SafeEvalException, \
            safe_eval, "while 1: pass")

    def test_invalid_context(self):
        # can't pass an enviroment with modules or builtins
        env = {'f' : __builtins__.open, 'g' : time}
        self.assertRaises(SafeEvalException, \
            safe_eval, "print 1", env)

    def test_callback(self):
        # modify local variable via callback
        self.value = 0
        def test(): self.value = 1
        env = {'test':test}
        safe_eval("test()", env)
        self.assertEqual(self.value, 1)




More information about the Python-list mailing list