Это решение, которое я выбрал. Для обсуждения безопасности этого подхода см.
Благодаря 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