From 8ddde056a0805aef1686d5d8ecd97e12293db36a Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 21 Aug 2023 12:42:21 +0530 Subject: [PATCH] fix: dont allow NamedExpr in safe_eval --- frappe/tests/test_safe_exec.py | 3 +++ frappe/utils/safe_exec.py | 23 +++++++++++++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/frappe/tests/test_safe_exec.py b/frappe/tests/test_safe_exec.py index d28c961ce8..49f4974148 100644 --- a/frappe/tests/test_safe_exec.py +++ b/frappe/tests/test_safe_exec.py @@ -44,6 +44,9 @@ class TestSafeExec(FrappeTestCase): ) self.assertEqual(1, frappe.safe_eval("int(enabled)", eval_locals=user.as_dict())) + def test_safe_eval_wal(self): + self.assertRaises(SyntaxError, frappe.safe_eval, "(x := (40+2))") + def test_sql(self): _locals = dict(out=None) safe_exec( diff --git a/frappe/utils/safe_exec.py b/frappe/utils/safe_exec.py index 0eee5b749a..f38cb3d4b7 100644 --- a/frappe/utils/safe_exec.py +++ b/frappe/utils/safe_exec.py @@ -1,3 +1,4 @@ +import ast import copy import inspect import json @@ -95,12 +96,7 @@ def safe_eval(code, eval_globals=None, eval_locals=None): code = unicodedata.normalize("NFKC", code) - for attribute in UNSAFE_ATTRIBUTES: - if attribute in code: - frappe.throw(f'Illegal rule {frappe.bold(code)}. Cannot use "{attribute}"') - - if "__" in code: - frappe.throw(f'Illegal rule {frappe.bold(code)}. Cannot use "__"') + _validate_safe_eval_syntax(code) if not eval_globals: eval_globals = {} @@ -115,6 +111,21 @@ def safe_eval(code, eval_globals=None, eval_locals=None): ) +def _validate_safe_eval_syntax(code): + BLOCKED_NODES = (ast.NamedExpr,) + for attribute in UNSAFE_ATTRIBUTES: + if attribute in code: + frappe.throw(f'Illegal rule {frappe.bold(code)}. Cannot use "{attribute}"', exc=AttributeError) + + if "__" in code: + frappe.throw(f'Illegal rule {frappe.bold(code)}. Cannot use "__"', exc=AttributeError) + + tree = ast.parse(code, mode="eval") + for node in ast.walk(tree): + if isinstance(node, BLOCKED_NODES): + raise SyntaxError(f"Operation not allowed: line {node.lineno} column {node.col_offset}") + + @contextmanager def safe_exec_flags(): frappe.flags.in_safe_exec = True