Используя ast и белые списки, чтобы сделать eval () python безопасным?

ХОРОШО. язнать эксперты иметь разговорный и ты не долженever использовать Pythoneval() на ненадежных данных, когда-либо. Я не умнее, чем весь остальной мир, и даже не должен пробовать это. Но! Я все равно собираюсь.

Моя основная проблема заключается в том, что я собираюсь написать небольшую программу для вычислений калькулятора, которая будет принимать ненадежные входные данные, используя подмножество синтаксиса python. Я знаю: использоватькурсировать или жеPyparsing и напиши парсер и поехали. Бродить с мимолетными глобалами и местными жителямиeval() не будет делать трюк.

Все подходы, которые я видел (и о которых мы подозревали), пытаются перечислить зло. Здесь я пытаюсь перечислитьgood - получить AST, разрешить только несколько типов узлов, а затем убедиться, что любые вызовы относятся к одной из набора функций из белого списка. Вот мини-реализация (исуть):

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))

Так,my_safe_eval('2*(4+exp(1.3))') работает, покаmy_safe_eval('[].__class__') трюки иmy_safe_eval('open("/something/evil")') также запрещено - без запрета__builtins__ или же__locals__ или что-нибудь.

Я ... я думаю, что это работает. Я сумасшедший?