We all know that eval is dangerous, even if you hide dangerous functions, because you can use Python\'s introspection features to dig down into things and re-extract them. F
Controlling the locals
and globals
dictionaries is extremely important. Otherwise, someone could just pass in eval
or exec
, and call it recursively
safe_eval('''e("""[c for c in ().__class__.__base__.__subclasses__()
if c.__name__ == \'catch_warnings\'][0]()._module.__builtins__""")''',
globals={'e': eval})
The expression in the recursive eval
is just a string.
You also need to set the eval
and exec
names in the global namespace to something that isn't the real eval
or exec
. The global namespace is important. If you use a local namespace, anything that creates a separate namespace, such as comprehensions and lambdas, will work around it
safe_eval('''[eval("""[c for c in ().__class__.__base__.__subclasses__()
if c.__name__ == \'catch_warnings\'][0]()._module.__builtins__""") for i in [1]][0]''', locals={'eval': None})
safe_eval('''(lambda: eval("""[c for c in ().__class__.__base__.__subclasses__()
if c.__name__ == \'catch_warnings\'][0]()._module.__builtins__"""))()''',
locals={'eval': None})
Again, here, safe_eval
only sees a string and a function call, not attribute accesses.
You also need to clear out the safe_eval
function itself, if it has a flag to disable safe parsing. Otherwise you could simply do
safe_eval('safe_eval("", safe=False)')