Safe eval critique (homework done)

B

Babar K. Zafar

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)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,767
Messages
2,569,572
Members
45,045
Latest member
DRCM

Latest Threads

Top