Usando ast e whitelists para tornar o eval () do python seguro?

ESTÁ BEM. Euconhecer a peritos ter falado e você não deveriasempre usar pythoneval() em dados não confiáveis, nunca. Eu não sou mais esperto do que o resto do mundo, e nem deveria tentar isso. Mas! Eu vou, de qualquer forma.

Meu problema básico é que eu estou olhando para escrever um pequeno programa de avaliador de calculadoras que receberá uma entrada não confiável, usando um subconjunto da sintaxe do python. Eu sei: usedobra oupyparsing e escreva um analisador e lá vamos nós. Aparafusando com globals passando e locals paraeval() não vai fazer o truque.

Todas as abordagens que tenho visto (e tem sido desconfiado) tentam enumerar o mal. Aqui estou tentando enumerarBoa - obtenha um AST, permita apenas alguns tipos de nó e verifique se todas as chamadas são para um conjunto de funções na lista de permissões. Aqui está uma mini-implementação (euma essência):

import ast
import math

SAFE_FX = {
    'exp': math.exp,
}

SAFE_NODES = set(
    (ast.Expression,
    ast.Num,
    ast.Call,
    ast.Name,
    ast.Load,
    ast.BinOp,
    ast.Add,
    ast.Sub,
    ast.Mult,
    ast.Div,)
)

class CleansingNodeVisitor(ast.NodeVisitor):
    def generic_visit(self, node):
        if type(node) not in SAFE_NODES:
            raise Exception("%s not in SAFE_NODES" % type(node))
        super(CleansingNodeVisitor, self).generic_visit(node)

    def visit_Call(self, call):
        if call.func.id not in SAFE_FX:
            raise Exception("Unknown function: %s" % call.func.id)

def my_safe_eval(s):
    tree = ast.parse(s, mode='eval')
    cnv = CleansingNodeVisitor()
    cnv.visit(tree)
    compiled = compile(tree, s, "eval")
    return(eval(compiled, SAFE_FX))

Assim,my_safe_eval('2*(4+exp(1.3))') funciona, enquantomy_safe_eval('[].__class__') truques emy_safe_eval('open("/something/evil")') é igualmente proibido - sem proibir__builtins__ ou__locals__ ou nada.

Eu acho que isso funciona. Eu estou louco?