Using python eval () vs ast.literal_eval ()?

I have a situation with some code where eval() appeared as a possible solution. Now I have never had to use eval() before, but I found a lot of information about the potential danger that this could cause. However, I am very afraid to use it.

My situation is that I have user input:

 datamap = raw_input('Provide some data here: ') 

Where datamap should be a dictionary. I searched and found that eval() could solve this. I thought I could check the input type before trying to use the data, and that would be a viable precaution.

 datamap = eval(raw_input('Provide some data here: ') if not isinstance(datamap, dict): return 

I have read the docs and it is still unclear whether this will be safe or not. Does eval evaluate data immediately after entering it or after calling the datamap variable?

Is ast .literal_eval() only safe option?

+140
python eval abstract-syntax-tree
Mar 04 '13 at 8:50
source share
6 answers

datamap = eval(raw_input('Provide some data here: ')) means that you really evaluate the code before considering it unsafe or not. It evaluates the code as soon as the function is called. See also the dangers of eval .

ast.literal_eval throws an exception if the input is not a valid Python data type, so the code will not be executed if it was not.

Use ast.literal_eval when you need eval . Normally you should not evaluate literals using Python.

+155
Mar 04 '13 at 8:52
source share

ast.literal_eval() only takes into account a small part of the Python syntax:

A string or node can contain only the following literary Python structures: strings, numbers, tuples, lists, dicts, booleans, and None.

Passing __import__('os').system('rm -rf /a-path-you-really-care-about') to ast.literal_eval() will cause an error, but eval() gladly erase your disk.

Since it looks like you are allowing the user to enter a simple dictionary, use ast.literal_eval() . He safely does what you want, and nothing more.

+88
Mar 04 '13 at 8:54
source share

Python is impatient in its assessment, so eval(raw_input(...)) will evaluate the user input as soon as it gets into eval , regardless of what you do with the data afterwards. Therefore, this is not safe , especially if you are eval entering a user.

Use ast.literal_eval .




As an example, typing this into a tooltip will be very, very bad for you:

 __import__('os').system('rm -rf /a-path-you-really-care-about') 
+41
Mar 04 '13 at 8:52
source share

eval: this is very powerful, but also very dangerous if you accept lines to evaluate from untrusted input. Suppose the string being evaluated is "os.system ('rm -rf /')"? This will really start deleting all files on your computer.

ast.literal_eval: Safely parsing an expression node or string containing a Python literal or container mapping. The provided string or node can only consist of the following Python literal structures: strings, bytes, numbers, tuples, lists, dictations, sets, booleans, None, bytes, and sets.

Syntax:

 eval(expression, globals=None, locals=None) import ast ast.literal_eval(node_or_string) 

Example:

 # python 2.x - doesn't accept operators in string format import ast ast.literal_eval('[1, 2, 3]') # output: [1, 2, 3] ast.literal_eval('1+1') # output: ValueError: malformed string # python 3.0 -3.6 import ast ast.literal_eval("1+1") # output : 2 ast.literal_eval("{'a': 2, 'b': 3, 3:'xyz'}") # output : {'a': 2, 'b': 3, 3:'xyz'} # type dictionary ast.literal_eval("",{}) # output : Syntax Error required only one parameter ast.literal_eval("__import__('os').system('rm -rf /')") # output : error eval("__import__('os').system('rm -rf /')") # output : start deleting all the files on your computer. # restricting using global and local variables eval("__import__('os').system('rm -rf /')",{'__builtins__':{}},{}) # output : Error due to blocked imports by passing '__builtins__':{} in global # But still eval is not safe. we can access and break the code as given below s = """ (lambda fc=( lambda n: [ c for c in ().__class__.__bases__[0].__subclasses__() if c.__name__ == n ][0] ): fc("function")( fc("code")( 0,0,0,0,"KABOOM",(),(),(),"","",0,"" ),{} )() )() """ eval(s, {'__builtins__':{}}) 

In the above code ().__class__.__bases__[0] just an object. Now we have created all the subclasses , here our main goal of enter code here is to find one class from it named n .

We need a code object and a function object from the created instances of subclasses. This is an alternative way from CPython to access the subclasses of the object and connect the system.

In python 3.7, ast.literal_eval () has now become more strict. Addition and subtraction of arbitrary numbers are no longer allowed. link

+38
Jan 20 '16 at 15:56
source share

If you only need a custom dictionary, perhaps the best solution is json.loads . The main limitation is that json dicts requires string keys. You can also provide only literal data, but this also applies to literal_eval .

+3
Aug 02 '17 at 16:42 on
source share

I am stuck with ast.literal_eval() . I tried this in the IntelliJ IDEA debugger and it returned None when the debugger output.

But later, when I assigned its output to a variable and printed it in code. It worked fine. Code sharing example:

 import ast sample_string = '[{"id":"XYZ_GTTC_TYR", "name":"Suction"}]' output_value = ast.literal_eval(sample_string) print(output_value) 

Its version of Python is 3.6.

+1
Oct 02 '18 at 7:34
source share



All Articles