Reputation: 118
I am writing a program where users need to be able to use self written mathematical functions containing functions from numpy and scipy, eg. scipy.special.wofz().
These functions will be stored in files and imported as strings by the program. I looked around and saw, that eval() or exec() are not a safe way to do it. eg. here.
The security issue would be that good users load a file from evil users who get access to the good users system.
I was thinking about doing something like this:
#!/bin/python
from scipy.special import *
from numpy import *
import sympy
# Define variable a
vars = {"a":1}
# This is the string I get from a file
string = "wofz(a)"
parsed_string = sympy.sympify(string)
parsed_string.evalf(subs=vars)
However, this does not work. It only returns:
wofz(a)
wofz(a) is not evaluated. Is this even supposed to work that way?
I had another idea: So I thought, once this mathematical function got through sympify, it should be safe. I could just simply do something like this:
globals = {wofz:wofz}
eval(str(parsed_string), vars, globals)
which works fine and returns:
(0.36787944117144233+0.60715770584139372j)
Is that safe? I know it's not nice.
Please help.
Upvotes: 4
Views: 2235
Reputation: 1461
If the string is the only untrusted information, I think the following should be safe:
To use eval()
with a restricted vocabulary, pass it a second argument that is a dictionary of allowed names, where __builtins__
is defined to something harmless (see http://docs.python.org/library/functions.html#eval).
>>> import numpy as np
>>> d = dict(linspace=np.linspace, range=range, __builtins__=None)
>>> eval("str(1), 2+2")
('1', 4)
>>> eval("str(1)", d)
Traceback (most recent call last)
NameError: name 'str' is not defined
>>> eval("{'a': linspace(0, 0.5, 6)}, range(2)", d)
({'a': array([ 0. , 0.1, 0.2, 0.3, 0.4, 0.5])}, [0, 1])
>>> eval("linspace.__dict__", d)
Traceback (most recent call last)
RuntimeError: function attributes not accessible in restricted mode
Upvotes: -2
Reputation: 2539
Use sympy, it's a way safer option.
import sympy
from sympy.core.function import Function
from sympy.core import S
from sympy import sympify
from sympy.functions import im
from scipy.special import wofz
class Wofz(Function):
is_real = True
@classmethod
def _should_evalf(csl,arg):
return True
def as_base_exp(cls):
return cls,S.One
def _eval_evalf(cls, prec):
return sympy.numbers.Number(im(wofz(float(cls.args[0]))))
print sympify("Wofz(2)",{'Wofz':Wofz}).evalf()
Output (you'll have to handle the imaginary part somehow):
0.340026217066065
Upvotes: 4