For logging "handlers", _install_handlers() of logging.config uses eval():
def _install_handlers(cp, formatters):
"""Install and return handlers"""
hlist = cp["handlers"]["keys"]
...
for hand in hlist:
...
klass = section["class"]
try:
klass = eval(klass, vars(logging))
except (AttributeError, NameError):
klass = _resolve(klass)
args = section.get("args", '()')
args = eval(args, vars(logging))
kwargs = section.get("kwargs", '{}')
kwargs = eval(kwargs, vars(logging))
h = klass(*args, **kwargs)
...
...
return handlers
eval() is considered harmful regarding security: it executes arbitrary Python code.
Would it be possible to rewrite this function without eval?
I'm not sure of the format of the handler "class". Is it something like "module.submod.attr"? If yes, maybe a regex to validate the class would help? Maybe a loop using getattr() would be safer?
Maybe ast.literal_eval() would be enough? At least for args and kwargs?
$ python3
Python 3.7.2 (default, Jan 16 2019, 19:49:22)
>>> import ast
# Legit positional and keyword arguments are accepted
>>> ast.literal_eval("(1, 2)")
(1, 2)
>>> ast.literal_eval("{'x': 1, 'y': 2}")
{'x': 1, 'y': 2}
# eval() executes arbitrary Python code
>>> eval('__import__("os").system("echo hello")')
hello
0
# literal_eval() doesn't execute system()
>>> ast.literal_eval('__import__("os").system("echo hello")')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib64/python3.7/ast.py", line 91, in literal_eval
return _convert(node_or_string)
File "/usr/lib64/python3.7/ast.py", line 90, in _convert
return _convert_signed_num(node)
File "/usr/lib64/python3.7/ast.py", line 63, in _convert_signed_num
return _convert_num(node)
File "/usr/lib64/python3.7/ast.py", line 55, in _convert_num
raise ValueError('malformed node or string: ' + repr(node))
ValueError: malformed node or string: <_ast.Call object at 0x7f60a400c780>
|