感谢arifwn,我开始探索Python的
ast(抽象语法树)模块。该模块提供了一个
ast.NodeVisitor遍历树的类。该代码子类化
NodeVisitor以创建语法检查器,该检查器将基本数学所需的代码列入白名单。由于仅允许某些功能,并且只允许使用未使用的名称,因此对函数调用和名称进行了专门的监视。
import astallowed_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请注意,这旨在仅接受代码的数学部分,并提供了函数定义和return语句。
可以修改这种将所有必需的安全构造列入白名单,特别是将不安全的构造列入白名单的方法,以产生许多有用的Python子集。非常适合用户脚本!
请注意,为了安全地执行此操作,它应该在自己的线程中带有超时,以减少名称冲突和如果用户代码生成无限循环或类似情况而导致的超时。



