@@ -260,15 +260,17 @@ def __init__(self, astnode):
260260 def __str__ (self ):
261261 return 'Immediate(%d)' % (self .node .value ,)
262262
263- _forbidden_re = re .compile ('[\;[\:]|__' )
263+
264+ _forbidden_re = re .compile ('[\;[\:]|__|\.[abcdefghjklmnopqstuvwxyzA-Z_]' )
264265def stringToExpression (s , types , context ):
265266 """Given a string, convert it to a tree of ExpressionNode's.
266267 """
267268 # sanitize the string for obvious attack vectors that NumExpr cannot
268269 # parse into its homebrew AST. This is to protect the call to `eval` below.
269- # We forbid `;`, `:`. `[` and `__`
270- # We would like to forbid `.` but it is both a reference and decimal point.
271- if _forbidden_re .search (s ) is not None :
270+ # We forbid `;`, `:`. `[` and `__`, and attribute access via '.'.
271+ # We cannot ban `.real` or `.imag` however...
272+ no_whitespace = re .sub (r'\s+' , '' , s )
273+ if _forbidden_re .search (no_whitespace ) is not None :
272274 raise ValueError (f'Expression { s } has forbidden control characters.' )
273275
274276 old_ctx = expressions ._context .get_current_context ()
@@ -766,7 +768,6 @@ def getArguments(names, local_dict=None, global_dict=None, _frame_depth: int=2):
766768_names_cache = CacheDict (256 )
767769_numexpr_cache = CacheDict (256 )
768770_numexpr_last = {}
769- _numexpr_sanity = set ()
770771evaluate_lock = threading .Lock ()
771772
772773# MAYBE: decorate this function to add attributes instead of having the
@@ -828,6 +829,13 @@ def validate(ex: str,
828829 _frame_depth: int
829830 The calling frame depth. Unless you are a NumExpr developer you should
830831 not set this value.
832+
833+ Note
834+ ----
835+ Both `validate` and by extension `evaluate` call `eval(ex)`, which is
836+ potentially dangerous on unsanitized inputs. As such, NumExpr does some
837+ sanitization, banning the character ':;[', the dunder '__', and attribute
838+ access to all but '.r' for real and '.i' for imag access to complex numbers.
831839 """
832840 global _numexpr_last
833841
@@ -857,8 +865,6 @@ def validate(ex: str,
857865 kwargs = {'out' : out , 'order' : order , 'casting' : casting ,
858866 'ex_uses_vml' : ex_uses_vml }
859867 _numexpr_last = dict (ex = compiled_ex , argnames = names , kwargs = kwargs )
860- # with evaluate_lock:
861- # return compiled_ex(*arguments, **kwargs)
862868 except Exception as e :
863869 return e
864870 return None
@@ -918,7 +924,12 @@ def evaluate(ex: str,
918924 The calling frame depth. Unless you are a NumExpr developer you should
919925 not set this value.
920926
921-
927+ Note
928+ ----
929+ Both `validate` and by extension `evaluate` call `eval(ex)`, which is
930+ potentially dangerous on unsanitized inputs. As such, NumExpr does some
931+ sanitization, banning the character ':;[', the dunder '__', and attribute
932+ access to all but '.r' for real and '.i' for imag access to complex numbers.
922933 """
923934 # We could avoid code duplication if we called validate and then re_evaluate
924935 # here, but they we have difficulties with the `sys.getframe(2)` call in
0 commit comments