Какой самый простой безопасный способ выполнить математический код пользователя на веб-сервере Python?

Я понимаю, что этот вопрос задавался раньше, однако этот случай немного отличается.

Я хочу запустить доску изображений на Python (используя web.py), которая позволит пользователям создавать новые изображения, отправляя код. Код будет иметь форму единственной функции, которая принимает координаты x, y пикселя и возвращает значения r, g, b, например:

def simpleGradient(xrel,yrel):
    r = xrel*256
    g = yrel*256
    b = 0
    return [r,g,b]

Требуется только очень маленький синтаксис, и он не обязательно должен быть python. Использование exec с ограниченным объемом кажется слишком небезопасным, а использование PyPy или виртуальной машины кажется излишне сложным (я новичок во всем этом).

Есть ли питонический способ выполнить код на гораздо меньшем языке, вместо того, чтобы помещать его в песочницу? Либо подмножество Python (синтаксический анализ и добавление в белые списки?), Либо математически ориентированный язык, который я могу встроить?


person SudoNhim    schedule 18.05.2012    source источник
comment
На самом деле я бы использовал песочницу PyPy.   -  person Jonathan Leonard    schedule 18.05.2012
comment
Несколько других ответов, которые я прочитал, проголосовали против ... Так что я еще не изучал PyPy - я проверю, спасибо   -  person SudoNhim    schedule 18.05.2012
comment
Отличный вопрос, возможно, PyPy - это ответ. Я только что сегодня говорил о том, что Python может быть здесь немного короче, чем, скажем, lua.   -  person Skylar Saveland    schedule 18.05.2012
comment
Если у вас есть время, я думаю, было бы забавно использовать собственный компилятор Python: stackoverflow.com/questions/594266/equation-parsing-in-python   -  person arifwn    schedule 18.05.2012
comment
Вау ... Я подумывал о создании своего собственного языка (в настоящее время пишу компилятор PL0 для назначения uni), но этот способ мог бы быть намного веселее!   -  person SudoNhim    schedule 18.05.2012


Ответы (2)


Это решение, которое я выбрал. Для обсуждения безопасности этого подхода см.

Благодаря arifwn я начал изучать модуль Python ast (абстрактное синтаксическое дерево). Этот модуль предоставляет класс ast.NodeVisitor для обхода дерева. Этот код является подклассом NodeVisitor для создания средства проверки синтаксиса, которое заносит в белый список код, необходимый для базовой математики. Вызовы функций и имена специально отслеживаются, так как должны быть разрешены только определенные функции и должны быть разрешены только неиспользуемые имена.

import ast

allowed_functions = set([
    #math library
    'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh',
    'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf',
    'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod',
    'frexp', 'fsum', 'gamma', 'hypot', 'isinf', 'isnan', 'ldexp',
    'lgamma', 'log', 'log10', 'log1p', 'modf', 'pi', 'pow', 'radians',
    'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'trunc',
    #builtins
    'abs', 'max', 'min', 'range', 'xrange'
    ])

allowed_node_types = set([
    #Meta
    'Module', 'Assign', 'Expr',
    #Control
    'For', 'If', 'Else',
    #Data
    'Store', 'Load', 'AugAssign', 'Subscript',
    #Datatypes
    'Num', 'Tuple', 'List',
    #Operations
    'BinOp', 'Add', 'Sub', 'Mult', 'Div', 'Mod', 'Compare'
    ])

safe_names = set([
    'True', 'False', 'None'
    ])


class SyntaxChecker(ast.NodeVisitor):

    def check(self, syntax):
        tree = ast.parse(syntax)
        self.passed=True
        self.visit(tree)

    def visit_Call(self, node):
        if node.func.id not in allowed_functions:
            raise SyntaxError("%s is not an allowed function!"%node.func.id)
        else:
            ast.NodeVisitor.generic_visit(self, node)

    def visit_Name(self, node):
        try:
            eval(node.id)
        except NameError:
            ast.NodeVisitor.generic_visit(self, node)
        else:
            if node.id not in safe_names and node.id not in allowed_functions:
                raise SyntaxError("%s is a reserved name!"%node.id)
            else:
                ast.NodeVisitor.generic_visit(self, node)

    def generic_visit(self, node):
        if type(node).__name__ not in allowed_node_types:
            raise SyntaxError("%s is not allowed!"%type(node).__name__)
        else:
            ast.NodeVisitor.generic_visit(self, node)

if __name__ == '__main__':
    x = SyntaxChecker()
    while True:
        try:
            x.check(raw_input())
        except Exception as e:
            print e

Обратите внимание, что это предназначено для приема только математической части кода, предоставляется определение функции и оператор возврата.

Этот метод внесения в белый список всех необходимых безопасных конструкций и, в частности, внесения в белый список требуемых небезопасных конструкций, может быть изменен для создания многих полезных подмножеств Python; отлично подходит для пользовательских скриптов!

Обратите внимание, что для того, чтобы это выполнялось безопасно, он должен быть в собственном потоке с тайм-аутом, чтобы уменьшить коллизии имен и тайм-аут, если пользовательский код генерирует бесконечный цикл или что-то подобное.

person SudoNhim    schedule 18.05.2012
comment
Это должен быть отдельный вопрос ИМО. - person TryPyPy; 19.05.2012
comment
Прости. Я знаю кого-то, кто, надеюсь, сможет проверить это за меня; если он сочтет, что все в порядке, я переформатирую его, чтобы он больше походил на ответ. (Иначе удалю). - person SudoNhim; 19.05.2012
comment
Я имел в виду: у вас будет больше глаз (и, возможно, новых предложений), если вы превратите этот ответ в новый вопрос :) - person TryPyPy; 19.05.2012
comment
Перемещено в stackoverflow.com/questions/10661079/ - person SudoNhim; 19.05.2012

На странице pypi pysandbox есть много полезной информации .

person Skylar Saveland    schedule 18.05.2012