0

Adapted from Antti Haapala's answer here, I got the code below, used with a str example, to check whether an evaluated object is a class or not:

from inspect import isclass
from ast import parse
isclass(eval(compile(parse("str", mode = "eval"), "<AST>", "eval")))
# => True

Could anyone tell me about any potential security flaws with this piece?

I tried using inspect.isclass(ast.literal_eval("str")), but got the following error:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.9/ast.py", line 105, in literal_eval
    return _convert(node_or_string)
  File "/usr/lib/python3.9/ast.py", line 104, in _convert
    return _convert_signed_num(node)
  File "/usr/lib/python3.9/ast.py", line 78, in _convert_signed_num
    return _convert_num(node)
  File "/usr/lib/python3.9/ast.py", line 69, in _convert_num
    _raise_malformed_node(node)
  File "/usr/lib/python3.9/ast.py", line 66, in _raise_malformed_node
    raise ValueError(f'malformed node or string: {node!r}')
ValueError: malformed node or string: <ast.Name object at 0x7fb5b8394e20>

I then tried using inspect.isclass(ast.literal_eval("'str'")), as per nojco's answer here, but while it worked, it evaluated to False.

ShadowRylander
  • 361
  • 2
  • 8
  • `ast.literal_eval()` only handles literals and operators, not any sort of name or attribute lookup. To the best of my knowledge, there is nothing that you could pass to it that would result in a class. – jasonharper Jan 20 '22 at 19:01
  • So parsing and then compiling was the best option, in my situation? – ShadowRylander Jan 20 '22 at 19:27
  • What is the actual problem that you are trying to solve? If you want to know whether some name refers to a builtin type, then you can do `import builtins; isclass(getattr(builtins, 'str'))`. So the user input would only go into the `getattr` function as a second argument, where it can't do any harm. – a_guest Jan 20 '22 at 19:47
  • @a_guest Basically, I'm trying to find out if a string is a type; your method won't let me determine if a string is a custom type, though should be useful for builtins. Is there any other way for me to find out if I can convert an object to the type determined by the string? Or should I create another question for it, and accept your answer? – ShadowRylander Jan 20 '22 at 20:06
  • @ShadowRylander Better create a separate question with a dedicated title. But anyway, these types need to live somewhere, if not the builtins. So you can just query that other location too. Something like `locations = [builtins, foomodule, barmodule]; any(isclass(getattr(m, name, None)) for m in locations]`. – a_guest Jan 20 '22 at 20:10
  • Actually, I won't know the strings from beforehand; if you could kindly join me [here](https://stackoverflow.com/questions/70794183/check-if-string-is-type)? – ShadowRylander Jan 20 '22 at 22:27

1 Answers1

2

Yes, using eval poses a security risk. Try for example the following (side effect: this will create a directory oops):

# [!] This will create a directory 'oops'.
isclass(eval(compile(parse("__import__('os').mkdir('oops')", mode="eval"), "<AST>", "eval")))

So eval'uating arbitrary input from untrusted sources can harm your system.

a_guest
  • 34,165
  • 12
  • 64
  • 118