fix: dont allow reading attributes of unsafe objects (#18706)
This commit is contained in:
parent
09d35c74eb
commit
e02b90cd5b
2 changed files with 51 additions and 4 deletions
|
|
@ -1,3 +1,5 @@
|
|||
import types
|
||||
|
||||
import frappe
|
||||
from frappe.tests.utils import FrappeTestCase
|
||||
from frappe.utils.safe_exec import get_safe_globals, safe_exec
|
||||
|
|
@ -59,3 +61,17 @@ class TestSafeExec(FrappeTestCase):
|
|||
|
||||
# enqueue whitelisted method
|
||||
safe_exec("""frappe.enqueue("ping", now=True)""")
|
||||
|
||||
def test_ensure_getattrable_globals(self):
|
||||
def check_safe(objects):
|
||||
for obj in objects:
|
||||
if isinstance(obj, (types.ModuleType, types.CodeType, types.TracebackType, types.FrameType)):
|
||||
self.fail(f"{obj} wont work in safe exec.")
|
||||
elif isinstance(obj, dict):
|
||||
check_safe(obj.values())
|
||||
|
||||
check_safe(get_safe_globals().values())
|
||||
|
||||
def test_unsafe_objects(self):
|
||||
unsafe_global = {"frappe": frappe}
|
||||
self.assertRaises(SyntaxError, safe_exec, """frappe.msgprint("Hello")""", unsafe_global)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@ import copy
|
|||
import inspect
|
||||
import json
|
||||
import mimetypes
|
||||
import types
|
||||
from contextlib import contextmanager
|
||||
from functools import lru_cache
|
||||
|
||||
import RestrictedPython.Guards
|
||||
from RestrictedPython import compile_restricted, safe_globals
|
||||
|
|
@ -64,14 +67,20 @@ def safe_exec(script, _globals=None, _locals=None, restrict_commit_rollback=Fals
|
|||
exec_globals.frappe.db.pop("rollback", None)
|
||||
exec_globals.frappe.db.pop("add_index", None)
|
||||
|
||||
# execute script compiled by RestrictedPython
|
||||
frappe.flags.in_safe_exec = True
|
||||
exec(compile_restricted(script), exec_globals, _locals) # pylint: disable=exec-used
|
||||
frappe.flags.in_safe_exec = False
|
||||
with safe_exec_flags(), patched_qb():
|
||||
# execute script compiled by RestrictedPython
|
||||
exec(compile_restricted(script), exec_globals, _locals) # pylint: disable=exec-used
|
||||
|
||||
return exec_globals, _locals
|
||||
|
||||
|
||||
@contextmanager
|
||||
def safe_exec_flags():
|
||||
frappe.flags.in_safe_exec = True
|
||||
yield
|
||||
frappe.flags.in_safe_exec = False
|
||||
|
||||
|
||||
def get_safe_globals():
|
||||
datautils = frappe._dict()
|
||||
|
||||
|
|
@ -258,6 +267,24 @@ def call_with_form_dict(function, kwargs):
|
|||
frappe.local.form_dict = form_dict
|
||||
|
||||
|
||||
@contextmanager
|
||||
def patched_qb():
|
||||
try:
|
||||
_terms = frappe.qb.terms
|
||||
frappe.qb.terms = _flatten(frappe.qb.terms)
|
||||
yield
|
||||
finally:
|
||||
frappe.qb.terms = _terms
|
||||
|
||||
|
||||
@lru_cache
|
||||
def _flatten(module):
|
||||
new_mod = NamespaceDict()
|
||||
for name, obj in inspect.getmembers(module, lambda x: not inspect.ismodule(x)):
|
||||
new_mod[name] = obj
|
||||
return new_mod
|
||||
|
||||
|
||||
def get_python_builtins():
|
||||
return {
|
||||
"abs": abs,
|
||||
|
|
@ -350,6 +377,10 @@ def _getattr(object, name, default=None):
|
|||
|
||||
if isinstance(name, str) and (name in UNSAFE_ATTRIBUTES):
|
||||
raise SyntaxError(f"{name} is an unsafe attribute")
|
||||
|
||||
if isinstance(object, (types.ModuleType, types.CodeType, types.TracebackType, types.FrameType)):
|
||||
raise SyntaxError(f"Reading {object} attributes is not allowed")
|
||||
|
||||
return RestrictedPython.Guards.safer_getattr(object, name, default=default)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue