general: forbid python when it is disabled (#64285)
This commit is contained in:
parent
10a148f1da
commit
97565ccb5a
|
@ -174,6 +174,31 @@ def test_python_datasource_errors(pub, error_email, http_requests, emails, caplo
|
|||
'[DATASOURCE] Python data source (\'[{"mairie-a-rdv", "Mairie A"}, {"mairie-b-rdv", "Mairie B"}]\') gave a non usable result'
|
||||
)
|
||||
|
||||
if not pub.site_options.has_section('options'):
|
||||
pub.site_options.add_section('options')
|
||||
pub.site_options.set('options', 'disable-python-expressions', 'true')
|
||||
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
|
||||
pub.site_options.write(fd)
|
||||
|
||||
# running with disabled python expressions
|
||||
if pub.is_using_postgresql():
|
||||
pub.loggederror_class.wipe()
|
||||
datasource = {
|
||||
'type': 'formula',
|
||||
'value': repr(['foo', 'bar']),
|
||||
'record_on_errors': False,
|
||||
'notify_on_errors': False,
|
||||
}
|
||||
emails.empty()
|
||||
assert data_sources.get_items(datasource) == []
|
||||
assert emails.count() == 1 # notified even with notify_on_errors set to False
|
||||
assert 'Unauthorized Python Usage' in emails.get_latest('subject')
|
||||
if pub.is_using_postgresql():
|
||||
assert pub.loggederror_class.count() == 1
|
||||
logged_error = pub.loggederror_class.select(order_by='latest_occurence_timestamp')[-1]
|
||||
assert logged_error.workflow_id is None
|
||||
assert logged_error.summary == 'Unauthorized Python Usage'
|
||||
|
||||
|
||||
def test_python_datasource_with_evalutils(pub):
|
||||
plain_list = [
|
||||
|
|
|
@ -19,7 +19,7 @@ from django.template import Context, Template, TemplateSyntaxError
|
|||
from django.utils.encoding import force_text
|
||||
from quixote import get_publisher
|
||||
|
||||
from .qommon import _, force_str
|
||||
from .qommon import _, force_str, misc
|
||||
|
||||
|
||||
class ValidationError(ValueError):
|
||||
|
@ -67,8 +67,7 @@ class Condition:
|
|||
|
||||
def evaluate_python(self, local_variables):
|
||||
global_variables = get_publisher().get_global_eval_dict()
|
||||
# noqa pylint: disable=eval-used
|
||||
return eval(self.value, global_variables, local_variables)
|
||||
return misc.eval_python(self.value, global_variables, local_variables)
|
||||
|
||||
def evaluate_django(self, local_variables):
|
||||
template = Template('{%% if %s %%}OK{%% endif %%}' % self.value)
|
||||
|
|
|
@ -337,8 +337,7 @@ def _get_structured_items(data_source, mode=None, raise_on_error=False):
|
|||
global_eval_dict = get_publisher().get_global_eval_dict()
|
||||
global_eval_dict.update(data_source_functions)
|
||||
try:
|
||||
# noqa pylint: disable=eval-used
|
||||
value = eval(data_source.get('value'), global_eval_dict, variables)
|
||||
value = misc.eval_python(data_source.get('value'), global_eval_dict, variables)
|
||||
if not isinstance(value, collections.abc.Iterable):
|
||||
get_publisher().record_error(
|
||||
'Python data source (%r) gave a non-iterable result' % data_source.get('value'),
|
||||
|
|
|
@ -535,8 +535,7 @@ class Field:
|
|||
elif t == 'formula':
|
||||
formula = self.prefill.get('value')
|
||||
try:
|
||||
# noqa pylint: disable=eval-used
|
||||
ret = eval(
|
||||
ret = misc.eval_python(
|
||||
formula,
|
||||
get_publisher().get_global_eval_dict(),
|
||||
get_publisher().substitutions.get_context_variables(),
|
||||
|
|
|
@ -1128,3 +1128,20 @@ def get_type_name(value):
|
|||
}
|
||||
object_type_name = object_type_names.get(value.__class__, value.__class__.__name__)
|
||||
return object_type_name
|
||||
|
||||
|
||||
class UnauthorizedPythonUsage(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def eval_python(expression, *args, **kwargs):
|
||||
# Inherently unsafe, abort if support has been disabled.
|
||||
if get_publisher().has_site_option('disable-python-expressions'):
|
||||
get_publisher().record_error(
|
||||
_('Unauthorized Python Usage'),
|
||||
notify=True,
|
||||
record=True,
|
||||
)
|
||||
raise UnauthorizedPythonUsage()
|
||||
# noqa pylint: disable=eval-used
|
||||
return eval(expression, *args, **kwargs)
|
||||
|
|
|
@ -1519,8 +1519,9 @@ class WorkflowGlobalActionTimeoutTrigger(WorkflowGlobalActionTrigger):
|
|||
elif self.anchor == 'python':
|
||||
variables = get_publisher().substitutions.get_context_variables()
|
||||
try:
|
||||
# noqa pylint: disable=eval-used
|
||||
anchor_date = eval(self.anchor_expression, get_publisher().get_global_eval_dict(), variables)
|
||||
anchor_date = misc.eval_python(
|
||||
self.anchor_expression, get_publisher().get_global_eval_dict(), variables
|
||||
)
|
||||
except Exception as e:
|
||||
# get the variables in the locals() namespace so they are
|
||||
# displayed within the trace.
|
||||
|
@ -2421,8 +2422,7 @@ class WorkflowStatusItem(XmlSerialisable):
|
|||
vars['allow_complex'] = old_allow_complex_value
|
||||
|
||||
try:
|
||||
# noqa pylint: disable=eval-used
|
||||
return eval(expression['value'], get_publisher().get_global_eval_dict(), vars)
|
||||
return misc.eval_python(expression['value'], get_publisher().get_global_eval_dict(), vars)
|
||||
except Exception as e:
|
||||
if record_errors:
|
||||
log_exception(e)
|
||||
|
@ -2617,8 +2617,7 @@ class WorkflowStatusItem(XmlSerialisable):
|
|||
try:
|
||||
# execute any Python expression
|
||||
# and magically convert string like 'form_var_*_raw' to a PicklableUpload
|
||||
# noqa pylint: disable=eval-used
|
||||
picklableupload = eval(attachment, global_eval_dict, local_eval_dict)
|
||||
picklableupload = misc.eval_python(attachment, global_eval_dict, local_eval_dict)
|
||||
except Exception as e:
|
||||
get_publisher().record_error(exception=e, context='[workflow/attachments]', notify=True)
|
||||
continue
|
||||
|
|
Loading…
Reference in New Issue