From 3a736eefa20cc294a022e4571fda3715ed9e7c3c Mon Sep 17 00:00:00 2001 From: Eric Herrera Date: Sun, 1 Aug 2021 09:19:23 -0500 Subject: [PATCH 1/5] feat: Codejail as external service (optional) Add codejail service settings (endpoint and feature flag). Add conditional to allow running codejail using a REST API service when flag is enabled. --- common/lib/capa/capa/safe_exec/safe_exec.py | 78 +++++++++++++++------ lms/envs/common.py | 15 ++++ 2 files changed, 73 insertions(+), 20 deletions(-) diff --git a/common/lib/capa/capa/safe_exec/safe_exec.py b/common/lib/capa/capa/safe_exec/safe_exec.py index c71854f917..2012d8544f 100644 --- a/common/lib/capa/capa/safe_exec/safe_exec.py +++ b/common/lib/capa/capa/safe_exec/safe_exec.py @@ -2,10 +2,13 @@ import hashlib +import requests +import json from codejail.safe_exec import SafeExecException, json_safe from codejail.safe_exec import not_safe_exec as codejail_not_safe_exec from codejail.safe_exec import safe_exec as codejail_safe_exec +from django.conf import settings from edx_django_utils.monitoring import function_trace import six from six import text_type @@ -143,28 +146,63 @@ def safe_exec( # Create the complete code we'll run. code_prolog = CODE_PROLOG % random_seed - # Decide which code executor to use. - if unsafely: - exec_fn = codejail_not_safe_exec + if settings.FEATURES.get('ENABLE_CODEJAIL_REST_SERVICE', False): + try: + codejail_service_endpoint = "".join([ + settings.CODE_JAIL_REST_SERVICE_HOST, + "/api/v0/code-exec" + ]) + data = { + "code": code_prolog + LAZY_IMPORTS + code, + "globals_dict": globals_dict, + "python_path": python_path, + "limit_overrides_context": limit_overrides_context, + "slug": slug, + "unsafely": unsafely + } + datajson = json.dumps(data) + response = requests.request( + "POST", + codejail_service_endpoint, + files=extra_files, + data={'payload': datajson} + ) + response_json = response.json() + emsg = response_json["emsg"] + if emsg: + exception_msg = ". ".join([ + emsg, + "For more information check Codejail Service logs." + ]) + + exception = SafeExecException(emsg) + globals_dict.update(response_json["globals_dict"]) + except Exception as e: + raise e + emsg = None else: - exec_fn = codejail_safe_exec + # Decide which code executor to use. + if unsafely: + exec_fn = codejail_not_safe_exec + else: + exec_fn = codejail_safe_exec - # Run the code! Results are side effects in globals_dict. - try: - exec_fn( - code_prolog + LAZY_IMPORTS + code, - globals_dict, - python_path=python_path, - extra_files=extra_files, - limit_overrides_context=limit_overrides_context, - slug=slug, - ) - except SafeExecException as e: - # Saving SafeExecException e in exception to be used later. - exception = e - emsg = text_type(e) - else: - emsg = None + # Run the code! Results are side effects in globals_dict. + try: + exec_fn( + code_prolog + LAZY_IMPORTS + code, + globals_dict, + python_path=python_path, + extra_files=extra_files, + limit_overrides_context=limit_overrides_context, + slug=slug, + ) + except SafeExecException as e: + # Saving SafeExecException e in exception to be used later. + exception = e + emsg = text_type(e) + else: + emsg = None # Put the result back in the cache. This is complicated by the fact that # the globals dict might not be entirely serializable. diff --git a/lms/envs/common.py b/lms/envs/common.py index 0bb174d0ec..12ad10d314 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -948,6 +948,18 @@ FEATURES = { # .. toggle_target_removal_date: 2021-10-01 # .. toggle_tickets: 'https://openedx.atlassian.net/browse/MICROBA-1405' 'ENABLE_V2_CERT_DISPLAY_SETTINGS': False, + + # .. toggle_name: ENABLE_CODEJAIL_REST_SERVICE + # .. toggle_implementation: DjangoSetting + # .. toggle_default: False + # .. toggle_description: Set this to True if you want to run Codejail code using + # a separate VM or container and communicate with edx-platform using REST API. + # .. toggle_use_cases: tutor + # .. toggle_creation_date: 2021-08-19 + # .. toggle_target_removal_date: None + # .. toggle_warnings: + # .. toggle_tickets: + 'ENABLE_CODEJAIL_REST_SERVICE': False, } # Specifies extra XBlock fields that should available when requested via the Course Blocks API @@ -1629,6 +1641,9 @@ CODE_JAIL = { # ] COURSES_WITH_UNSAFE_CODE = [] +# Cojail REST service +CODE_JAIL_REST_SERVICE_HOST = 'http://127.0.0.1:8550' + ############################### DJANGO BUILT-INS ############################### # Change DEBUG in your environment settings files, not here DEBUG = False From d672d309e80ed2f0b2924138c42ca8351177caa7 Mon Sep 17 00:00:00 2001 From: Eric Herrera Date: Sun, 1 Aug 2021 09:22:31 -0500 Subject: [PATCH 2/5] fix: Improve and fix codejail service integration Address recommendations and fixes discussed in PR. Use similar structure to notes app. Fix errors detected in tests. --- common/lib/capa/capa/safe_exec/exceptions.py | 24 +++++ common/lib/capa/capa/safe_exec/helpers.py | 97 ++++++++++++++++++++ common/lib/capa/capa/safe_exec/safe_exec.py | 50 +++------- lms/envs/common.py | 11 ++- 4 files changed, 141 insertions(+), 41 deletions(-) create mode 100644 common/lib/capa/capa/safe_exec/exceptions.py create mode 100644 common/lib/capa/capa/safe_exec/helpers.py diff --git a/common/lib/capa/capa/safe_exec/exceptions.py b/common/lib/capa/capa/safe_exec/exceptions.py new file mode 100644 index 0000000000..c6cb78c8f8 --- /dev/null +++ b/common/lib/capa/capa/safe_exec/exceptions.py @@ -0,0 +1,24 @@ +""" +Exceptions related to safe exec. +""" + + +class CodejailServiceParseError(Exception): + """ + An exception that is raised whenever we have issues with data parsing. + """ + pass # lint-amnesty, pylint: disable=unnecessary-pass + + +class CodejailServiceStatusError(Exception): + """ + An exception that is raised whenever Codejail service response status is different to 200. + """ + pass # lint-amnesty, pylint: disable=unnecessary-pass + + +class CodejailServiceUnavailable(Exception): + """ + An exception that is raised whenever Codejail service is unavailable. + """ + pass # lint-amnesty, pylint: disable=unnecessary-pass diff --git a/common/lib/capa/capa/safe_exec/helpers.py b/common/lib/capa/capa/safe_exec/helpers.py new file mode 100644 index 0000000000..76c62e4259 --- /dev/null +++ b/common/lib/capa/capa/safe_exec/helpers.py @@ -0,0 +1,97 @@ +""" +Helper methods related to safe exec. +""" + +import requests +import json +import logging + +from codejail.safe_exec import SafeExecException +from django.conf import settings +from django.utils.translation import ugettext as _ +from edx_toggles.toggles import SettingDictToggle +from requests.exceptions import RequestException, HTTPError +from simplejson import JSONDecodeError + +from .exceptions import CodejailServiceParseError, CodejailServiceStatusError, CodejailServiceUnavailable + +log = logging.getLogger(__name__) + +# .. toggle_name: ENABLE_CODEJAIL_REST_SERVICE +# .. toggle_implementation: DjangoSetting +# .. toggle_default: False +# .. toggle_description: Set this to True if you want to run Codejail code using +# a separate VM or container and communicate with edx-platform using REST API. +# .. toggle_use_cases: open_edx +# .. toggle_creation_date: 2021-08-19 +# .. toggle_target_removal_date: None +# .. toggle_warnings: +# .. toggle_tickets: +ENABLE_CODEJAIL_REST_SERVICE = SettingDictToggle( + "FEATURES", "ENABLE_CODEJAIL_REST_SERVICE", default=False, module_name=__name__ +) + + +def is_codejail_rest_service_enabled(): + return ENABLE_CODEJAIL_REST_SERVICE.is_enabled() + + +def get_codejail_rest_service_endpoint(): + return "".join([ + settings.CODE_JAIL_REST_SERVICE_HOST, + "/api/v0/code-exec"]) + + +def send_safe_exec_request(data, extra_files): + """ + Sends a request to a codejail api service forwarding required code and files. + Arguments: + data: Dict containing code and othe parameters + required for jailed code execution. + extra_files: python_lib.zip file containing extra files + required by the codejail execution. + Returns: + Response received from codejsail api service + """ + globals_dict = data["globals_dict"] + + codejail_service_endpoint = get_codejail_rest_service_endpoint() + payload = json.dumps(data) + + try: + response = requests.post( + codejail_service_endpoint, + files=extra_files, + data={'payload': payload}, + timeout=(settings.CODE_JAIL_REST_SERVICE_CONNECT_TIMEOUT, settings.CODE_JAIL_REST_SERVICE_READ_TIMEOUT) + ) + + except RequestException: + log.error("Failed to connect to codejail api service: url=%s, params=%s", + codejail_service_endpoint, str(payload)) + raise CodejailServiceUnavailable(_("Codejail API Service is unavailable. Please try again in a few minutes.")) # lint-amnesty, pylint: disable=raise-missing-from + + try: + response.raise_for_status() + except HTTPError: + raise CodejailServiceStatusError(_("Codejail API Service invalid response.")) # lint-amnesty, pylint: disable=raise-missing-from + + try: + response_json = response.json() + except JSONDecodeError: + log.error("Invalid JSON response received from codejail api service: Response_Content=%s", response.content) + raise CodejailServiceParseError(_("Invalid JSON response received from codejail api service.")) # lint-amnesty, pylint: disable=raise-missing-from + + emsg = response_json.get("emsg") + exception = None + + if emsg: + exception_msg = ". ".join([ + emsg, + "For more information check Codejail Service logs."]) + + exception = SafeExecException(exception_msg) + + globals_dict.update(response_json.get("globals_dict")) + + return emsg, exception diff --git a/common/lib/capa/capa/safe_exec/safe_exec.py b/common/lib/capa/capa/safe_exec/safe_exec.py index 2012d8544f..4b5d36c74d 100644 --- a/common/lib/capa/capa/safe_exec/safe_exec.py +++ b/common/lib/capa/capa/safe_exec/safe_exec.py @@ -2,18 +2,16 @@ import hashlib -import requests -import json from codejail.safe_exec import SafeExecException, json_safe from codejail.safe_exec import not_safe_exec as codejail_not_safe_exec from codejail.safe_exec import safe_exec as codejail_safe_exec -from django.conf import settings from edx_django_utils.monitoring import function_trace import six from six import text_type from . import lazymod +from .helpers import is_codejail_rest_service_enabled, send_safe_exec_request # Establish the Python environment for Capa. # Capa assumes float-friendly division always. @@ -146,40 +144,18 @@ def safe_exec( # Create the complete code we'll run. code_prolog = CODE_PROLOG % random_seed - if settings.FEATURES.get('ENABLE_CODEJAIL_REST_SERVICE', False): - try: - codejail_service_endpoint = "".join([ - settings.CODE_JAIL_REST_SERVICE_HOST, - "/api/v0/code-exec" - ]) - data = { - "code": code_prolog + LAZY_IMPORTS + code, - "globals_dict": globals_dict, - "python_path": python_path, - "limit_overrides_context": limit_overrides_context, - "slug": slug, - "unsafely": unsafely - } - datajson = json.dumps(data) - response = requests.request( - "POST", - codejail_service_endpoint, - files=extra_files, - data={'payload': datajson} - ) - response_json = response.json() - emsg = response_json["emsg"] - if emsg: - exception_msg = ". ".join([ - emsg, - "For more information check Codejail Service logs." - ]) - - exception = SafeExecException(emsg) - globals_dict.update(response_json["globals_dict"]) - except Exception as e: - raise e - emsg = None + if is_codejail_rest_service_enabled(): + data = { + "code": code_prolog + LAZY_IMPORTS + code, + "globals_dict": globals_dict, + "python_path": python_path, + "limit_overrides_context": limit_overrides_context, + "slug": slug, + "unsafely": unsafely + } + + emsg, exception = send_safe_exec_request(data, extra_files) + else: # Decide which code executor to use. if unsafely: diff --git a/lms/envs/common.py b/lms/envs/common.py index 12ad10d314..15c81ea58c 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -948,17 +948,17 @@ FEATURES = { # .. toggle_target_removal_date: 2021-10-01 # .. toggle_tickets: 'https://openedx.atlassian.net/browse/MICROBA-1405' 'ENABLE_V2_CERT_DISPLAY_SETTINGS': False, - + # .. toggle_name: ENABLE_CODEJAIL_REST_SERVICE # .. toggle_implementation: DjangoSetting # .. toggle_default: False # .. toggle_description: Set this to True if you want to run Codejail code using # a separate VM or container and communicate with edx-platform using REST API. - # .. toggle_use_cases: tutor + # .. toggle_use_cases: open_edx # .. toggle_creation_date: 2021-08-19 # .. toggle_target_removal_date: None - # .. toggle_warnings: - # .. toggle_tickets: + # .. toggle_warnings: + # .. toggle_tickets: 'ENABLE_CODEJAIL_REST_SERVICE': False, } @@ -1643,6 +1643,9 @@ COURSES_WITH_UNSAFE_CODE = [] # Cojail REST service CODE_JAIL_REST_SERVICE_HOST = 'http://127.0.0.1:8550' +CODE_JAIL_REST_SERVICE_CONNECT_TIMEOUT = 0.5 # time in seconds +CODE_JAIL_REST_SERVICE_READ_TIMEOUT = 3.5 # time in seconds + ############################### DJANGO BUILT-INS ############################### # Change DEBUG in your environment settings files, not here From fc7104cde5fe260e0d2f5cadbd8c7ab66da66f7b Mon Sep 17 00:00:00 2001 From: Eric Herrera Date: Mon, 4 Oct 2021 21:15:10 -0500 Subject: [PATCH 3/5] fix: Address PR comments Address fixes recommended by @timmc-edx. --- common/lib/capa/capa/safe_exec/exceptions.py | 3 -- common/lib/capa/capa/safe_exec/helpers.py | 39 +++++++++----------- lms/envs/common.py | 12 ------ 3 files changed, 17 insertions(+), 37 deletions(-) diff --git a/common/lib/capa/capa/safe_exec/exceptions.py b/common/lib/capa/capa/safe_exec/exceptions.py index c6cb78c8f8..e3843d0617 100644 --- a/common/lib/capa/capa/safe_exec/exceptions.py +++ b/common/lib/capa/capa/safe_exec/exceptions.py @@ -7,18 +7,15 @@ class CodejailServiceParseError(Exception): """ An exception that is raised whenever we have issues with data parsing. """ - pass # lint-amnesty, pylint: disable=unnecessary-pass class CodejailServiceStatusError(Exception): """ An exception that is raised whenever Codejail service response status is different to 200. """ - pass # lint-amnesty, pylint: disable=unnecessary-pass class CodejailServiceUnavailable(Exception): """ An exception that is raised whenever Codejail service is unavailable. """ - pass # lint-amnesty, pylint: disable=unnecessary-pass diff --git a/common/lib/capa/capa/safe_exec/helpers.py b/common/lib/capa/capa/safe_exec/helpers.py index 76c62e4259..42d567f3ab 100644 --- a/common/lib/capa/capa/safe_exec/helpers.py +++ b/common/lib/capa/capa/safe_exec/helpers.py @@ -9,7 +9,7 @@ import logging from codejail.safe_exec import SafeExecException from django.conf import settings from django.utils.translation import ugettext as _ -from edx_toggles.toggles import SettingDictToggle +from edx_toggles.toggles import SettingToggle from requests.exceptions import RequestException, HTTPError from simplejson import JSONDecodeError @@ -18,17 +18,14 @@ from .exceptions import CodejailServiceParseError, CodejailServiceStatusError, C log = logging.getLogger(__name__) # .. toggle_name: ENABLE_CODEJAIL_REST_SERVICE -# .. toggle_implementation: DjangoSetting +# .. toggle_implementation: SettingToggle # .. toggle_default: False # .. toggle_description: Set this to True if you want to run Codejail code using # a separate VM or container and communicate with edx-platform using REST API. # .. toggle_use_cases: open_edx # .. toggle_creation_date: 2021-08-19 -# .. toggle_target_removal_date: None -# .. toggle_warnings: -# .. toggle_tickets: -ENABLE_CODEJAIL_REST_SERVICE = SettingDictToggle( - "FEATURES", "ENABLE_CODEJAIL_REST_SERVICE", default=False, module_name=__name__ +ENABLE_CODEJAIL_REST_SERVICE = SettingToggle( + "ENABLE_CODEJAIL_REST_SERVICE", default=False, module_name=__name__ ) @@ -37,21 +34,19 @@ def is_codejail_rest_service_enabled(): def get_codejail_rest_service_endpoint(): - return "".join([ - settings.CODE_JAIL_REST_SERVICE_HOST, - "/api/v0/code-exec"]) + return f"{settings.CODE_JAIL_REST_SERVICE_HOST}/api/v0/code-exec" def send_safe_exec_request(data, extra_files): """ Sends a request to a codejail api service forwarding required code and files. Arguments: - data: Dict containing code and othe parameters + data: Dict containing code and other parameters required for jailed code execution. extra_files: python_lib.zip file containing extra files required by the codejail execution. Returns: - Response received from codejsail api service + Response received from codejail api service """ globals_dict = data["globals_dict"] @@ -66,30 +61,30 @@ def send_safe_exec_request(data, extra_files): timeout=(settings.CODE_JAIL_REST_SERVICE_CONNECT_TIMEOUT, settings.CODE_JAIL_REST_SERVICE_READ_TIMEOUT) ) - except RequestException: + except RequestException as err: log.error("Failed to connect to codejail api service: url=%s, params=%s", codejail_service_endpoint, str(payload)) - raise CodejailServiceUnavailable(_("Codejail API Service is unavailable. Please try again in a few minutes.")) # lint-amnesty, pylint: disable=raise-missing-from + raise CodejailServiceUnavailable(_( + "Codejail API Service is unavailable. " + "Please try again in a few minutes." + )) from err try: response.raise_for_status() - except HTTPError: - raise CodejailServiceStatusError(_("Codejail API Service invalid response.")) # lint-amnesty, pylint: disable=raise-missing-from + except HTTPError as err: + raise CodejailServiceStatusError(_("Codejail API Service invalid response.")) from err try: response_json = response.json() - except JSONDecodeError: + except JSONDecodeError as err: log.error("Invalid JSON response received from codejail api service: Response_Content=%s", response.content) - raise CodejailServiceParseError(_("Invalid JSON response received from codejail api service.")) # lint-amnesty, pylint: disable=raise-missing-from + raise CodejailServiceParseError(_("Invalid JSON response received from codejail api service.")) from err emsg = response_json.get("emsg") exception = None if emsg: - exception_msg = ". ".join([ - emsg, - "For more information check Codejail Service logs."]) - + exception_msg = f"{emsg}. For more information check Codejail Service logs." exception = SafeExecException(exception_msg) globals_dict.update(response_json.get("globals_dict")) diff --git a/lms/envs/common.py b/lms/envs/common.py index 15c81ea58c..40c0343be0 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -948,18 +948,6 @@ FEATURES = { # .. toggle_target_removal_date: 2021-10-01 # .. toggle_tickets: 'https://openedx.atlassian.net/browse/MICROBA-1405' 'ENABLE_V2_CERT_DISPLAY_SETTINGS': False, - - # .. toggle_name: ENABLE_CODEJAIL_REST_SERVICE - # .. toggle_implementation: DjangoSetting - # .. toggle_default: False - # .. toggle_description: Set this to True if you want to run Codejail code using - # a separate VM or container and communicate with edx-platform using REST API. - # .. toggle_use_cases: open_edx - # .. toggle_creation_date: 2021-08-19 - # .. toggle_target_removal_date: None - # .. toggle_warnings: - # .. toggle_tickets: - 'ENABLE_CODEJAIL_REST_SERVICE': False, } # Specifies extra XBlock fields that should available when requested via the Course Blocks API From fe6882819a692cb90ee7a9a2c738d96a3de81a48 Mon Sep 17 00:00:00 2001 From: Eric Herrera Date: Thu, 7 Oct 2021 10:42:20 -0500 Subject: [PATCH 4/5] fix: Address more PR comments Address @felipemonotoya recommendations related to add versioning to safe_exec_remote functions. --- cms/envs/common.py | 22 +++++++++++++++++++ .../safe_exec/{helpers.py => remote_exec.py} | 21 +++++++++++++++--- common/lib/capa/capa/safe_exec/safe_exec.py | 7 +++--- lms/envs/common.py | 17 ++++++++++++++ 4 files changed, 61 insertions(+), 6 deletions(-) rename common/lib/capa/capa/safe_exec/{helpers.py => remote_exec.py} (78%) diff --git a/cms/envs/common.py b/cms/envs/common.py index 5036425d25..974e99370a 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -1000,6 +1000,28 @@ CODE_JAIL = { COURSES_WITH_UNSAFE_CODE = [] +# Cojail REST service +ENABLE_CODEJAIL_REST_SERVICE = False +# .. setting_name: CODE_JAIL_REST_SERVICE_REMOTE_EXEC +# .. setting_default: 'common.lib.capa.capa.safe_exec.remote_exec.send_safe_exec_request_v0' +# .. setting_description: Set the python package.module.function that is reponsible of +# calling the remote service in charge of jailed code execution +CODE_JAIL_REST_SERVICE_REMOTE_EXEC = 'common.lib.capa.capa.safe_exec.remote_exec.send_safe_exec_request_v0' +# .. setting_name: CODE_JAIL_REST_SERVICE_HOST +# .. setting_default: 'http://127.0.0.1:8550' +# .. setting_description: Set the codejail remote service host +CODE_JAIL_REST_SERVICE_HOST = 'http://127.0.0.1:8550' +# .. setting_name: CODE_JAIL_REST_SERVICE_CONNECT_TIMEOUT +# .. setting_default: 0.5 +# .. setting_description: Set the number of seconds CMS will wait to establish an internal +# connection to the codejail remote service. +CODE_JAIL_REST_SERVICE_CONNECT_TIMEOUT = 0.5 # time in seconds +# .. setting_name: CODE_JAIL_REST_SERVICE_READ_TIMEOUT +# .. setting_default: 3.5 +# .. setting_description: Set the number of seconds CMS will wait for a response from the +# codejail remote service endpoint. +CODE_JAIL_REST_SERVICE_READ_TIMEOUT = 3.5 # time in seconds + ############################ DJANGO_BUILTINS ################################ # Change DEBUG in your environment settings files, not here DEBUG = False diff --git a/common/lib/capa/capa/safe_exec/helpers.py b/common/lib/capa/capa/safe_exec/remote_exec.py similarity index 78% rename from common/lib/capa/capa/safe_exec/helpers.py rename to common/lib/capa/capa/safe_exec/remote_exec.py index 42d567f3ab..7f13e0fbd3 100644 --- a/common/lib/capa/capa/safe_exec/helpers.py +++ b/common/lib/capa/capa/safe_exec/remote_exec.py @@ -10,6 +10,7 @@ from codejail.safe_exec import SafeExecException from django.conf import settings from django.utils.translation import ugettext as _ from edx_toggles.toggles import SettingToggle +from importlib import import_module from requests.exceptions import RequestException, HTTPError from simplejson import JSONDecodeError @@ -33,22 +34,36 @@ def is_codejail_rest_service_enabled(): return ENABLE_CODEJAIL_REST_SERVICE.is_enabled() +def get_remote_exec(*args, **kwargs): + """Get remote exec function based on setting and executes it.""" + remote_exec_function_name = settings.CODE_JAIL_REST_SERVICE_REMOTE_EXEC + try: + mod_name, func_name = remote_exec_function_name.rsplit('.', 1) + remote_exec_module = import_module(mod_name) + remote_exec_function = getattr(remote_exec_module, func_name) + if not remote_exec_function: + remote_exec_function = send_safe_exec_request_v0 + except ModuleNotFoundError: + return send_safe_exec_request_v0(*args, **kwargs) + return remote_exec_function(*args, **kwargs) + + def get_codejail_rest_service_endpoint(): return f"{settings.CODE_JAIL_REST_SERVICE_HOST}/api/v0/code-exec" -def send_safe_exec_request(data, extra_files): +def send_safe_exec_request_v0(data): """ Sends a request to a codejail api service forwarding required code and files. Arguments: data: Dict containing code and other parameters required for jailed code execution. - extra_files: python_lib.zip file containing extra files - required by the codejail execution. + It also includes extra_files (python_lib.zip) required by the codejail execution. Returns: Response received from codejail api service """ globals_dict = data["globals_dict"] + extra_files = data.pop("extra_files") codejail_service_endpoint = get_codejail_rest_service_endpoint() payload = json.dumps(data) diff --git a/common/lib/capa/capa/safe_exec/safe_exec.py b/common/lib/capa/capa/safe_exec/safe_exec.py index 4b5d36c74d..e08e58d488 100644 --- a/common/lib/capa/capa/safe_exec/safe_exec.py +++ b/common/lib/capa/capa/safe_exec/safe_exec.py @@ -11,7 +11,7 @@ import six from six import text_type from . import lazymod -from .helpers import is_codejail_rest_service_enabled, send_safe_exec_request +from .remote_exec import is_codejail_rest_service_enabled, get_remote_exec # Establish the Python environment for Capa. # Capa assumes float-friendly division always. @@ -151,10 +151,11 @@ def safe_exec( "python_path": python_path, "limit_overrides_context": limit_overrides_context, "slug": slug, - "unsafely": unsafely + "unsafely": unsafely, + "extra_files": extra_files, } - emsg, exception = send_safe_exec_request(data, extra_files) + emsg, exception = get_remote_exec(data) else: # Decide which code executor to use. diff --git a/lms/envs/common.py b/lms/envs/common.py index 40c0343be0..22580a0d59 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -1630,8 +1630,25 @@ CODE_JAIL = { COURSES_WITH_UNSAFE_CODE = [] # Cojail REST service +ENABLE_CODEJAIL_REST_SERVICE = False +# .. setting_name: CODE_JAIL_REST_SERVICE_REMOTE_EXEC +# .. setting_default: 'common.lib.capa.capa.safe_exec.remote_exec.send_safe_exec_request_v0' +# .. setting_description: Set the python package.module.function that is reponsible of +# calling the remote service in charge of jailed code execution +CODE_JAIL_REST_SERVICE_REMOTE_EXEC = 'common.lib.capa.capa.safe_exec.remote_exec.send_safe_exec_request_v0' +# .. setting_name: CODE_JAIL_REST_SERVICE_HOST +# .. setting_default: 'http://127.0.0.1:8550' +# .. setting_description: Set the codejail remote service host CODE_JAIL_REST_SERVICE_HOST = 'http://127.0.0.1:8550' +# .. setting_name: CODE_JAIL_REST_SERVICE_CONNECT_TIMEOUT +# .. setting_default: 0.5 +# .. setting_description: Set the number of seconds LMS will wait to establish an internal +# connection to the codejail remote service. CODE_JAIL_REST_SERVICE_CONNECT_TIMEOUT = 0.5 # time in seconds +# .. setting_name: CODE_JAIL_REST_SERVICE_READ_TIMEOUT +# .. setting_default: 3.5 +# .. setting_description: Set the number of seconds LMS will wait for a response from the +# codejail remote service endpoint. CODE_JAIL_REST_SERVICE_READ_TIMEOUT = 3.5 # time in seconds From 2a371e800bfb19c350035193fe454046f133b5dd Mon Sep 17 00:00:00 2001 From: Eric Herrera Date: Sat, 9 Oct 2021 08:25:54 -0500 Subject: [PATCH 5/5] fix: Constrain nltk version for py35 sandbox nltk 3.6.3 drops support for python 3.5. --- requirements/edx-sandbox/py35-constraints.txt | 2 ++ requirements/edx-sandbox/py35.in | 2 +- requirements/edx-sandbox/py35.txt | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/requirements/edx-sandbox/py35-constraints.txt b/requirements/edx-sandbox/py35-constraints.txt index c8cb6ea2b7..23ff7b627b 100644 --- a/requirements/edx-sandbox/py35-constraints.txt +++ b/requirements/edx-sandbox/py35-constraints.txt @@ -22,6 +22,8 @@ sympy<1.7.0 # sympy 1.7.0 drops support for Python 3.5 markupsafe<2.0.0 # markupsafe 2.0.0 requires Python >= 3.6 +nltk<3.6.3 # nltk 3.6.3 drops support for Python 3.5 + cryptography<3.3 # cryptography 3.3 has dropped python3.5 support. PyJWT[crypto]<2.0.0 # PYJWT[crypto]==2.0.1 requires cryptography>=3.3.1 social-auth-core<4.0.0 # social-auth-core>=4.0.0 requires PYJWT>=2.0.0 diff --git a/requirements/edx-sandbox/py35.in b/requirements/edx-sandbox/py35.in index d1087cfd84..e4d1a2184b 100644 --- a/requirements/edx-sandbox/py35.in +++ b/requirements/edx-sandbox/py35.in @@ -21,7 +21,7 @@ cryptography # Implementations of assorted cryptography a lxml # XML parser matplotlib==2.2.4 # 2D plotting library networkx==2.2 # Utilities for creating, manipulating, and studying network graphs -nltk # Natural language processing; used by the chem package +nltk==3.6.2 # Natural language processing; used by the chem package numpy==1.16.5 # Numeric array processing utilities; used by scipy openedx-calc<2.0.0 pyparsing==2.2.0 # Python Parsing module diff --git a/requirements/edx-sandbox/py35.txt b/requirements/edx-sandbox/py35.txt index cc73253ce7..2168f0b7bf 100644 --- a/requirements/edx-sandbox/py35.txt +++ b/requirements/edx-sandbox/py35.txt @@ -51,7 +51,7 @@ networkx==2.2 # via # -c requirements/edx-sandbox/../constraints.txt # -r requirements/edx-sandbox/py35.in -nltk==3.6.5 +nltk==3.6.2 # via # -r requirements/edx-sandbox/py35.in # chem