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.
This commit is contained in:
@@ -440,9 +440,6 @@ class LoncapaProblem(object):
|
||||
random.seed(self.seed)
|
||||
|
||||
context = {}
|
||||
|
||||
# pass instance of LoncapaProblem in
|
||||
context['the_lcp'] = self
|
||||
context['script_code'] = ''
|
||||
|
||||
self._execute_scripts(tree.findall('.//script'), context)
|
||||
@@ -473,7 +470,9 @@ class LoncapaProblem(object):
|
||||
context['script_code'] += code
|
||||
try:
|
||||
# use "context" for global context; thus defs in code are global within code
|
||||
safe_exec.safe_exec(code, context)
|
||||
locals_dict = {}
|
||||
safe_exec.safe_exec(code, context, locals_dict)
|
||||
context.update(locals_dict)
|
||||
except Exception as err:
|
||||
log.exception("Error while execing script code: " + code)
|
||||
msg = "Error while executing script code: %s" % str(err).replace('<', '<')
|
||||
|
||||
@@ -934,8 +934,9 @@ class CustomResponse(LoncapaResponse):
|
||||
'expect': expect,
|
||||
'ans': ans,
|
||||
}
|
||||
safe_exec.safe_exec(code, globals_dict)
|
||||
return globals_dict['cfn_return']
|
||||
locals_dict = {}
|
||||
safe_exec.safe_exec(code, globals_dict, locals_dict)
|
||||
return locals_dict['cfn_return']
|
||||
return check_function
|
||||
|
||||
self.code = make_check_function(self.context['script_code'], cfn)
|
||||
@@ -995,9 +996,6 @@ class CustomResponse(LoncapaResponse):
|
||||
# put these in the context of the check function evaluator
|
||||
# note that this doesn't help the "cfn" version - only the exec version
|
||||
self.context.update({
|
||||
# our subtree
|
||||
'xml': self.xml,
|
||||
|
||||
# my ID
|
||||
'response_id': self.myid,
|
||||
|
||||
@@ -1037,7 +1035,9 @@ class CustomResponse(LoncapaResponse):
|
||||
# exec the check function
|
||||
if isinstance(self.code, basestring):
|
||||
try:
|
||||
safe_exec.safe_exec(self.code, self.context)
|
||||
locals_dict = {}
|
||||
safe_exec.safe_exec(self.code, self.context, locals_dict)
|
||||
self.context.update(locals_dict)
|
||||
correct = self.context['correct']
|
||||
messages = self.context['messages']
|
||||
overall_message = self.context['overall_message']
|
||||
@@ -1754,10 +1754,10 @@ class SchematicResponse(LoncapaResponse):
|
||||
json.loads(student_answers[k]) for k in sorted(self.answer_ids)
|
||||
]
|
||||
self.context.update({'submission': submission})
|
||||
safe_exec.safe_exec(self.code, {}, self.context)
|
||||
locals_dict = {}
|
||||
safe_exec.safe_exec(self.code, self.context, locals_dict)
|
||||
cmap = CorrectMap()
|
||||
cmap.set_dict(dict(zip(sorted(
|
||||
self.answer_ids), self.context['correct'])))
|
||||
cmap.set_dict(dict(zip(sorted(self.answer_ids), locals_dict['correct'])))
|
||||
return cmap
|
||||
|
||||
def get_answers(self):
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import codejail.safe_exec
|
||||
|
||||
def safe_exec(code, globals_dict, locals_dict=None):
|
||||
def safe_exec(code, globals_dict, locals_dict):
|
||||
codejail.safe_exec.safe_exec(
|
||||
code, globals_dict, locals_dict, future_division=True,
|
||||
assumed_imports=[
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
"""Safe execution of untrusted Python code."""
|
||||
|
||||
import json
|
||||
import textwrap
|
||||
|
||||
from .lazymod import LazyModule
|
||||
import lazymod
|
||||
import jailpy
|
||||
|
||||
def straw(v):
|
||||
return json.loads(json.dumps(jsonable_dict(v)))
|
||||
|
||||
def jsonable_dict(d):
|
||||
# If we aren't running safe, then we need to artificially pass the values
|
||||
# through a JSON straw to ensure we aren't passing something that won't
|
||||
# be executable in the safe context.
|
||||
def straw(d):
|
||||
jd = {}
|
||||
for k,v in d.iteritems():
|
||||
try:
|
||||
@@ -16,9 +18,9 @@ def jsonable_dict(d):
|
||||
continue
|
||||
else:
|
||||
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.
|
||||
|
||||
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
|
||||
code = "from __future__ import division\n" + code
|
||||
|
||||
g_dict = straw(globals_dict)
|
||||
|
||||
if locals_dict is None:
|
||||
l_dict = g_dict
|
||||
else:
|
||||
l_dict = straw(locals_dict)
|
||||
l_dict = straw(locals_dict)
|
||||
|
||||
for modname in assumed_imports or ():
|
||||
if isinstance(modname, tuple):
|
||||
name, modname = modname
|
||||
else:
|
||||
name = modname
|
||||
g_dict[name] = LazyModule(modname)
|
||||
g_dict[name] = lazymod.LazyModule(modname)
|
||||
|
||||
exec code in g_dict, l_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):
|
||||
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):
|
||||
def test_cant_use_too_much_memory(self):
|
||||
|
||||
Reference in New Issue
Block a user