Duncan Booth said:
Dan said:
Simon said:
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