Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
codejail
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
codejail
Commits
e43e576b
Commit
e43e576b
authored
Apr 12, 2013
by
Ned Batchelder
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
jail_code can execute a provided file also.
parent
b781a172
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
49 additions
and
21 deletions
+49
-21
codejail/jail_code.py
+15
-5
codejail/safe_exec.py
+1
-1
codejail/tests/doit.py
+4
-0
codejail/tests/test_jailpy.py
+29
-15
No files found.
codejail/jail_code.py
View file @
e43e576b
...
@@ -68,15 +68,20 @@ class JailResult(object):
...
@@ -68,15 +68,20 @@ class JailResult(object):
self
.
stdout
=
self
.
stderr
=
self
.
status
=
None
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.
"""Run code in a jailed subprocess.
`command` is an abstract command ("python", "node", ...) that must have
`command` is an abstract command ("python", "node", ...) that must have
been configured using `configure`.
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:
Return an object with:
...
@@ -92,6 +97,8 @@ def jail_code(command, code, files=None, argv=None, stdin=None):
...
@@ -92,6 +97,8 @@ def jail_code(command, code, files=None, argv=None, stdin=None):
log
.
debug
(
"Executing jailed code:
%
r"
,
code
)
log
.
debug
(
"Executing jailed code:
%
r"
,
code
)
argv
=
argv
or
[]
# All the supporting files are copied into our directory.
# All the supporting files are copied into our directory.
for
filename
in
files
or
():
for
filename
in
files
or
():
if
os
.
path
.
isfile
(
filename
):
if
os
.
path
.
isfile
(
filename
):
...
@@ -101,10 +108,13 @@ def jail_code(command, code, files=None, argv=None, stdin=None):
...
@@ -101,10 +108,13 @@ def jail_code(command, code, files=None, argv=None, stdin=None):
shutil
.
copytree
(
filename
,
dest
)
shutil
.
copytree
(
filename
,
dest
)
# Create the main file.
# Create the main file.
with
open
(
os
.
path
.
join
(
tmpdir
,
"jailed_code.py"
),
"w"
)
as
jailed
:
if
code
:
with
open
(
os
.
path
.
join
(
tmpdir
,
"jailed_code"
),
"w"
)
as
jailed
:
jailed
.
write
(
code
)
jailed
.
write
(
code
)
cmd
=
COMMANDS
[
command
]
+
[
'jailed_code.py'
]
+
(
argv
or
[])
argv
=
[
"jailed_code"
]
+
argv
cmd
=
COMMANDS
[
command
]
+
argv
subproc
=
subprocess
.
Popen
(
subproc
=
subprocess
.
Popen
(
cmd
,
preexec_fn
=
set_process_limits
,
cwd
=
tmpdir
,
cmd
,
preexec_fn
=
set_process_limits
,
cwd
=
tmpdir
,
...
...
codejail/safe_exec.py
View file @
e43e576b
...
@@ -85,7 +85,7 @@ def safe_exec(code, globals_dict, files=None, python_path=None):
...
@@ -85,7 +85,7 @@ def safe_exec(code, globals_dict, files=None, python_path=None):
log
.
debug
(
"Exec:
%
s"
,
code
)
log
.
debug
(
"Exec:
%
s"
,
code
)
log
.
debug
(
"Stdin:
%
s"
,
stdin
)
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
:
if
res
.
status
!=
0
:
raise
Exception
(
"Couldn't execute jailed code:
%
s"
%
res
.
stderr
)
raise
Exception
(
"Couldn't execute jailed code:
%
s"
%
res
.
stderr
)
globals_dict
.
update
(
json
.
loads
(
res
.
stdout
))
globals_dict
.
update
(
json
.
loads
(
res
.
stdout
))
...
...
codejail/tests/doit.py
0 → 100644
View file @
e43e576b
import
sys
print
"This is doit.py!"
print
"My args are
%
r"
%
(
sys
.
argv
,)
codejail/tests/test_jailpy.py
View file @
e43e576b
...
@@ -11,9 +11,15 @@ dedent = textwrap.dedent
...
@@ -11,9 +11,15 @@ dedent = textwrap.dedent
def
jailpy
(
*
args
,
**
kwargs
):
def
jailpy
(
*
args
,
**
kwargs
):
"""Run `jail_code` on Python."""
return
jail_code
(
"python"
,
*
args
,
**
kwargs
)
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
):
class
JailCodeHelpers
(
object
):
"""Assert helpers for jail_code tests."""
"""Assert helpers for jail_code tests."""
def
setUp
(
self
):
def
setUp
(
self
):
...
@@ -28,32 +34,32 @@ class JailCodeHelpers(object):
...
@@ -28,32 +34,32 @@ class JailCodeHelpers(object):
class
TestFeatures
(
JailCodeHelpers
,
unittest
.
TestCase
):
class
TestFeatures
(
JailCodeHelpers
,
unittest
.
TestCase
):
def
test_hello_world
(
self
):
def
test_hello_world
(
self
):
res
=
jailpy
(
"print 'Hello, world!'"
)
res
=
jailpy
(
code
=
"print 'Hello, world!'"
)
self
.
assertResultOk
(
res
)
self
.
assertResultOk
(
res
)
self
.
assertEqual
(
res
.
stdout
,
'Hello, world!
\n
'
)
self
.
assertEqual
(
res
.
stdout
,
'Hello, world!
\n
'
)
def
test_argv
(
self
):
def
test_argv
(
self
):
res
=
jailpy
(
res
=
jailpy
(
"import sys; print ':'.join(sys.argv[1:])"
,
code
=
"import sys; print ':'.join(sys.argv[1:])"
,
argv
=
[
"Hello"
,
"world"
,
"-x"
]
argv
=
[
"Hello"
,
"world"
,
"-x"
]
)
)
self
.
assertResultOk
(
res
)
self
.
assertResultOk
(
res
)
self
.
assertEqual
(
res
.
stdout
,
"Hello:world:-x
\n
"
)
self
.
assertEqual
(
res
.
stdout
,
"Hello:world:-x
\n
"
)
def
test_ends_with_exception
(
self
):
def
test_ends_with_exception
(
self
):
res
=
jailpy
(
"""raise Exception('FAIL')"""
)
res
=
jailpy
(
code
=
"""raise Exception('FAIL')"""
)
self
.
assertNotEqual
(
res
.
status
,
0
)
self
.
assertNotEqual
(
res
.
status
,
0
)
self
.
assertEqual
(
res
.
stdout
,
""
)
self
.
assertEqual
(
res
.
stdout
,
""
)
self
.
assertEqual
(
res
.
stderr
,
dedent
(
"""
\
self
.
assertEqual
(
res
.
stderr
,
dedent
(
"""
\
Traceback (most recent call last):
Traceback (most recent call last):
File "jailed_code
.py
", line 1, in <module>
File "jailed_code", line 1, in <module>
raise Exception('FAIL')
raise Exception('FAIL')
Exception: FAIL
Exception: FAIL
"""
))
"""
))
def
test_stdin_is_provided
(
self
):
def
test_stdin_is_provided
(
self
):
res
=
jailpy
(
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]"
stdin
=
"[1, 2.5, 33]"
)
)
self
.
assertResultOk
(
res
)
self
.
assertResultOk
(
res
)
...
@@ -61,27 +67,35 @@ class TestFeatures(JailCodeHelpers, unittest.TestCase):
...
@@ -61,27 +67,35 @@ class TestFeatures(JailCodeHelpers, unittest.TestCase):
def
test_files_are_copied
(
self
):
def
test_files_are_copied
(
self
):
res
=
jailpy
(
res
=
jailpy
(
"print 'Look:', open('hello.txt').read()"
,
code
=
"print 'Look:', open('hello.txt').read()"
,
files
=
[
os
.
path
.
dirname
(
__file__
)
+
"/hello.txt"
]
files
=
[
file_here
(
"hello.txt"
)
]
)
)
self
.
assertResultOk
(
res
)
self
.
assertResultOk
(
res
)
self
.
assertEqual
(
res
.
stdout
,
'Look: Hello there.
\n\n
'
)
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!
\n
My args are ['doit.py', '1', '2', '3']
\n
"
)
class
TestLimits
(
JailCodeHelpers
,
unittest
.
TestCase
):
class
TestLimits
(
JailCodeHelpers
,
unittest
.
TestCase
):
def
test_cant_use_too_much_memory
(
self
):
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
.
assertNotEqual
(
res
.
status
,
0
)
self
.
assertEqual
(
res
.
stdout
,
""
)
self
.
assertEqual
(
res
.
stdout
,
""
)
def
test_cant_use_too_much_cpu
(
self
):
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
.
assertNotEqual
(
res
.
status
,
0
)
self
.
assertEqual
(
res
.
stdout
,
""
)
self
.
assertEqual
(
res
.
stdout
,
""
)
def
test_cant_use_too_much_time
(
self
):
def
test_cant_use_too_much_time
(
self
):
raise
SkipTest
# TODO: test this once we can kill sleeping processes.
raise
SkipTest
# TODO: test this once we can kill sleeping processes.
res
=
jailpy
(
dedent
(
"""
\
res
=
jailpy
(
code
=
dedent
(
"""
\
import time
import time
time.sleep(5)
time.sleep(5)
print 'Done!'
print 'Done!'
...
@@ -90,7 +104,7 @@ class TestLimits(JailCodeHelpers, unittest.TestCase):
...
@@ -90,7 +104,7 @@ class TestLimits(JailCodeHelpers, unittest.TestCase):
self
.
assertEqual
(
res
.
stdout
,
""
)
self
.
assertEqual
(
res
.
stdout
,
""
)
def
test_cant_write_files
(
self
):
def
test_cant_write_files
(
self
):
res
=
jailpy
(
dedent
(
"""
\
res
=
jailpy
(
code
=
dedent
(
"""
\
print "Trying"
print "Trying"
with open("mydata.txt", "w") as f:
with open("mydata.txt", "w") as f:
f.write("hello")
f.write("hello")
...
@@ -102,7 +116,7 @@ class TestLimits(JailCodeHelpers, unittest.TestCase):
...
@@ -102,7 +116,7 @@ class TestLimits(JailCodeHelpers, unittest.TestCase):
self
.
assertIn
(
"ermission denied"
,
res
.
stderr
)
self
.
assertIn
(
"ermission denied"
,
res
.
stderr
)
def
test_cant_use_network
(
self
):
def
test_cant_use_network
(
self
):
res
=
jailpy
(
dedent
(
"""
\
res
=
jailpy
(
code
=
dedent
(
"""
\
import urllib
import urllib
print "Reading google"
print "Reading google"
u = urllib.urlopen("http://google.com")
u = urllib.urlopen("http://google.com")
...
@@ -121,7 +135,7 @@ class TestLimits(JailCodeHelpers, unittest.TestCase):
...
@@ -121,7 +135,7 @@ class TestLimits(JailCodeHelpers, unittest.TestCase):
class
TestMalware
(
JailCodeHelpers
,
unittest
.
TestCase
):
class
TestMalware
(
JailCodeHelpers
,
unittest
.
TestCase
):
def
test_crash_cpython
(
self
):
def
test_crash_cpython
(
self
):
# http://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html
# http://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html
res
=
jailpy
(
dedent
(
"""
\
res
=
jailpy
(
code
=
dedent
(
"""
\
import new, sys
import new, sys
crash_me = new.function(new.code(0,0,0,0,"KABOOM",(),(),(),"","",0,""), {})
crash_me = new.function(new.code(0,0,0,0,"KABOOM",(),(),(),"","",0,""), {})
print "Here we go..."
print "Here we go..."
...
@@ -134,7 +148,7 @@ class TestMalware(JailCodeHelpers, unittest.TestCase):
...
@@ -134,7 +148,7 @@ class TestMalware(JailCodeHelpers, unittest.TestCase):
self
.
assertEqual
(
res
.
stderr
,
""
)
self
.
assertEqual
(
res
.
stderr
,
""
)
def
test_read_etc_passwd
(
self
):
def
test_read_etc_passwd
(
self
):
res
=
jailpy
(
dedent
(
"""
\
res
=
jailpy
(
code
=
dedent
(
"""
\
bytes = len(open('/etc/passwd').read())
bytes = len(open('/etc/passwd').read())
print 'Gotcha', bytes
print 'Gotcha', bytes
"""
))
"""
))
...
@@ -143,7 +157,7 @@ class TestMalware(JailCodeHelpers, unittest.TestCase):
...
@@ -143,7 +157,7 @@ class TestMalware(JailCodeHelpers, unittest.TestCase):
self
.
assertIn
(
"ermission denied"
,
res
.
stderr
)
self
.
assertIn
(
"ermission denied"
,
res
.
stderr
)
def
test_find_other_sandboxes
(
self
):
def
test_find_other_sandboxes
(
self
):
res
=
jailpy
(
dedent
(
"""
res
=
jailpy
(
code
=
dedent
(
"""
import os;
import os;
places = [
places = [
"..", "/tmp", "/", "/home", "/etc",
"..", "/tmp", "/", "/home", "/etc",
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment