Skip to content

Commit 5c3dbb7

Browse files
committed
new SafeEval
1 parent 63d9fa1 commit 5c3dbb7

File tree

5 files changed

+43
-194
lines changed

5 files changed

+43
-194
lines changed

Diff for: module/lib/SafeEval.py

+40-63
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,47 @@
1-
## {{{ http://code.activestate.com/recipes/364469/ (r2)
2-
import compiler
1+
## {{{ http://code.activestate.com/recipes/286134/ (r3) (modified)
2+
import dis
33

4-
class Unsafe_Source_Error(Exception):
5-
def __init__(self,error,descr = None,node = None):
6-
self.error = error
7-
self.descr = descr
8-
self.node = node
9-
self.lineno = getattr(node,"lineno",None)
10-
11-
def __repr__(self):
12-
return "Line %d. %s: %s" % (self.lineno, self.error, self.descr)
13-
__str__ = __repr__
14-
15-
class SafeEval(object):
16-
17-
def visit(self, node,**kw):
18-
cls = node.__class__
19-
meth = getattr(self,'visit'+cls.__name__,self.default)
20-
return meth(node, **kw)
21-
22-
def default(self, node, **kw):
23-
for child in node.getChildNodes():
24-
return self.visit(child, **kw)
25-
26-
visitExpression = default
27-
28-
def visitConst(self, node, **kw):
29-
return node.value
4+
_const_codes = map(dis.opmap.__getitem__, [
5+
'POP_TOP','ROT_TWO','ROT_THREE','ROT_FOUR','DUP_TOP',
6+
'BUILD_LIST','BUILD_MAP','BUILD_TUPLE',
7+
'LOAD_CONST','RETURN_VALUE','STORE_SUBSCR'
8+
])
309

31-
def visitDict(self,node,**kw):
32-
return dict([(self.visit(k),self.visit(v)) for k,v in node.items])
33-
34-
def visitTuple(self,node, **kw):
35-
return tuple(self.visit(i) for i in node.nodes)
36-
37-
def visitList(self,node, **kw):
38-
return [self.visit(i) for i in node.nodes]
3910

40-
class SafeEvalWithErrors(SafeEval):
11+
_load_names = ['False', 'True', 'null', 'true', 'false']
4112

42-
def default(self, node, **kw):
43-
raise Unsafe_Source_Error("Unsupported source construct",
44-
node.__class__,node)
45-
46-
def visitName(self,node, **kw):
47-
if node.name == "None":
48-
return None
49-
elif node.name == "True":
50-
return True
51-
elif node.name == "False":
52-
return False
13+
_locals = {'null': None, 'true': True, 'false': False}
14+
15+
def _get_opcodes(codeobj):
16+
i = 0
17+
opcodes = []
18+
s = codeobj.co_code
19+
names = codeobj.co_names
20+
while i < len(s):
21+
code = ord(s[i])
22+
opcodes.append(code)
23+
if code >= dis.HAVE_ARGUMENT:
24+
i += 3
5325
else:
54-
raise Unsafe_Source_Error("Strings must be quoted",
55-
node.name, node)
56-
57-
# Add more specific errors if desired
58-
26+
i += 1
27+
return opcodes, names
5928

60-
def safe_eval(source, fail_on_error = True):
61-
walker = fail_on_error and SafeEvalWithErrors() or SafeEval()
62-
try:
63-
ast = compiler.parse(source,"eval")
64-
except SyntaxError, err:
65-
raise
29+
def test_expr(expr, allowed_codes):
6630
try:
67-
return walker.visit(ast)
68-
except Unsafe_Source_Error, err:
69-
raise
70-
## end of http://code.activestate.com/recipes/364469/ }}}
31+
c = compile(expr, "", "eval")
32+
except:
33+
raise ValueError, "%s is not a valid expression" % expr
34+
codes, names = _get_opcodes(c)
35+
for code in codes:
36+
if code not in allowed_codes:
37+
for n in names:
38+
if n not in _load_names:
39+
raise ValueError, "opcode %s not allowed" % dis.opname[code]
40+
return c
41+
42+
43+
def const_eval(expr):
44+
c = test_expr(expr, _const_codes)
45+
return eval(c, None, _locals)
46+
47+
## end of http://code.activestate.com/recipes/286134/ }}}

Diff for: module/lib/SecureXMLRPCServer.py

-120
This file was deleted.

Diff for: module/plugins/PluginManager.py

+1-5
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,7 @@
2626
from itertools import chain
2727
from traceback import print_exc
2828

29-
try:
30-
from ast import literal_eval
31-
except ImportError: # python 2.5
32-
from module.lib.SafeEval import safe_eval as literal_eval
33-
29+
from module.lib.SafeEval import const_eval as literal_eval
3430
from module.ConfigParser import IGNORE
3531

3632
class PluginManager():

Diff for: module/web/api_app.py

+1-5
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,7 @@
1313
from webinterface import PYLOAD
1414

1515
from module.common.json_layer import json
16-
17-
try:
18-
from ast import literal_eval
19-
except ImportError: # python 2.5
20-
from module.lib.SafeEval import safe_eval as literal_eval
16+
from module.lib.SafeEval import const_eval as literal_eval
2117

2218
# json encoder that accepts TBase objects
2319
class TBaseEncoder(json.JSONEncoder):

Diff for: pyLoadCore.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ def print_help(self):
147147
print ""
148148
print "<Options>"
149149
print " -v, --version", " " * 10, "Print version to terminal"
150-
print " -c, --clear", " " * 12, "Delete the saved linklist"
150+
print " -c, --clear", " " * 12, "Delete all saved packages/links"
151151
#print " -a, --add=<link/list>", " " * 2, "Add the specified links"
152152
print " -u, --user", " " * 13, "Manages users"
153153
print " -d, --debug", " " * 12, "Enable debug mode"

0 commit comments

Comments
 (0)