Package park :: Package util :: Module safemath

Source Code for Module park.util.safemath

  1  # This program is public domain 
  2  """ 
  3  Safe math context for evaluating expressions. 
  4   
  5  The following are defined:: 
  6   
  7    context - dictionary of math functions suitable for use as global in eval 
  8    seval(expr) - expression evaluator 
  9    sexec(expr) - code evaluator 
 10    show() - display the symbols available in contexgt 
 11   
 12  The math symbols (functions, pi, e, inf, nan) are pulled from numpy and scipy 
 13  so they will work equally well with scalars and vectors, real and complex. 
 14   
 15  The restricted environment does not allow any use of the underscore 
 16  character.  This suppresses most python introspection exploits.  Of 
 17  course, it also means that user code cannot define new classes or many 
 18  of the other things a plugin environment may want to support. 
 19   
 20  Note: DoS attacks are pretty much impossible to stop if you give access 
 21  to the interpreter.  E.g., 'a'*2**64 will exceed memory, as will 
 22  array([1,2]*2**64).  Running the service in a separate process with 
 23  resource limits can mitigate the problem, as well as protect against 
 24  accidental infinite loops.  If this process is running in a chroot jail 
 25  or on a virtual machine, then eval/exec can be used directly. 
 26  """ 
 27  # TODO: create a safe context for string manipulations 
 28   
 29  __all__ = ['context','eval','show'] 
 30   
 31  import __builtin__ 
 32  import numpy 
 33   
 34  # Erf, bessel, airy, etc. come from scipy.special 
 35  try: 
 36      import scipy.special 
 37      special = scipy.special.__dict__ 
 38  except ImportError: 
 39      special = {} 
 40   
