Commit bde976da by Ned Batchelder

Refactor code_jail to accommodate non-Python code.

parent 55e910aa
......@@ -30,7 +30,7 @@ ASSUMED_IMPORTS=[
("draganddrop", "verifiers.draganddrop"),
]
# We'll need the code from lazymod.py for use in jailpy, so read it now.
# We'll need the code from lazymod.py for use in safe_exec, so read it now.
lazymod_py_file = lazymod.__file__
if lazymod_py_file.endswith("c"):
lazymod_py_file = lazymod_py_file[:-1]
......
......@@ -3,7 +3,7 @@
from django.core.exceptions import MiddlewareNotUsed
from django.conf import settings
import codejail.jailpy
import codejail.jail_code
class ConfigureCodeJailMiddleware(object):
......@@ -13,5 +13,5 @@ class ConfigureCodeJailMiddleware(object):
python_bin = settings.CODE_JAIL.get('python_bin')
if python_bin:
user = settings.CODE_JAIL['user']
codejail.jailpy.configure(python_bin, user=user)
codejail.jail_code.configure("python", python_bin, user=user)
raise MiddlewareNotUsed
......@@ -19,39 +19,49 @@ log = logging.getLogger(__name__)
# TODO: limit too much stdout data?
# Configure the Python command
# Configure the commands
PYTHON_CMD = None
# COMMANDS is a map from an abstract command name to a list of command-line
# pieces, such as subprocess.Popen wants.
COMMANDS = {}
def configure(python_bin, user=None):
"""Configure the jailpy module."""
global PYTHON_CMD
PYTHON_CMD = []
def configure(command, bin_path, user=None):
"""Configure a command for jail_code to use.
`command` is the abstract command you're configuring, such as "python" or
"node". `bin_path` is the path to the binary. `user`, if provided, is
the user name to run the command under.
"""
cmd_argv = []
if user:
PYTHON_CMD.extend(['sudo', '-u', 'sandbox'])
PYTHON_CMD.extend([python_bin, '-E'])
cmd_argv.extend(['sudo', '-u', 'sandbox'])
cmd_argv.extend([bin_path, '-E'])
COMMANDS[command] = cmd_argv
def is_configured():
return bool(PYTHON_CMD)
def is_configured(command):
return command in COMMANDS
# By default, look where our current Python is, and maybe there's a
# python-sandbox alongside. Only do this if running in a virtualenv.
if hasattr(sys, 'real_prefix'):
if os.path.isdir(sys.prefix + "-sandbox"):
configure(sys.prefix + "-sandbox/bin/python", "sandbox")
configure("python", sys.prefix + "-sandbox/bin/python", "sandbox")
class JailResult(object):
"""A passive object for us to return from jailpy."""
"""A passive object for us to return from jail_code."""
def __init__(self):
self.stdout = self.stderr = self.status = None
def jailpy(code, files=None, argv=None, stdin=None):
"""
Run Python code in a jailed subprocess.
def jail_code(command, code, 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.
......@@ -64,8 +74,8 @@ def jailpy(code, files=None, argv=None, stdin=None):
.status: return status of the process: an int, 0 for successful
"""
if not PYTHON_CMD:
raise Exception("jailpy needs to be configured")
if not is_configured(command):
raise Exception("jail_code needs to be configured for %r" % command)
with temp_directory(delete_when_done=True) as tmpdir:
......@@ -83,7 +93,7 @@ def jailpy(code, files=None, argv=None, stdin=None):
with open(os.path.join(tmpdir, "jailed_code.py"), "w") as jailed:
jailed.write(code)
cmd = PYTHON_CMD + ['jailed_code.py'] + (argv or [])
cmd = COMMANDS[command] + ['jailed_code.py'] + (argv or [])
subproc = subprocess.Popen(
cmd, preexec_fn=set_process_limits, cwd=tmpdir,
......
......@@ -7,7 +7,7 @@ import shutil
import sys
import textwrap
from codejail import jailpy
from codejail import jail_code
from codejail.util import temp_directory, change_directory
log = logging.getLogger(__name__)
......@@ -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 = jailpy.jailpy(jailed_code, stdin=stdin, files=files)
res = jail_code.jail_code("python", 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))
......@@ -144,5 +144,5 @@ def not_safe_exec(code, globals_dict, files=None, python_path=None):
# Running Python code in the sandbox makes it difficult to debug.
# Change 0 to 1 to run the code directly.
if 0 or not jailpy.is_configured():
if 0 or not jail_code.is_configured("python"):
safe_exec = not_safe_exec
"""Test jailpy.py"""
"""Test jail_code.py"""
import os.path
import textwrap
import unittest
from nose.plugins.skip import SkipTest
from codejail.jailpy import jailpy, is_configured
from codejail.jail_code import jail_code, is_configured
dedent = textwrap.dedent
class JailPyHelpers(object):
"""Assert helpers for jailpy tests."""
def jailpy(*args, **kwargs):
return jail_code("python", *args, **kwargs)
class JailCodeHelpers(object):
"""Assert helpers for jail_code tests."""
def setUp(self):
super(JailPyHelpers, self).setUp()
if not is_configured():
super(JailCodeHelpers, self).setUp()
if not is_configured("python"):
raise SkipTest
def assertResultOk(self, res):
......@@ -22,7 +26,7 @@ class JailPyHelpers(object):
self.assertEqual(res.status, 0)
class TestFeatures(JailPyHelpers, unittest.TestCase):
class TestFeatures(JailCodeHelpers, unittest.TestCase):
def test_hello_world(self):
res = jailpy("print 'Hello, world!'")
self.assertResultOk(res)
......@@ -64,7 +68,7 @@ class TestFeatures(JailPyHelpers, unittest.TestCase):
self.assertEqual(res.stdout, 'Look: Hello there.\n\n')
class TestLimits(JailPyHelpers, unittest.TestCase):
class TestLimits(JailCodeHelpers, unittest.TestCase):
def test_cant_use_too_much_memory(self):
res = jailpy("print sum(range(100000000))")
self.assertNotEqual(res.status, 0)
......@@ -114,7 +118,7 @@ class TestLimits(JailPyHelpers, unittest.TestCase):
# TODO: fork
class TestMalware(JailPyHelpers, 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("""\
......
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