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