41 -def _math_context():
42 """ 43 Return a context suitable for evaluating math expressions. 44 45 The context is a subset of the following: 46 47 from numpy import * 48 from numpy.linalg import * 49 from scipy.special import * 50 51 Most of the mathematical functions are included. 52 """ 53 context = {'__builtins__':None, 54 '__name__':'__safemath__', 55 } 56 57 # Builtins 58 for fn in [ 59 'True', 'False', 'None', 60 'enumerate', 'zip', 'range', 'reversed', 61 ]: 62 context[fn] = getattr(__builtin__,fn) 63 64 # Scalars 65 for v in ['e','pi','inf','nan']: 66 context[v] = numpy.__dict__[v] 67 context['Inf'] = numpy.inf 68 context['INF'] = numpy.inf 69 context['NaN'] = numpy.nan 70 context['NAN'] = numpy.nan 71 72 # Trig functions 73 for base in ['cos','sin','tan']: 74 for fn in [base, base+'h']: 75 context[fn] = numpy.__dict__[fn] 76 context['a'+fn] = numpy.__dict__['arc'+fn] 77 context['arc'+fn] = numpy.__dict__['arc'+fn] 78 context['atan2'] = numpy.__dict__['arctan2'] 79 context['arctan2'] = numpy.__dict__['arctan2'] 80 81 # Other scalar functions 82 for fn in [ 83 'abs','angle', 'around','clip', 'degrees', 'e', 84 'exp', 'expm1', 'fabs', 'hypot', 'imag', 85 'iscomplex', 'isfinite', 'isinf', 'isnan', 'isreal', 'isscalar', 86 'log', 'log10', 'log1p', 'log2', 'polyval', 'radians', 87 'round', 'sign', 'sinc', 'sqrt', 'square', 'unwrap', 88 ]: 89 # For compatibility with the whole numpy 1.0 series 90 if fn in numpy.__dict__: 91 context[fn] = numpy.__dict__[fn] 92 93 # Special functions from scipy (if available) 94 for fn in [ 95 'airy','airye', 96 'jn', 'jv', 'yn', 'yv', 'kn', 'kv', 'iv', 97 'j0', 'j1', 'y0', 'y1', 'k0', 'k1', 'i0', 'i1', 98 'gamma', 'gammaln', 'gammainc', 99 'beta', 'betaln', 'betainc', 100 'erf', 'erfc', 'erfinv', 'erfcinv', 101 'expn', 'exp1', 'expi', 102 'wofz', 'dawsn', 'shichi', 'sici', 'spence', 'zeta', 'zetac', 103 ]: 104 if fn in special: 105 context[fn] = special[fn] 106 107 108 # Vector functions 109 for fn in [ 110 'all', 'allclose', 'amax', 'amin', 'any', 'convolve', 111 'corrcoef', 'correlate', 'cov', 'cross', 'cumprod', 'cumsum', 112 'diff', 'dot', 'histogram', 'interp', 'kron', 113 'mean', 'median', 'outer', 'poly', 'prod', 'roots', 'std', 114 'sort', 'sum', 'trace', 'trapz', 'var', 'vdot', 115 ]: 116 context[fn] = numpy.__dict__[fn] 117 118 # Array constructors and slicers 119 for fn in [ 120 'array', 'arange', 'choose', 'dstack', 'diag', 121 'extract', 'eye', 'hstack', 'linspace', 'logspace', 122 'meshgrid', 'ones', 'reshape', 'searchsorted', 'select', 123 'squeeze', 'take', 'tri', 'tril', 'triu', 'vstack', 124 'where', 'zeros', 125 ]: 126 # For compatibility with the whole numpy 1.0 series 127 if fn in numpy.__dict__: 128 context[fn] = numpy.__dict__[fn] 129 130 # Linear algebra 131 for fn in [ 132 'cholesky', 'cond', 'det', 133 'eig', 'eigh', 'eigvals', 'eigvalsh', 'inv', 'lstsq', 134 'norm', 'pinv', 'qr', 'solve', 'svd', 135 'tensorinv','tensorsolve' 136 ]: 137 # For compatibility with the whole numpy 1.0 series 138 if fn in numpy.linalg.__dict__: 139 context[fn] = numpy.linalg.__dict__[fn] 140 141 # Make sure we haven't leaked the '_' character into the user context, 142 # including via chr(). The '_' character can be used to construct 143 # python introspection statements that we can't easily prevent, so 144 # simply eliminate them. 145 if 'chr' in context: 146 raise RuntimeError('chr() snuck into context') 147 return context
148 149 context = _math_context() 150 """Table of available mathematical functions""" 151
152 -def seval(expr, locals=None, context=context):
153 """ 154 Evaluate an expression containing math functions. 155 """ 156 if '_' in expr: raise ValueError('expression contains underscores') 157 return eval(expr,context,locals)
158
159 -def sexec(code, locals=None, context=context):
160 """ 161 Evaluate code in a restricted context, returning a dictionary of 162 local variables. Context should include all symbols that the code 163 might need; no import statements will be allowed. 164 """ 165 if '_' in code: raise ValueError('code contains underscores') 166 exec code in context,locals
167
168 -def show():
169 """ 170 Show the functions available in the math context. 171 """ 172 keys = context.keys() 173 keys.sort() 174 print ", ".join(keys)
175
176 -def _zipimport_hole():
177 # This example (due to Andrew Dalke) assumes that an egg with the os 178 # symbol available at the top level has previously be imported, and 179 # that we can guess the path to its zip-file. Given that we are 180 # an open source project, it is likely the attacker can make pretty 181 # good guesses for this sort of thing. 182 # 183 # This is not a formal test since it depends on the particular execution 184 # environment, which may change from installation to installation. 185 import configobj 186 s = """ 187 all_types = ().__class__.__bases__[0].__subclasses__() 188 zipimport = [x for x in all_types if x.__name__ == "zipimporter"][0] 189 egg = "/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/site-packages/configobj-4.5.3-py2.5.egg" 190 x = zipimport(egg) 191 mod = x.load_module("configobj") 192 193 print "system", mod.os.system("ls") 194 """ 195 sexec(s)
196
197 -def test():
198 # Simple expressions 199 assert seval("3*pi") == 3*numpy.pi 200 201 # Check additional context examples 202 class Table: 203 Si = 2.07 204 values = {'Au': 4}
205 table = Table() 206 def f(x): return x+3 207 vars = dict(f=f, table=table, x=3.5, vec=numpy.arange(10)) 208 assert seval("f(3)",vars) == 6 209 assert seval("table.Si*4*pi", vars) == 2.07*4*numpy.pi 210 assert seval("table.values['Au']+3", vars) == 7 211 assert seval("sum(vec)", vars) == 45 212 assert seval("x**2",vars) == 3.5**2 213 assert seval("True") == True 214 assert seval("False") == False 215 assert seval("None") is None 216 assert seval('zip([1,2,3],[4,5,6])') == [(1,4),(2,5),(3,6)] 217 218 # Verify that we capture invalid expressions 219 # The following works when used directly, but not from seval: 220 # poly.func_globals['__builtins__']['__import__']('sys').platform 221 #print numpy.poly.func_globals['__builtins__']['__import__']('sys').platform 222 #print seval("poly([2,1])") 223 #print seval("poly.func_globals['__builtins__']['__import__']('sys').platform") 224 for expr in ['G4.cage', 'M0.cage', 'M1.G1 + *2', 225 'piddle', 226 'open("hello","w")', 227 '().__class__', 228 'import sys; print sys.platform', 229 '__import__("sys").platform', 230 '(lambda: __import__("sys").argv)()', 231 "poly.func_globals['__builtins__']['__import__']('sys').platform", 232 ]: 233 try: seval(expr) 234 except Exception,msg: pass 235 else: raise "Failed to raise error for %s"%expr 236 try: sexec(expr) 237 except Exception,msg: pass 238 else: raise "Failed to raise error for %s"%expr 239 240 if __name__ == "__main__": test() 241