safe eval of moderately simple math expressions
Aaron Brady
castironpi at gmail.com
Sat Apr 11 06:22:19 EDT 2009
On Apr 11, 3:18 am, Joel Hedlund <yoh... at ifm.liu.se> wrote:
> Aaron Brady wrote:
> > Would you be willing to examine a syntax tree to determine if there
> > are any class accesses?
>
> Sure? How do I do that? I've never done that type of thing before so I
> can't really say if it would work or not.
>
> /Joel
NO PROMISES. No warranty is made, express or implied.
Of course, something this devious, a "white" list, may just make it so
your enemy finds out its weakness before you do.
It's ostensibly for Python 3, but IIRC there's a way to do it in 2.
'ast.literal_eval' appears to evaluate a literal, but won't do
expressions, which is what you are looking for. We should refer
people to it more often.
+1 ast.walk, btw.
If you want subtraction and division, you'll have to add them
yourself. You could probably compress the 'is_it_safe' function to
one line, provided that it's sound to start with: if all( x in
safe_node_classes for x in ast.walk( ast.parse( exp ) ) ), or better
yet, if set( ast.walk( ast.parse( exp ) ) )<= safe_node_classes. +1!
/Source:
import ast
safe_exp= '( 2+ 4 )* 7'
unsafe_exp= '( 2+ 4 ).__class__'
unsafe_exp2= '__import__( "os" )'
safe_node_classes= set( [
ast.Module,
ast.Expr,
ast.BinOp,
ast.Mult,
ast.Add,
ast.Num
] )
def is_it_safe( exp ):
print( 'trying %s'% exp )
top= ast.parse( exp )
for node in ast.walk( top ):
print( node )
if node.__class__ not in safe_node_classes:
return False
print( 'ok!' )
return True
print( safe_exp, is_it_safe( safe_exp ) )
print( )
print( unsafe_exp, is_it_safe( unsafe_exp ) )
print( )
print( unsafe_exp2, is_it_safe( unsafe_exp2 ) )
print( )
/Output:
trying ( 2+ 4 )* 7
<_ast.Module object at 0x00BB5DF0>
<_ast.Expr object at 0x00BB5E10>
<_ast.BinOp object at 0x00BB5E30>
<_ast.BinOp object at 0x00BB5E50>
<_ast.Mult object at 0x00BAF590>
<_ast.Num object at 0x00BB5EB0>
<_ast.Num object at 0x00BB5E70>
<_ast.Add object at 0x00BAF410>
<_ast.Num object at 0x00BB5E90>
ok!
( 2+ 4 )* 7 True
trying ( 2+ 4 ).__class__
<_ast.Module object at 0x00BB5E90>
<_ast.Expr object at 0x00BB5DF0>
<_ast.Attribute object at 0x00BB5E10>
( 2+ 4 ).__class__ False
trying __import__( "os" )
<_ast.Module object at 0x00BB5E10>
<_ast.Expr object at 0x00BB5E30>
<_ast.Call object at 0x00BB5E50>
__import__( "os" ) False
More information about the Python-list
mailing list