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