jail_code can execute a provided file also.
This commit is contained in:
@@ -68,15 +68,20 @@ class JailResult(object):
|
||||
self.stdout = self.stderr = self.status = None
|
||||
|
||||
|
||||
def jail_code(command, code, files=None, argv=None, stdin=None):
|
||||
def jail_code(command, code=None, files=None, argv=None, stdin=None):
|
||||
"""Run code in a jailed subprocess.
|
||||
|
||||
`command` is an abstract command ("python", "node", ...) that must have
|
||||
been configured using `configure`.
|
||||
|
||||
`code` is a string containing the Python code to run.
|
||||
`code` is a string containing the code to run. If no code is supplied,
|
||||
then the code to run must be in one of the `files` copied, and must be
|
||||
named in the `argv` list.
|
||||
|
||||
`files` is a list of file paths.
|
||||
`files` is a list of file paths, they are all copied to the jailed
|
||||
directory.
|
||||
|
||||
`argv` is the command-line arguments to supply.
|
||||
|
||||
Return an object with:
|
||||
|
||||
@@ -92,6 +97,8 @@ def jail_code(command, code, files=None, argv=None, stdin=None):
|
||||
|
||||
log.debug("Executing jailed code: %r", code)
|
||||
|
||||
argv = argv or []
|
||||
|
||||
# All the supporting files are copied into our directory.
|
||||
for filename in files or ():
|
||||
if os.path.isfile(filename):
|
||||
@@ -101,10 +108,13 @@ def jail_code(command, code, files=None, argv=None, stdin=None):
|
||||
shutil.copytree(filename, dest)
|
||||
|
||||
# Create the main file.
|
||||
with open(os.path.join(tmpdir, "jailed_code.py"), "w") as jailed:
|
||||
jailed.write(code)
|
||||
if code:
|
||||
with open(os.path.join(tmpdir, "jailed_code"), "w") as jailed:
|
||||
jailed.write(code)
|
||||
|
||||
cmd = COMMANDS[command] + ['jailed_code.py'] + (argv or [])
|
||||
argv = ["jailed_code"] + argv
|
||||
|
||||
cmd = COMMANDS[command] + argv
|
||||
|
||||
subproc = subprocess.Popen(
|
||||
cmd, preexec_fn=set_process_limits, cwd=tmpdir,
|
||||
|
||||
@@ -85,7 +85,7 @@ def safe_exec(code, globals_dict, files=None, python_path=None):
|
||||
log.debug("Exec: %s", code)
|
||||
log.debug("Stdin: %s", stdin)
|
||||
|
||||
res = jail_code.jail_code("python", jailed_code, stdin=stdin, files=files)
|
||||
res = jail_code.jail_code("python", code=jailed_code, stdin=stdin, files=files)
|
||||
if res.status != 0:
|
||||
raise Exception("Couldn't execute jailed code: %s" % res.stderr)
|
||||
globals_dict.update(json.loads(res.stdout))
|
||||
|
||||
4
common/lib/codejail/codejail/tests/doit.py
Normal file
4
common/lib/codejail/codejail/tests/doit.py
Normal file
@@ -0,0 +1,4 @@
|
||||
import sys
|
||||
|
||||
print "This is doit.py!"
|
||||
print "My args are %r" % (sys.argv,)
|
||||
@@ -11,9 +11,15 @@ dedent = textwrap.dedent
|
||||
|
||||
|
||||
def jailpy(*args, **kwargs):
|
||||
"""Run `jail_code` on Python."""
|
||||
return jail_code("python", *args, **kwargs)
|
||||
|
||||
|
||||
def file_here(fname):
|
||||
"""Return the full path to a file alongside this code."""
|
||||
return os.path.join(os.path.dirname(__file__), fname)
|
||||
|
||||
|
||||
class JailCodeHelpers(object):
|
||||
"""Assert helpers for jail_code tests."""
|
||||
def setUp(self):
|
||||
@@ -28,32 +34,32 @@ class JailCodeHelpers(object):
|
||||
|
||||
class TestFeatures(JailCodeHelpers, unittest.TestCase):
|
||||
def test_hello_world(self):
|
||||
res = jailpy("print 'Hello, world!'")
|
||||
res = jailpy(code="print 'Hello, world!'")
|
||||
self.assertResultOk(res)
|
||||
self.assertEqual(res.stdout, 'Hello, world!\n')
|
||||
|
||||
def test_argv(self):
|
||||
res = jailpy(
|
||||
"import sys; print ':'.join(sys.argv[1:])",
|
||||
code="import sys; print ':'.join(sys.argv[1:])",
|
||||
argv=["Hello", "world", "-x"]
|
||||
)
|
||||
self.assertResultOk(res)
|
||||
self.assertEqual(res.stdout, "Hello:world:-x\n")
|
||||
|
||||
def test_ends_with_exception(self):
|
||||
res = jailpy("""raise Exception('FAIL')""")
|
||||
res = jailpy(code="""raise Exception('FAIL')""")
|
||||
self.assertNotEqual(res.status, 0)
|
||||
self.assertEqual(res.stdout, "")
|
||||
self.assertEqual(res.stderr, dedent("""\
|
||||
Traceback (most recent call last):
|
||||
File "jailed_code.py", line 1, in <module>
|
||||
File "jailed_code", line 1, in <module>
|
||||
raise Exception('FAIL')
|
||||
Exception: FAIL
|
||||
"""))
|
||||
|
||||
def test_stdin_is_provided(self):
|
||||
res = jailpy(
|
||||
"import json,sys; print sum(json.load(sys.stdin))",
|
||||
code="import json,sys; print sum(json.load(sys.stdin))",
|
||||
stdin="[1, 2.5, 33]"
|
||||
)
|
||||
self.assertResultOk(res)
|
||||
@@ -61,27 +67,35 @@ class TestFeatures(JailCodeHelpers, unittest.TestCase):
|
||||
|
||||
def test_files_are_copied(self):
|
||||
res = jailpy(
|
||||
"print 'Look:', open('hello.txt').read()",
|
||||
files=[os.path.dirname(__file__) + "/hello.txt"]
|
||||
code="print 'Look:', open('hello.txt').read()",
|
||||
files=[file_here("hello.txt")]
|
||||
)
|
||||
self.assertResultOk(res)
|
||||
self.assertEqual(res.stdout, 'Look: Hello there.\n\n')
|
||||
|
||||
def test_executing_a_copied_file(self):
|
||||
res = jailpy(
|
||||
files=[file_here("doit.py")],
|
||||
argv=["doit.py", "1", "2", "3"]
|
||||
)
|
||||
self.assertResultOk(res)
|
||||
self.assertEqual(res.stdout, "This is doit.py!\nMy args are ['doit.py', '1', '2', '3']\n")
|
||||
|
||||
|
||||
class TestLimits(JailCodeHelpers, unittest.TestCase):
|
||||
def test_cant_use_too_much_memory(self):
|
||||
res = jailpy("print sum(range(100000000))")
|
||||
res = jailpy(code="print sum(range(100000000))")
|
||||
self.assertNotEqual(res.status, 0)
|
||||
self.assertEqual(res.stdout, "")
|
||||
|
||||
def test_cant_use_too_much_cpu(self):
|
||||
res = jailpy("print sum(xrange(100000000))")
|
||||
res = jailpy(code="print sum(xrange(100000000))")
|
||||
self.assertNotEqual(res.status, 0)
|
||||
self.assertEqual(res.stdout, "")
|
||||
|
||||
def test_cant_use_too_much_time(self):
|
||||
raise SkipTest # TODO: test this once we can kill sleeping processes.
|
||||
res = jailpy(dedent("""\
|
||||
res = jailpy(code=dedent("""\
|
||||
import time
|
||||
time.sleep(5)
|
||||
print 'Done!'
|
||||
@@ -90,7 +104,7 @@ class TestLimits(JailCodeHelpers, unittest.TestCase):
|
||||
self.assertEqual(res.stdout, "")
|
||||
|
||||
def test_cant_write_files(self):
|
||||
res = jailpy(dedent("""\
|
||||
res = jailpy(code=dedent("""\
|
||||
print "Trying"
|
||||
with open("mydata.txt", "w") as f:
|
||||
f.write("hello")
|
||||
@@ -102,7 +116,7 @@ class TestLimits(JailCodeHelpers, unittest.TestCase):
|
||||
self.assertIn("ermission denied", res.stderr)
|
||||
|
||||
def test_cant_use_network(self):
|
||||
res = jailpy(dedent("""\
|
||||
res = jailpy(code=dedent("""\
|
||||
import urllib
|
||||
print "Reading google"
|
||||
u = urllib.urlopen("http://google.com")
|
||||
@@ -121,7 +135,7 @@ class TestLimits(JailCodeHelpers, unittest.TestCase):
|
||||
class TestMalware(JailCodeHelpers, unittest.TestCase):
|
||||
def test_crash_cpython(self):
|
||||
# http://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html
|
||||
res = jailpy(dedent("""\
|
||||
res = jailpy(code=dedent("""\
|
||||
import new, sys
|
||||
crash_me = new.function(new.code(0,0,0,0,"KABOOM",(),(),(),"","",0,""), {})
|
||||
print "Here we go..."
|
||||
@@ -134,7 +148,7 @@ class TestMalware(JailCodeHelpers, unittest.TestCase):
|
||||
self.assertEqual(res.stderr, "")
|
||||
|
||||
def test_read_etc_passwd(self):
|
||||
res = jailpy(dedent("""\
|
||||
res = jailpy(code=dedent("""\
|
||||
bytes = len(open('/etc/passwd').read())
|
||||
print 'Gotcha', bytes
|
||||
"""))
|
||||
@@ -143,7 +157,7 @@ class TestMalware(JailCodeHelpers, unittest.TestCase):
|
||||
self.assertIn("ermission denied", res.stderr)
|
||||
|
||||
def test_find_other_sandboxes(self):
|
||||
res = jailpy(dedent("""
|
||||
res = jailpy(code=dedent("""
|
||||
import os;
|
||||
places = [
|
||||
"..", "/tmp", "/", "/home", "/etc",
|
||||
|
||||
Reference in New Issue
Block a user