Koloboking
Grey Team
- 12.01.2017
- 196
- 83
господа, кто хочет поломать голову?)
Разработчик делает калькулятор с поддержкой выражений, чтобы пользователи могли задавать скидки и правила округления в виде формул:
Задача
Разработчик делает калькулятор с поддержкой выражений, чтобы пользователи могли задавать скидки и правила округления в виде формул:
Python:
import ast
import operator
import math
class SafeMathEvaluator:
allowed_names = {
"abs": abs,
"round": round,
"min": min,
"max": max,
"pow": pow,
"floor": math.floor,
"ceil": math.ceil,
"pi": math.pi,
"e": math.e,
}
allowed_ops = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
ast.Pow: operator.pow,
ast.USub: operator.neg,
}
def eval_expr(self, expr):
tree = ast.parse(expr, mode="eval")
return self._eval(tree.body)
def _eval(self, node):
if isinstance(node, ast.Constant):
return node.value
elif isinstance(node, ast.BinOp):
if type(node.op) not in self.allowed_ops:
raise ValueError("Operator not allowed")
return self.allowed_ops[type(node.op)](
self._eval(node.left),
self._eval(node.right)
)
elif isinstance(node, ast.UnaryOp):
if type(node.op) not in self.allowed_ops:
raise ValueError("Unary operator not allowed")
return self.allowed_ops[type(node.op)](self._eval(node.operand))
elif isinstance(node, ast.Call):
if isinstance(node.func, ast.Name) and node.func.id in self.allowed_names:
func = self.allowed_names[node.func.id]
args = [self._eval(arg) for arg in node.args]
return func(*args)
raise ValueError("Function not allowed")
elif isinstance(node, ast.Name):
if node.id in self.allowed_names:
return self.allowed_names[node.id]
raise ValueError("Unknown identifier")
else:
raise TypeError("Unsupported expression node")
Пользователь может передать любую строку выражения в eval_expr(expr).
Нельзя использовать eval, exec, import, os, sys и т.п.
Но — есть способ заставить этот код “провалиться” и вытащить из него данные или вызвать нестандартное поведение.Вроде бы всё безопасно — только математика.