from opcode import HAVE_ARGUMENT, opmap, opname
from types import CodeType
+import logging
+import os
__all__ = ['test_expr', 'literal_eval', 'safe_eval', 'const_eval', 'ext_eval' ]
+# The time module is usually already provided in the safe_eval environment
+# but some code, e.g. datetime.datetime.now() (Windows/Python 2.5.2, bug
+# lp:703841), does import time.
+_ALLOWED_MODULES = ['_strptime', 'time']
+
_CONST_OPCODES = set(opmap[x] for x in [
- 'POP_TOP', 'ROT_TWO', 'ROT_THREE', 'ROT_FOUR', 'DUP_TOP',
+ 'POP_TOP', 'ROT_TWO', 'ROT_THREE', 'ROT_FOUR', 'DUP_TOP','POP_BLOCK','SETUP_LOOP',
'BUILD_LIST', 'BUILD_MAP', 'BUILD_TUPLE',
'LOAD_CONST', 'RETURN_VALUE', 'STORE_SUBSCR'] if x in opmap)
_SAFE_OPCODES = _EXPR_OPCODES.union(set(opmap[x] for x in [
'STORE_MAP', 'LOAD_NAME', 'CALL_FUNCTION', 'COMPARE_OP', 'LOAD_ATTR',
- 'STORE_NAME', 'GET_ITER', 'FOR_ITER', 'LIST_APPEND', 'JUMP_ABSOLUTE',
- 'DELETE_NAME', 'JUMP_IF_TRUE', 'JUMP_IF_FALSE',
+ 'STORE_NAME', 'GET_ITER', 'FOR_ITER', 'LIST_APPEND', 'DELETE_NAME',
+ 'JUMP_FORWARD', 'JUMP_IF_TRUE', 'JUMP_IF_FALSE', 'JUMP_ABSOLUTE',
+ 'MAKE_FUNCTION', 'SLICE+0', 'SLICE+1', 'SLICE+2', 'SLICE+3',
+ # New in Python 2.7 - http://bugs.python.org/issue4715 :
+ 'JUMP_IF_FALSE_OR_POP', 'JUMP_IF_TRUE_OR_POP', 'POP_JUMP_IF_FALSE',
+ 'POP_JUMP_IF_TRUE'
] if x in opmap))
+_logger = logging.getLogger('safe_eval')
def _get_opcodes(codeobj):
"""_get_opcodes(codeobj) -> [opcodes]
Test that the expression contains only the allowed opcodes.
If the expression is valid and contains only allowed codes,
- return the compiled code object. Otherwise raise a ValueError.
+ return the compiled code object.
+ Otherwise raise a ValueError, a Syntax Error or TypeError accordingly.
"""
try:
+ if mode == 'eval':
+ # eval() does not like leading/trailing whitespace
+ expr = expr.strip()
code_obj = compile(expr, "", mode)
- except:
+ except (SyntaxError, TypeError):
+ _logger.debug('Invalid eval expression', exc_info=True)
+ raise
+ except Exception:
+ _logger.debug('Disallowed or invalid eval expression', exc_info=True)
raise ValueError("%s is not a valid expression" % expr)
for code in _get_opcodes(code_obj):
if code not in allowed_codes:
node_or_string = node_or_string.body
return _convert(node_or_string)
-
+def _import(name, globals={}, locals={}, fromlist=[], level=-1):
+ if name in _ALLOWED_MODULES:
+ return __import__(name, globals, locals, level)
+ raise ImportError(name)
def safe_eval(expr, globals_dict=None, locals_dict=None, mode="eval", nocopy=False):
"""safe_eval(expression[, globals[, locals[, mode[, nocopy]]]]) -> result
This can be used to e.g. evaluate
an OpenERP domain expression from an untrusted source.
+ Throws TypeError, SyntaxError or ValueError (not allowed) accordingly.
+
>>> safe_eval("__import__('sys').modules")
Traceback (most recent call last):
...
globals_dict.update(
__builtins__ = {
+ '__import__': _import,
'True': True,
'False': False,
'None': None,
'dict': dict,
'list': list,
'tuple': tuple,
+ 'map': map,
+ 'abs': abs,
+ 'reduce': reduce,
+ 'filter': filter,
+ 'round': round,
+ 'len': len,
+ 'set' : set
}
)
-
return eval(test_expr(expr,_SAFE_OPCODES, mode=mode), globals_dict, locals_dict)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: