~openerp-dev/openobject-server/saas-3-bug_1291322-ptr

« back to all changes in this revision

Viewing changes to openerp/tools/safe_eval.py

  • Committer: Kersten Jeremy
  • Date: 2014-04-09 15:35:27 UTC
  • mfrom: (4743.1.535 7.0)
  • Revision ID: jke@openerp.com-20140409153527-mic1a8afcvdhsd27
[MERGE] Forward port of branch 7.0 up to rev 5278 rev-id jke@openerp.com-20140409151659-xwttchbtbj02v1w7

Show diffs side-by-side

added added

removed removed

Lines of Context:
71
71
    'CONTINUE_LOOP', 'RAISE_VARARGS',
72
72
    # New in Python 2.7 - http://bugs.python.org/issue4715 :
73
73
    'JUMP_IF_FALSE_OR_POP', 'JUMP_IF_TRUE_OR_POP', 'POP_JUMP_IF_FALSE',
74
 
    'POP_JUMP_IF_TRUE', 'SETUP_EXCEPT', 'END_FINALLY'
 
74
    'POP_JUMP_IF_TRUE', 'SETUP_EXCEPT', 'END_FINALLY', 'LOAD_FAST',
 
75
    'LOAD_GLOBAL', # Only allows access to restricted globals
75
76
    ] if x in opmap))
76
77
 
77
78
_logger = logging.getLogger(__name__)
86
87
    [100, 100, 23, 100, 100, 102, 103, 83]
87
88
    """
88
89
    i = 0
89
 
    opcodes = []
90
90
    byte_codes = codeobj.co_code
91
91
    while i < len(byte_codes):
92
92
        code = ord(byte_codes[i])
93
 
        opcodes.append(code)
 
93
        yield code
 
94
 
94
95
        if code >= HAVE_ARGUMENT:
95
96
            i += 3
96
97
        else:
97
98
            i += 1
98
 
    return opcodes
 
99
 
 
100
def assert_no_dunder_name(code_obj, expr):
 
101
    """ assert_no_dunder_name(code_obj, expr) -> None
 
102
 
 
103
    Asserts that the code object does not refer to any "dunder name"
 
104
    (__$name__), so that safe_eval prevents access to any internal-ish Python
 
105
    attribute or method (both are loaded via LOAD_ATTR which uses a name, not a
 
106
    const or a var).
 
107
 
 
108
    Checks that no such name exists in the provided code object (co_names).
 
109
 
 
110
    :param code_obj: code object to name-validate
 
111
    :type code_obj: CodeType
 
112
    :param str expr: expression corresponding to the code object, for debugging
 
113
                     purposes
 
114
    :raises NameError: in case a forbidden name (containing two underscores)
 
115
                       is found in ``code_obj``
 
116
 
 
117
    .. note:: actually forbids every name containing 2 underscores
 
118
    """
 
119
    for name in code_obj.co_names:
 
120
        if "__" in name:
 
121
            raise NameError('Access to forbidden name %r (%r)' % (name, expr))
 
122
 
 
123
def assert_valid_codeobj(allowed_codes, code_obj, expr):
 
124
    """ Asserts that the provided code object validates against the bytecode
 
125
    and name constraints.
 
126
 
 
127
    Recursively validates the code objects stored in its co_consts in case
 
128
    lambdas are being created/used (lambdas generate their own separated code
 
129
    objects and don't live in the root one)
 
130
 
 
131
    :param allowed_codes: list of permissible bytecode instructions
 
132
    :type allowed_codes: set(int)
 
133
    :param code_obj: code object to name-validate
 
134
    :type code_obj: CodeType
 
135
    :param str expr: expression corresponding to the code object, for debugging
 
136
                     purposes
 
137
    :raises ValueError: in case of forbidden bytecode in ``code_obj``
 
138
    :raises NameError: in case a forbidden name (containing two underscores)
 
139
                       is found in ``code_obj``
 
140
    """
 
141
    assert_no_dunder_name(code_obj, expr)
 
142
    for opcode in _get_opcodes(code_obj):
 
143
        if opcode not in allowed_codes:
 
144
            raise ValueError(
 
145
                "opcode %s not allowed (%r)" % (opname[opcode], expr))
 
146
    for const in code_obj.co_consts:
 
147
        if isinstance(const, CodeType):
 
148
            assert_valid_codeobj(allowed_codes, const, 'lambda')
99
149
 
100
150
def test_expr(expr, allowed_codes, mode="eval"):
101
151
    """test_expr(expression, allowed_codes[, mode]) -> code_object
110
160
            # eval() does not like leading/trailing whitespace
111
161
            expr = expr.strip()
112
162
        code_obj = compile(expr, "", mode)
113
 
    except (SyntaxError, TypeError):
 
163
    except (SyntaxError, TypeError, ValueError):
114
164
        raise
115
165
    except Exception, e:
116
166
        import sys
117
167
        exc_info = sys.exc_info()
118
168
        raise ValueError, '"%s" while compiling\n%r' % (ustr(e), expr), exc_info[2]
119
 
    for code in _get_opcodes(code_obj):
120
 
        if code not in allowed_codes:
121
 
            raise ValueError("opcode %s not allowed (%r)" % (opname[code], expr))
 
169
    assert_valid_codeobj(allowed_codes, code_obj, expr)
122
170
    return code_obj
123
171
 
124
172
 
187
235
    This can be used to e.g. evaluate
188
236
    an OpenERP domain expression from an untrusted source.
189
237
 
190
 
    Throws TypeError, SyntaxError or ValueError (not allowed) accordingly.
191
 
 
192
 
    >>> safe_eval("__import__('sys').modules")
193
 
    Traceback (most recent call last):
194
 
    ...
195
 
    ValueError: opcode LOAD_NAME not allowed
196
 
 
 
238
    :throws TypeError: If the expression provided is a code object
 
239
    :throws SyntaxError: If the expression provided is not valid Python
 
240
    :throws NameError: If the expression provided accesses forbidden names
 
241
    :throws ValueError: If the expression provided uses forbidden bytecode
197
242
    """
198
243
    if isinstance(expr, CodeType):
199
 
        raise ValueError("safe_eval does not allow direct evaluation of code objects.")
200
 
 
201
 
    if '__subclasses__' in expr:
202
 
        raise ValueError('expression not allowed (__subclasses__)')
 
244
        raise TypeError("safe_eval does not allow direct evaluation of code objects.")
203
245
 
204
246
    if globals_dict is None:
205
247
        globals_dict = {}