Refactor code_jail to accommodate non-Python code.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
"""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("""\
|
||||
|
||||
Reference in New Issue
Block a user