"Duncan Booth" <[EMAIL PROTECTED]> wrote in message news:[EMAIL PROTECTED] > Dan Bishop wrote: > >> Simon Brunning wrote: >>> [...] >> >> Or if you do use eval, don't give it access to any names. >> >>> [...] >> os.system("rm -rf *") >> Traceback (most recent call last): >> File "<stdin>", line 1, in ? >> File "<string>", line 0, in ? >> NameError: name 'os' is not defined >> > Have you tried giving it the string '__import__("os").system("rm -rf *")'? > [Don't try that at home children!] > > Even if you take steps to avoid that working by hiding the builtins, there > are still too many ways to do nasty things with eval for it ever to be > safe.
There was a posting here Nov 5, 2003 by Huaiyu Zhu at IBM Almaden that shows how to do eval type stuff safely. The basic notion is to use the compiler and then check the ast to see if the result fits the straitjacket you want to put it into. Pass / Fail; trying to fix it up if it's "close" is usually a real bad idea. He gives an example, and there's a much more extensive set of working code in the taBase.py module of PyFit that handles lists, tuples and dicts which contain arbitrary literals including complex and arbitrarily nested lists, tuples and dicts. ------- code snippet starts here -------- def _safeEval(self, s): """ Evaluate strings that only contain the following structures: const, tuple, list, dict Taken from c.l.py newsgroup posting Nov 5, 2003 by Huaiyu Zhu at IBM Almaden """ #print "in _safeEval. input: '%s'" % s node1 = compiler.parse(s) # !!! special case of attempting to compile a lone string if node1.doc is not None and len(node1.node.nodes) == 0: #print "in _safeEval. string: '%s' found as docstring" % node1.doc return node1.doc #print "in _safeEval. nodes: '%s'" % (node1,) stmts = node1.node.nodes assert len(stmts) == 1 node = compiler.parse(s).node.nodes[0] assert node.__class__ == compiler.ast.Discard nodes = node.getChildNodes() assert len(nodes) == 1 result = self._safeAssemble(nodes[0]) #print "in _safeEval result: '%s'" % (result,) return result seq_types = { compiler.ast.Tuple: tuple, compiler.ast.List: list, } map_types = { compiler.ast.Dict: dict, } oper_types = { compiler.ast.Add: operator.add, compiler.ast.Sub: operator.sub, } builtin_consts = { "True": True, "False": False, "None": None, } def _safeAssemble(self, node): """ Recursively assemble parsed ast node """ cls = node.__class__ if cls == compiler.ast.Const: return node.value elif cls in self.seq_types: nodes = node.nodes args = map(self._safeAssemble, nodes) return self.seq_types[cls](args) elif cls in self.map_types: keys, values = zip(*node.items) keys = map(self._safeAssemble, keys) values = map(self._safeAssemble, values) return self.map_types[cls](zip(keys, values)) elif cls in self.oper_types: left = self._safeAssemble(node.left) right = self._safeAssemble(node.right) if type(left) == type(1.0j) or type(right) == type(1.0j): return self.oper_types[cls](left, right) else: raise FitException, ("Parse001",) elif cls == compiler.ast.Name: result = self.builtin_consts.get(node.name, "?") if result != "?": return result else: raise FitException, ("Parse002", node.name) else: raise FitException, ("Parse003", cls) ------- end of code snippet ----------- John Roth > -- http://mail.python.org/mailman/listinfo/python-list