I am trying to write safyfied evaluator class, and get unexpected __builtins__ added to dictionary after evaluation. Can somebody explain why/how it happens?

import math

def echo(n):
    print n
    return n

def nothing(n):
    return n

debug = nothing

class MathParser(object):
    Mathematical Expression Evaluator class.
    call evaluate() function that will return you the result of the 
    mathematical expression given as a string when initializing.
    math library functions allowed, no __ allowed
    __slots__ = 'expression', 'variables', 'math'

    def __init__(self, expression, variables=None):
        self.math = dict((a,b) for a,b in math.__dict__.items() if not a.startswith('__') and '__' not in str(b))
        assert '__builtins__' not in self.math # passes

        self.expression = expression
        self.variables = variables

    def check(self):
        if any(any('__' in debug(str(n)) for n in d)
               for d in (self.expression.split(), self.math.keys(), self.math.values(), self.variables.keys(), self.variables.values())):
            raise ValueError('__ not allowed')
        return True

    def evaluate(self, variables=None):
        """ Evaluate the mathematical expression given as a string in the expression member variable.
        if not variables is None:
        if self.check():        
            return eval(self.expression, self.math, self.variables)

    def __str__(self):
        return str((str(self.expression), str(self.variables), str(self.math)))

if __name__ == "__main__":

    variables = {'x':1.5, 'y':2.0}
    g = 3
    for expr in '1+ 2+sin (x+ y)/e+pi', '1+2+sin( x + y)/abc(x)', "[]+__import__('os').listdir('/')", "dir()", 'g+x' :
            p = MathParser(expr, variables)
            assert '__builtins__' not in p.math # passes
            print('%s = %s' % (expr,   p.evaluate()))
            assert '__builtins__' not in p.math # does not pass
        except (NameError, ValueError) as e:

    # update variable in evaluate
    debug = echo
    print('%s = %s' % (expr,  p.evaluate()))
    print('%s = %s' % (expr,  p.evaluate({'builtins':__builtins__})))

So only the assert after p.evaluate() is failing.

Edited by pyTony

4 Years
Discussion Span
Last Post by HiHe

By setting the globals parameter to {"__builtins__":None} and putting everything to the math dictionary and giving it as locals, it seemed to be solved, but would be nice to understand better.

Edited by pyTony


It's always been like this: if you evaluate a python expression in a namespace, the __builtins__ dictionary is inserted in that namespace. For example

In [3]: D = dict()

In [4]: D.keys()
Out[4]: []

In [5]: eval("1+2", D)
Out[5]: 3

In [6]: D.keys()
Out[6]: ['__builtins__']

I think it's done at line 4702 in ceval.c.

Edited by Gribouillis


You can keep the import local in a function:

def mylocals():
    import math

This question has already been answered. Start a new discussion instead.
Have something to contribute to this discussion? Please be thoughtful, detailed and courteous, and be sure to adhere to our posting rules.