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,16 +87,65 @@ def _get_opcodes(codeobj):
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,15 +160,13 @@ def test_expr(expr, allowed_codes, mode="eval"):
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,19 +235,13 @@ def safe_eval(expr, globals_dict=None, locals_dict=None, mode="eval", nocopy=Fal
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 = {}
0 commit comments