Commit 37016b0e by Ned Batchelder

Implement safe_exec on top of jailpy (old unsafe safe_exec is still here);…

Implement safe_exec on top of jailpy (old unsafe safe_exec is still here); Remove some crazy stuff from the context; always pass globals and locals, locals are the things that can be changed.
parent fc318739
"""Safe execution of untrusted Python code.""" """Safe execution of untrusted Python code."""
import json import json
import textwrap
from .lazymod import LazyModule import lazymod
import jailpy
def straw(v): # If we aren't running safe, then we need to artificially pass the values
return json.loads(json.dumps(jsonable_dict(v))) # through a JSON straw to ensure we aren't passing something that won't
# be executable in the safe context.
def jsonable_dict(d): def straw(d):
jd = {} jd = {}
for k,v in d.iteritems(): for k,v in d.iteritems():
try: try:
...@@ -16,9 +18,9 @@ def jsonable_dict(d): ...@@ -16,9 +18,9 @@ def jsonable_dict(d):
continue continue
else: else:
jd[k] = v jd[k] = v
return jd return json.loads(json.dumps(jd))
def safe_exec(code, globals_dict, locals_dict=None, future_division=False, assumed_imports=None): def safe_exec(code, globals_dict, locals_dict, future_division=False, assumed_imports=None):
"""Execute code safely. """Execute code safely.
Returns None. The code can modify globals in `global_dict`. Returns None. The code can modify globals in `global_dict`.
...@@ -28,21 +30,57 @@ def safe_exec(code, globals_dict, locals_dict=None, future_division=False, assum ...@@ -28,21 +30,57 @@ def safe_exec(code, globals_dict, locals_dict=None, future_division=False, assum
code = "from __future__ import division\n" + code code = "from __future__ import division\n" + code
g_dict = straw(globals_dict) g_dict = straw(globals_dict)
l_dict = straw(locals_dict)
if locals_dict is None:
l_dict = g_dict
else:
l_dict = straw(locals_dict)
for modname in assumed_imports or (): for modname in assumed_imports or ():
if isinstance(modname, tuple): if isinstance(modname, tuple):
name, modname = modname name, modname = modname
else: else:
name = modname name = modname
g_dict[name] = LazyModule(modname) g_dict[name] = lazymod.LazyModule(modname)
exec code in g_dict, l_dict exec code in g_dict, l_dict
globals_dict.update(straw(g_dict)) globals_dict.update(straw(g_dict))
if locals_dict is not None: locals_dict.update(straw(l_dict))
locals_dict.update(straw(l_dict))
# We'll need the code from lazymod.py for use in jailpy, so read it now.
lazymod_py_file = lazymod.__file__
if lazymod_py_file.endswith("c"):
lazymod_py_file = lazymod_py_file[:-1]
lazymod_py = open(lazymod_py_file).read()
def xxxsafe_exec(code, globals_dict, locals_dict, future_division=False, assumed_imports=None):
the_code = []
the_code.append(textwrap.dedent("""\
import json
import sys
code, g_dict, l_dict = json.load(sys.stdin)
"""))
if assumed_imports:
the_code.append(lazymod_py)
for modname in assumed_imports:
if isinstance(modname, tuple):
name, modname = modname
else:
name = modname
the_code.append("g_dict['{}'] = LazyModule('{}')\n".format(name, modname))
the_code.append(textwrap.dedent("""\
exec code in g_dict, l_dict
print >>sys.stderr, l_dict.keys()
ok_types = (int, long, float, str, unicode, list, tuple, dict)
l_dict = {k:v for k,v in l_dict.iteritems() if isinstance(v, ok_types)}
json.dump(l_dict, sys.stdout)
"""))
print "".join(the_code)
stdin = json.dumps([code, globals_dict, locals_dict])
res = jailpy.jailpy("".join(the_code), stdin=stdin)
if res.status != 0:
raise Exception("Couldn't excecute jailed code: %s" % res.stderr)
locals_dict.update(json.loads(res.stdout))
...@@ -33,6 +33,13 @@ class TestFeatures(unittest.TestCase): ...@@ -33,6 +33,13 @@ class TestFeatures(unittest.TestCase):
Exception: FAIL Exception: FAIL
""")) """))
def test_stdin_is_provided(self):
res = jailpy(
"import json,sys; print sum(json.load(sys.stdin))",
stdin="[1, 2.5, 33]"
)
self.assertEqual(res.stdout.strip(), "36.5")
class TestLimits(unittest.TestCase): class TestLimits(unittest.TestCase):
def test_cant_use_too_much_memory(self): def test_cant_use_too_much_memory(self):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment