general: forbid python when it is disabled (#64285)

This commit is contained in:
Frédéric Péters 2022-04-16 17:24:46 +02:00
parent 10a148f1da
commit 97565ccb5a
6 changed files with 51 additions and 13 deletions

View File

@ -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 = [

View File

@ -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)

View File

@ -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'),

View File

@ -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(),

View File

@ -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)

View File

@ -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