Commit f1fac732 by Ned Batchelder

A new boolean on XModuleSystem that determines whether to allow execution of…

A new boolean on XModuleSystem that determines whether to allow execution of untrusted unsandboxed code.
parent f4d84e67
...@@ -502,6 +502,10 @@ class JavascriptResponse(LoncapaResponse): ...@@ -502,6 +502,10 @@ class JavascriptResponse(LoncapaResponse):
return tmp_env return tmp_env
def call_node(self, args): def call_node(self, args):
# Node.js code is un-sandboxed. If the XModuleSystem says we aren't
# allowed to run unsafe code, then stop now.
if not self.system.can_execute_unsafe_code:
raise LoncapaProblemError("Execution of unsafe Javascript code is not allowed.")
subprocess_args = ["node"] subprocess_args = ["node"]
subprocess_args.extend(args) subprocess_args.extend(args)
......
...@@ -40,6 +40,7 @@ def test_system(): ...@@ -40,6 +40,7 @@ def test_system():
node_path=os.environ.get("NODE_PATH", "/usr/local/lib/node_modules"), node_path=os.environ.get("NODE_PATH", "/usr/local/lib/node_modules"),
anonymous_student_id='student', anonymous_student_id='student',
cache=None, cache=None,
can_execute_unsafe_code=False,
) )
return the_system return the_system
......
...@@ -12,7 +12,7 @@ import textwrap ...@@ -12,7 +12,7 @@ import textwrap
import mock import mock
import textwrap import textwrap
from . import new_loncapa_problem from . import new_loncapa_problem, test_system
from capa.responsetypes import LoncapaProblemError, \ from capa.responsetypes import LoncapaProblemError, \
StudentInputError, ResponseError StudentInputError, ResponseError
...@@ -30,9 +30,9 @@ class ResponseTest(unittest.TestCase): ...@@ -30,9 +30,9 @@ class ResponseTest(unittest.TestCase):
if self.xml_factory_class: if self.xml_factory_class:
self.xml_factory = self.xml_factory_class() self.xml_factory = self.xml_factory_class()
def build_problem(self, **kwargs): def build_problem(self, system=None, **kwargs):
xml = self.xml_factory.build_xml(**kwargs) xml = self.xml_factory.build_xml(**kwargs)
return new_loncapa_problem(xml) return new_loncapa_problem(xml, system=system)
def assert_grade(self, problem, submission, expected_correctness, msg=None): def assert_grade(self, problem, submission, expected_correctness, msg=None):
input_dict = {'1_2_1': submission} input_dict = {'1_2_1': submission}
...@@ -746,16 +746,37 @@ class JavascriptResponseTest(ResponseTest): ...@@ -746,16 +746,37 @@ class JavascriptResponseTest(ResponseTest):
coffee_file_path = os.path.dirname(__file__) + "/test_files/js/*.coffee" coffee_file_path = os.path.dirname(__file__) + "/test_files/js/*.coffee"
os.system("coffee -c %s" % (coffee_file_path)) os.system("coffee -c %s" % (coffee_file_path))
problem = self.build_problem(generator_src="test_problem_generator.js", system = test_system()
system.can_execute_unsafe_code = True
problem = self.build_problem(
system=system,
generator_src="test_problem_generator.js",
grader_src="test_problem_grader.js", grader_src="test_problem_grader.js",
display_class="TestProblemDisplay", display_class="TestProblemDisplay",
display_src="test_problem_display.js", display_src="test_problem_display.js",
param_dict={'value': '4'}) param_dict={'value': '4'},
)
# Test that we get graded correctly # Test that we get graded correctly
self.assert_grade(problem, json.dumps({0: 4}), "correct") self.assert_grade(problem, json.dumps({0: 4}), "correct")
self.assert_grade(problem, json.dumps({0: 5}), "incorrect") self.assert_grade(problem, json.dumps({0: 5}), "incorrect")
def test_cant_execute_javascript(self):
# If the system says to disallow unsafe code execution, then making
# this problem will raise an exception.
system = test_system()
system.can_execute_unsafe_code = False
with self.assertRaises(LoncapaProblemError):
problem = self.build_problem(
system=system,
generator_src="test_problem_generator.js",
grader_src="test_problem_grader.js",
display_class="TestProblemDisplay",
display_src="test_problem_display.js",
param_dict={'value': '4'},
)
class NumericalResponseTest(ResponseTest): class NumericalResponseTest(ResponseTest):
from response_xml_factory import NumericalResponseXMLFactory from response_xml_factory import NumericalResponseXMLFactory
......
...@@ -702,6 +702,7 @@ class ModuleSystem(object): ...@@ -702,6 +702,7 @@ class ModuleSystem(object):
open_ended_grading_interface=None, open_ended_grading_interface=None,
s3_interface=None, s3_interface=None,
cache=None, cache=None,
can_execute_unsafe_code=False,
): ):
''' '''
Create a closure around the system environment. Create a closure around the system environment.
...@@ -749,6 +750,9 @@ class ModuleSystem(object): ...@@ -749,6 +750,9 @@ class ModuleSystem(object):
.get(key) returns an object from the cache or None. .get(key) returns an object from the cache or None.
.set(key, value, timeout_secs=None) stores a value in the cache with a timeout. .set(key, value, timeout_secs=None) stores a value in the cache with a timeout.
can_execute_unsafe_code - A boolean, whether or not to allow the execution
of unsafe, unsandboxed code.
''' '''
self.ajax_url = ajax_url self.ajax_url = ajax_url
self.xqueue = xqueue self.xqueue = xqueue
...@@ -774,6 +778,7 @@ class ModuleSystem(object): ...@@ -774,6 +778,7 @@ class ModuleSystem(object):
self.s3_interface = s3_interface self.s3_interface = s3_interface
self.cache = cache or DoNothingCache() self.cache = cache or DoNothingCache()
self.can_execute_unsafe_code = can_execute_unsafe_code
def get(self, attr): def get(self, attr):
''' provide uniform access to attributes (like etree).''' ''' provide uniform access to attributes (like etree).'''
......
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