Commit de656884 by Ned Batchelder

Do the right things with symlinks.

parent 6b0f973f
...@@ -120,7 +120,9 @@ def jail_code(command, code=None, files=None, argv=None, stdin=None): ...@@ -120,7 +120,9 @@ def jail_code(command, code=None, files=None, argv=None, stdin=None):
`files` is a list of file paths, they are all copied to the jailed `files` is a list of file paths, they are all copied to the jailed
directory. Note that no check is made here that the files don't contain directory. Note that no check is made here that the files don't contain
sensitive information. The caller must somehow determine whether to allow sensitive information. The caller must somehow determine whether to allow
the code access to the files. the code access to the files. Symlinks will be copied as symlinks. If the
linked-to file is not accessible to the sandbox, the symlink will be
unreadable as well.
`argv` is the command-line arguments to supply. `argv` is the command-line arguments to supply.
...@@ -142,11 +144,13 @@ def jail_code(command, code=None, files=None, argv=None, stdin=None): ...@@ -142,11 +144,13 @@ def jail_code(command, code=None, files=None, argv=None, stdin=None):
# 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): dest = os.path.join(tmpdir, os.path.basename(filename))
if os.path.islink(filename):
os.symlink(os.readlink(filename), dest)
elif os.path.isfile(filename):
shutil.copy(filename, tmpdir) shutil.copy(filename, tmpdir)
else: else:
dest = os.path.join(tmpdir, os.path.basename(filename)) shutil.copytree(filename, dest, symlinks=True)
shutil.copytree(filename, dest)
# Create the main file. # Create the main file.
if code: if code:
......
...@@ -2,8 +2,11 @@ ...@@ -2,8 +2,11 @@
import os import os
import os.path import os.path
import shutil
import textwrap import textwrap
import tempfile
import unittest import unittest
from nose.plugins.skip import SkipTest from nose.plugins.skip import SkipTest
from codejail.jail_code import jail_code, is_configured, set_limit, LIMITS from codejail.jail_code import jail_code, is_configured, set_limit, LIMITS
...@@ -29,6 +32,7 @@ class JailCodeHelpers(object): ...@@ -29,6 +32,7 @@ class JailCodeHelpers(object):
raise SkipTest raise SkipTest
def assertResultOk(self, res): def assertResultOk(self, res):
"""Assert that `res` exited well (0), and had no stderr output."""
self.assertEqual(res.stderr, "") self.assertEqual(res.stderr, "")
self.assertEqual(res.status, 0) self.assertEqual(res.status, 0)
...@@ -74,6 +78,21 @@ class TestFeatures(JailCodeHelpers, unittest.TestCase): ...@@ -74,6 +78,21 @@ class TestFeatures(JailCodeHelpers, unittest.TestCase):
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_directories_are_copied(self):
res = jailpy(
code=dedent("""\
import os
for path, dirs, files in os.walk("."):
print (path, sorted(dirs), sorted(files))
"""),
files=[file_here("hello.txt"), file_here("pylib")]
)
self.assertResultOk(res)
self.assertEqual(res.stdout, dedent("""\
('.', ['pylib'], ['hello.txt', 'jailed_code'])
('./pylib', [], ['module.py', 'module.pyc'])
"""))
def test_executing_a_copied_file(self): def test_executing_a_copied_file(self):
res = jailpy( res = jailpy(
files=[file_here("doit.py")], files=[file_here("doit.py")],
...@@ -150,6 +169,61 @@ class TestLimits(JailCodeHelpers, unittest.TestCase): ...@@ -150,6 +169,61 @@ class TestLimits(JailCodeHelpers, unittest.TestCase):
self.assertNotIn("HONEY", res.stdout) self.assertNotIn("HONEY", res.stdout)
class TestSymlinks(JailCodeHelpers, unittest.TestCase):
"""Testing symlink behavior."""
def setUp(self):
# Make a temp dir, and arrange to have it removed when done.
tmp_dir = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, tmp_dir)
# Make a directory that won't be copied into the sandbox.
self.not_copied = os.path.join(tmp_dir, "not_copied")
os.mkdir(self.not_copied)
self.linked_txt = os.path.join(self.not_copied, "linked.txt")
with open(self.linked_txt, "w") as linked:
linked.write("Hi!")
# Make a directory that will be copied into the sandbox, with a
# symlink to a file we aren't copying in.
self.copied = os.path.join(tmp_dir, "copied")
os.mkdir(self.copied)
self.here_txt = os.path.join(self.copied, "here.txt")
with open(self.here_txt, "w") as here:
here.write("012345")
self.link_txt = os.path.join(self.copied, "link.txt")
os.symlink(self.linked_txt, self.link_txt)
self.herelink_txt = os.path.join(self.copied, "herelink.txt")
os.symlink("here.txt", self.herelink_txt)
def test_symlinks_in_directories_wont_copy_data(self):
# Run some code in the sandbox, with a copied directory containing
# the symlink.
res = jailpy(
code=dedent("""\
print open('copied/here.txt').read() # can read
print open('copied/herelink.txt').read() # can read
print open('copied/link.txt').read() # can't read
"""),
files=[self.copied],
)
self.assertEqual(res.stdout, "012345\n012345\n")
self.assertIn("ermission denied", res.stderr)
def test_symlinks_wont_copy_data(self):
# Run some code in the sandbox, with a copied file which is a symlink.
res = jailpy(
code=dedent("""\
print open('here.txt').read() # can read
print open('herelink.txt').read() # can read
print open('link.txt').read() # can't read
"""),
files=[self.here_txt, self.herelink_txt, self.link_txt],
)
self.assertEqual(res.stdout, "012345\n012345\n")
self.assertIn("ermission denied", res.stderr)
class TestChangingLimits(JailCodeHelpers, unittest.TestCase): class TestChangingLimits(JailCodeHelpers, unittest.TestCase):
def setUp(self): def setUp(self):
super(TestChangingLimits, self).setUp() super(TestChangingLimits, self).setUp()
......
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