@@ -28,6 +28,7 @@ import zipimport
|
||||
import osv
|
||||
import tools
|
||||
import tools.osutil
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
import pooler
|
||||
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@
|
||||
],
|
||||
'test': [
|
||||
'test/base_test.xml',
|
||||
#'test/base_test.yml'
|
||||
'test/test_context.xml',
|
||||
'bug_lp541545.xml',
|
||||
],
|
||||
|
||||
@@ -78,7 +78,8 @@
|
||||
<form string="Users">
|
||||
<notebook colspan="4">
|
||||
<page string="Current Activity">
|
||||
<field name="company_id" widget="selection" readonly="0" context="{'user_preference':True}"/>
|
||||
<field name="company_id" widget="selection" readonly="0"
|
||||
context="{'user_id': self, 'user_preference': 1}"/>
|
||||
<newline/>
|
||||
<separator colspan="4" string="Preferences"/>
|
||||
</page>
|
||||
@@ -110,7 +111,7 @@
|
||||
<page string="User">
|
||||
<field name="address_id" select="1"/>
|
||||
<field name="user_email" widget="email"/>
|
||||
<field name="company_id" required="1"/>
|
||||
<field name="company_id" required="1" context="{'user_id': self, 'user_preference': 1}"/>
|
||||
<field name="action_id" required="True"/>
|
||||
<field domain="[('usage','=','menu')]" name="menu_id" required="True"/>
|
||||
<field name="context_lang"/>
|
||||
|
||||
@@ -7,13 +7,13 @@ msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 5.0.4\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2009-12-18 08:39+0000\n"
|
||||
"PO-Revision-Date: 2010-06-02 04:11+0000\n"
|
||||
"PO-Revision-Date: 2010-06-03 03:58+0000\n"
|
||||
"Last-Translator: goranc <goranc@gmail.com>\n"
|
||||
"Language-Team: openerp-translators\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2010-06-03 03:33+0000\n"
|
||||
"X-Launchpad-Export-Date: 2010-06-04 03:44+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
"Language: hr\n"
|
||||
|
||||
|
||||
@@ -8,13 +8,13 @@ msgstr ""
|
||||
"Project-Id-Version: openobject-server\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2009-12-18 08:39+0000\n"
|
||||
"PO-Revision-Date: 2010-06-02 04:08+0000\n"
|
||||
"PO-Revision-Date: 2010-06-03 03:56+0000\n"
|
||||
"Last-Translator: Harry (Open ERP) <hmo@tinyerp.com>\n"
|
||||
"Language-Team: Japanese <ja@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2010-06-03 03:33+0000\n"
|
||||
"X-Launchpad-Export-Date: 2010-06-04 03:44+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#. module: base
|
||||
|
||||
@@ -7,13 +7,13 @@ msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 5.0.0\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2009-12-18 08:39+0000\n"
|
||||
"PO-Revision-Date: 2010-06-02 04:18+0000\n"
|
||||
"PO-Revision-Date: 2010-06-03 03:59+0000\n"
|
||||
"Last-Translator: Anders Wallenquist <anders.wallenquist@vertel.se>\n"
|
||||
"Language-Team: <>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2010-06-03 03:33+0000\n"
|
||||
"X-Launchpad-Export-Date: 2010-06-04 03:44+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#. module: base
|
||||
|
||||
@@ -7,13 +7,13 @@ msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 5.0.0\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2009-12-18 08:39+0000\n"
|
||||
"PO-Revision-Date: 2010-06-02 04:06+0000\n"
|
||||
"PO-Revision-Date: 2010-06-03 03:59+0000\n"
|
||||
"Last-Translator: Fabien (Open ERP) <fp@tinyerp.com>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2010-06-03 03:33+0000\n"
|
||||
"X-Launchpad-Export-Date: 2010-06-04 03:45+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#. module: base
|
||||
|
||||
@@ -7,13 +7,13 @@ msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 5.0.4\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2009-12-18 08:39+0000\n"
|
||||
"PO-Revision-Date: 2010-06-02 04:06+0000\n"
|
||||
"PO-Revision-Date: 2010-06-03 03:59+0000\n"
|
||||
"Last-Translator: OpenERP Administrators <Unknown>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2010-06-03 03:33+0000\n"
|
||||
"X-Launchpad-Export-Date: 2010-06-04 03:45+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#. module: base
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
##############################################################################
|
||||
|
||||
from osv import fields,osv
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
import tools
|
||||
import time
|
||||
from tools.config import config
|
||||
@@ -564,7 +565,7 @@ class actions_server(osv.osv):
|
||||
'object':obj,
|
||||
'obj': obj,
|
||||
}
|
||||
exec action.code in localdict
|
||||
eval(action.code, localdict, "exec")
|
||||
if 'action' in localdict:
|
||||
return localdict['action']
|
||||
else:
|
||||
|
||||
@@ -25,6 +25,7 @@ from datetime import datetime
|
||||
from dateutil.relativedelta import relativedelta
|
||||
import netsvc
|
||||
import tools
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
import pooler
|
||||
from osv import fields, osv
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ from osv.orm import browse_null
|
||||
import ir
|
||||
import report.custom
|
||||
from tools.translate import _
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
import netsvc
|
||||
|
||||
class report_custom(osv.osv):
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
from osv import fields,osv
|
||||
import time
|
||||
import tools
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
|
||||
class ir_rule(osv.osv):
|
||||
_name = 'ir.rule'
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
from osv import fields,osv
|
||||
from lxml import etree
|
||||
from tools import graph
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
import tools
|
||||
import netsvc
|
||||
import os
|
||||
|
||||
@@ -24,6 +24,7 @@ from osv import fields
|
||||
import os
|
||||
import tools
|
||||
from tools.translate import _
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
|
||||
class multi_company_default(osv.osv):
|
||||
"""
|
||||
@@ -91,11 +92,18 @@ class res_company(osv.osv):
|
||||
def search(self, cr, uid, args, offset=0, limit=None, order=None,
|
||||
context=None, count=False):
|
||||
|
||||
user_preference = context and context.get('user_preference', False) or False
|
||||
if context is None:
|
||||
context = {}
|
||||
user_preference = context.get('user_preference', False)
|
||||
if user_preference:
|
||||
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
|
||||
cmp_ids = list(set([user.company_id.id] + [cmp.id for cmp in user.company_ids]))
|
||||
# TODO: improve this as soon as the client sends the proper
|
||||
# combination of active_id and active_model we'll be able to
|
||||
# use active_id here to restrict to the user being modified instead
|
||||
# of current user.
|
||||
user_id = context.get('user_id', uid)
|
||||
|
||||
user = self.pool.get('res.users').browse(cr, uid, user_id, context=context)
|
||||
cmp_ids = list(set([user.company_id.id] + [cmp.id for cmp in user.company_ids]))
|
||||
return cmp_ids
|
||||
return super(res_company, self).search(cr, uid, args, offset=offset, limit=limit, order=order,
|
||||
context=context, count=count)
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
from osv import fields, osv
|
||||
from locale import localeconv
|
||||
import tools
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
|
||||
class lang(osv.osv):
|
||||
_name = "res.lang"
|
||||
|
||||
@@ -233,8 +233,13 @@ class users(osv.osv):
|
||||
'menu_id': fields.many2one('ir.actions.actions', 'Menu Action'),
|
||||
'groups_id': fields.many2many('res.groups', 'res_groups_users_rel', 'uid', 'gid', 'Groups'),
|
||||
'roles_id': fields.many2many('res.roles', 'res_roles_users_rel', 'uid', 'rid', 'Roles'),
|
||||
|
||||
# Special behavior for this field: res.company.search() will only return the companies
|
||||
# available to the current user (should be the user's companies?), when the user_preference
|
||||
# context is set.
|
||||
'company_id': fields.many2one('res.company', 'Company', required=True,
|
||||
help="The company this user is currently working for."),
|
||||
help="The company this user is currently working for.", context={'user_preference': True}),
|
||||
|
||||
'company_ids':fields.many2many('res.company','res_company_users_rel','user_id','cid','Companies'),
|
||||
'context_lang': fields.selection(_lang_get, 'Language', required=True,
|
||||
help="Sets the language for the user's user interface, when UI "
|
||||
@@ -277,7 +282,7 @@ class users(osv.osv):
|
||||
return all(((this.company_id in this.company_ids) or not this.company_ids) for this in self.browse(cr, uid, ids, context))
|
||||
|
||||
_constraints = [
|
||||
(_check_company, 'The chosen company is not in the allowed companies', ['company_id', 'company_ids']),
|
||||
(_check_company, 'The chosen company is not in the allowed companies for this user', ['company_id', 'company_ids']),
|
||||
]
|
||||
|
||||
_sql_constraints = [
|
||||
@@ -327,19 +332,18 @@ class users(osv.osv):
|
||||
'groups_id': _get_group,
|
||||
'address_id': False,
|
||||
}
|
||||
def company_get(self, cr, uid, uid2, context={}):
|
||||
def company_get(self, cr, uid, uid2, context=None):
|
||||
return self._get_company(cr, uid, context=context, uid2=uid2)
|
||||
company_get = tools.cache()(company_get)
|
||||
|
||||
def write(self, cr, uid, ids, values, *args, **argv):
|
||||
if (ids == [uid]):
|
||||
ok = True
|
||||
for k in values.keys():
|
||||
if k not in ('password','signature','action_id', 'context_lang', 'context_tz','company_id'):
|
||||
ok=False
|
||||
if ok:
|
||||
def write(self, cr, uid, ids, values, context=None):
|
||||
if ids == [uid]:
|
||||
for key in values.keys():
|
||||
if not (key in ('view', 'password','signature','action_id', 'company_id') or key.startswith('context_')):
|
||||
break
|
||||
else:
|
||||
uid = 1
|
||||
res = super(users, self).write(cr, uid, ids, values, *args, **argv)
|
||||
res = super(users, self).write(cr, uid, ids, values, context=context)
|
||||
self.company_get.clear_cache(cr.dbname)
|
||||
# Restart the cache on the company_get method
|
||||
self.pool.get('ir.model.access').call_cache_clearing_methods(cr)
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
- |
|
||||
Safe_Eval Scenario:
|
||||
In order to check that common dangerous operations are
|
||||
not allowed by the safe_eval mechanism, attempt to
|
||||
evaluate some bad expressions, and verify that it triggers
|
||||
an error.
|
||||
-
|
||||
1. Try a few common expressions to verify they work with safe_eval
|
||||
-
|
||||
!python {model: ir.model}: |
|
||||
from tools.safe_eval import safe_eval
|
||||
expected = (1, {"a": 9 * 2}, (True, False, None))
|
||||
actual = safe_eval('(1, {"a": 9 * 2}, (True, False, None))')
|
||||
assert actual == expected, "Simple python expressions are not working with safe_eval"
|
||||
-
|
||||
2. Try simple literal definition to verify it works with literal_eval
|
||||
-
|
||||
!python {model: ir.model}: |
|
||||
from tools.safe_eval import literal_eval
|
||||
expected = (1, {"a": 9}, (True, False, None))
|
||||
actual = literal_eval('(1, {"a": 9}, (True, False, None))')
|
||||
assert actual == expected, "Simple python expressions are not working with literal_eval"
|
||||
-
|
||||
3. Try arithmetic expression in literal_eval to verify it does not work
|
||||
-
|
||||
!python {model: ir.model}: |
|
||||
from tools.safe_eval import literal_eval
|
||||
try:
|
||||
literal_eval('(1, {"a": 2*9}, (True, False, None))')
|
||||
assert False, "literal_eval should not accept arithmetic expressions"
|
||||
except ValueError:
|
||||
pass
|
||||
-
|
||||
4. Try forbidden expressions in literal_eval to verify they are not allowed
|
||||
-
|
||||
!python {model: ir.model}: |
|
||||
from tools.safe_eval import literal_eval
|
||||
try:
|
||||
literal_eval('{"a": True.__class__}')
|
||||
assert False, "literal_eval should accept only literals"
|
||||
except ValueError:
|
||||
pass
|
||||
-
|
||||
5. Try forbidden expressions in safe_eval to verify they are not allowed (open)
|
||||
-
|
||||
!python {model: ir.model}: |
|
||||
from tools.safe_eval import safe_eval
|
||||
try:
|
||||
safe_eval('open("/etc/passwd","r")')
|
||||
assert False, "safe_eval should not allow arbitrary expressions"
|
||||
except NameError:
|
||||
pass
|
||||
except:
|
||||
# NameError should be raised because open() builtin is not found,
|
||||
# but other exceptions probably indicate that open() was executed!
|
||||
assert False, "safe_eval should not allow arbitrary expressions"
|
||||
|
||||
+15
-9
@@ -56,7 +56,7 @@ from tools.translate import _
|
||||
|
||||
import fields
|
||||
import tools
|
||||
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
|
||||
regex_order = re.compile('^(([a-z0-9_]+|"[a-z0-9_]+")( *desc| *asc)?( *, *|))+$', re.I)
|
||||
|
||||
@@ -353,8 +353,8 @@ def get_pg_type(f):
|
||||
f_type = ('int4', 'INTEGER')
|
||||
else:
|
||||
f_type = ('varchar', 'VARCHAR(%d)' % f_size)
|
||||
elif isinstance(f, fields.function) and eval('fields.'+(f._type)) in type_dict:
|
||||
t = eval('fields.'+(f._type))
|
||||
elif isinstance(f, fields.function) and eval('fields.'+(f._type),globals()) in type_dict:
|
||||
t = eval('fields.'+(f._type), globals())
|
||||
f_type = (type_dict[t], type_dict[t])
|
||||
elif isinstance(f, fields.function) and f._type == 'float':
|
||||
if f.digits:
|
||||
@@ -1228,17 +1228,23 @@ class orm_template(object):
|
||||
name = node.get('name')
|
||||
default = self.default_get(cr, user, [name], context=context).get(name)
|
||||
if default:
|
||||
attrs['selection'] = relation.name_get(cr, 1, default, context=context)
|
||||
attrs['selection'] = relation.name_get(cr, 1, [default], context=context)
|
||||
else:
|
||||
attrs['selection'] = []
|
||||
# We can not use the 'string' domain has it is defined according to the record !
|
||||
else:
|
||||
# If domain and context are strings, we keep them for client-side, otherwise
|
||||
# we evaluate them server-side to consider them when generating the list of
|
||||
# possible values
|
||||
# TODO: find a way to remove this hack, by allow dynamic domains
|
||||
dom = []
|
||||
if column._domain and not isinstance(column._domain, (str, unicode)):
|
||||
if column._domain and not isinstance(column._domain, basestring):
|
||||
dom = column._domain
|
||||
dom += eval(node.get('domain','[]'), {'uid':user, 'time':time})
|
||||
context.update(eval(node.get('context','{}')))
|
||||
attrs['selection'] = relation._name_search(cr, user, '', dom, context=context, limit=None, name_get_uid=1)
|
||||
search_context = dict(context)
|
||||
if column._context and not isinstance(column._context, basestring):
|
||||
search_context.update(column._context)
|
||||
attrs['selection'] = relation._name_search(cr, user, '', dom, context=search_context, limit=None, name_get_uid=1)
|
||||
if (node.get('required') and not int(node.get('required'))) or not column.required:
|
||||
attrs['selection'].append((False,''))
|
||||
fields[node.get('name')] = attrs
|
||||
@@ -1951,9 +1957,9 @@ class orm_memory(orm_template):
|
||||
f = True
|
||||
for arg in result:
|
||||
if arg[1] =='=':
|
||||
val =eval('data[arg[0]]'+'==' +' arg[2]')
|
||||
val =eval('data[arg[0]]'+'==' +' arg[2]', locals())
|
||||
elif arg[1] in ['<','>','in','not in','<=','>=','<>']:
|
||||
val =eval('data[arg[0]]'+arg[1] +' arg[2]')
|
||||
val =eval('data[arg[0]]'+arg[1] +' arg[2]', locals())
|
||||
elif arg[1] in ['ilike']:
|
||||
if str(data[arg[0]]).find(str(arg[2]))!=-1:
|
||||
val= True
|
||||
|
||||
@@ -24,6 +24,7 @@ import time
|
||||
import netsvc
|
||||
|
||||
import tools
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
import print_xml
|
||||
import render
|
||||
from interface import report_int
|
||||
|
||||
@@ -23,6 +23,7 @@ import os,types
|
||||
from lxml import etree
|
||||
import netsvc
|
||||
import tools
|
||||
from tools.safe_eval import safe_eval
|
||||
import print_fnc
|
||||
import copy
|
||||
from osv.orm import browse_null, browse_record
|
||||
@@ -111,7 +112,7 @@ class document(object):
|
||||
#Pinky: Why not this ? eval(expr, browser) ?
|
||||
# name = browser.name
|
||||
# data_dict = browser._data[self.get_value(browser, 'id')]
|
||||
return eval(expr, {}, {'obj': record})
|
||||
return safe_eval(expr, {}, {'obj': record})
|
||||
|
||||
def parse_node(self, node, parent, browser, datas=None):
|
||||
attrs = self.node_attrs_get(node)
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
from report.interface import report_int
|
||||
import pooler
|
||||
import tools
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
from lxml import etree
|
||||
from report import render
|
||||
import locale
|
||||
|
||||
@@ -34,6 +34,7 @@ import os
|
||||
from lxml import etree
|
||||
import base64
|
||||
from reportlab.platypus.doctemplate import ActionFlowable
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
|
||||
encoding = 'utf-8'
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ from lxml import etree
|
||||
import copy
|
||||
import locale
|
||||
import traceback, sys
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
|
||||
_regex = re.compile('\[\[(.+?)\]\]')
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import re
|
||||
import reportlab
|
||||
import reportlab.lib.units
|
||||
from lxml import etree
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
|
||||
_regex = re.compile('\[\[(.+?)\]\]')
|
||||
|
||||
@@ -128,4 +129,4 @@ def attr_get(node, attrs, dict={}):
|
||||
res[key] = unit_get(node.get(key))
|
||||
return res
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
+25
-18
@@ -37,6 +37,12 @@ import pooler
|
||||
from config import config
|
||||
from yaml_import import convert_yaml_import
|
||||
|
||||
# Import of XML records requires the unsafe eval as well,
|
||||
# almost everywhere, which is ok because it supposedly comes
|
||||
# from trusted data, but at least we make it obvious now.
|
||||
unsafe_eval = eval
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
|
||||
class ConvertError(Exception):
|
||||
def __init__(self, doc, orig_excpt):
|
||||
self.d = doc
|
||||
@@ -62,7 +68,7 @@ def _eval_xml(self,node, pool, cr, uid, idref, context=None):
|
||||
f_search = node.get("search",'').encode('utf-8')
|
||||
f_use = node.get("use",'id').encode('ascii')
|
||||
f_name = node.get("name",'').encode('utf-8')
|
||||
q = eval(f_search, idref)
|
||||
q = unsafe_eval(f_search, idref)
|
||||
ids = pool.get(f_model).search(cr, uid, q)
|
||||
if f_use != 'id':
|
||||
ids = map(lambda x: x[f_use], pool.get(f_model).read(cr, uid, ids, [f_use]))
|
||||
@@ -96,10 +102,10 @@ def _eval_xml(self,node, pool, cr, uid, idref, context=None):
|
||||
pytz=pytzclass()
|
||||
idref2['pytz'] = pytz
|
||||
try:
|
||||
return eval(a_eval, idref2)
|
||||
except:
|
||||
logger = netsvc.Logger()
|
||||
logger.notifyChannel("init", netsvc.LOG_WARNING, 'could eval(%s) for %s in %s, please get back and fix it!' % (a_eval,node.get('name'),context))
|
||||
return unsafe_eval(a_eval, idref2)
|
||||
except Exception, e:
|
||||
logger = logging.getLogger('init')
|
||||
logger.warning('couldn\'t eval(%s) for %s in %s, please get back and fix it!' % (a_eval,node.get('name'),context), exc_info=True)
|
||||
return ""
|
||||
if t == 'xml':
|
||||
def _process(s, idref):
|
||||
@@ -144,7 +150,7 @@ def _eval_xml(self,node, pool, cr, uid, idref, context=None):
|
||||
a_eval = node.get('eval','')
|
||||
if a_eval:
|
||||
idref['ref'] = lambda x: self.id_get(cr, False, x)
|
||||
args = eval(a_eval, idref)
|
||||
args = unsafe_eval(a_eval, idref)
|
||||
for n in node:
|
||||
return_val = _eval_xml(self,n, pool, cr, uid, idref, context)
|
||||
if return_val is not None:
|
||||
@@ -205,13 +211,13 @@ class xml_import(object):
|
||||
def get_context(self, data_node, node, eval_dict):
|
||||
data_node_context = (len(data_node) and data_node.get('context','').encode('utf8'))
|
||||
if data_node_context:
|
||||
context = eval(data_node_context, eval_dict)
|
||||
context = unsafe_eval(data_node_context, eval_dict)
|
||||
else:
|
||||
context = {}
|
||||
|
||||
node_context = node.get("context",'').encode('utf8')
|
||||
if node_context:
|
||||
context.update(eval(node_context, eval_dict))
|
||||
context.update(unsafe_eval(node_context, eval_dict))
|
||||
|
||||
return context
|
||||
|
||||
@@ -241,7 +247,7 @@ form: module.record_id""" % (xml_id,)
|
||||
d_id = rec.get("id",'')
|
||||
ids = []
|
||||
if d_search:
|
||||
ids = self.pool.get(d_model).search(cr,self.uid,eval(d_search))
|
||||
ids = self.pool.get(d_model).search(cr, self.uid, unsafe_eval(d_search))
|
||||
if d_id:
|
||||
try:
|
||||
ids.append(self.id_get(cr, d_model, d_id))
|
||||
@@ -403,7 +409,8 @@ form: module.record_id""" % (xml_id,)
|
||||
|
||||
def ref(str_id):
|
||||
return self.id_get(cr, None, str_id)
|
||||
context = eval(context)
|
||||
|
||||
context = unsafe_eval(context)
|
||||
# domain = eval(domain) # XXX need to test this line -> uid, active_id, active_ids, ...
|
||||
|
||||
res = {
|
||||
@@ -623,7 +630,7 @@ form: module.record_id""" % (xml_id,)
|
||||
if rec_id:
|
||||
ids = [self.id_get(cr, rec_model, rec_id)]
|
||||
elif rec_src:
|
||||
q = eval(rec_src, eval_dict)
|
||||
q = unsafe_eval(rec_src, eval_dict)
|
||||
ids = self.pool.get(rec_model).search(cr, uid, q, context=context)
|
||||
if rec_src_count:
|
||||
count = int(rec_src_count)
|
||||
@@ -651,14 +658,14 @@ form: module.record_id""" % (xml_id,)
|
||||
if key in brrec:
|
||||
return brrec[key]
|
||||
return dict.__getitem__(self2, key)
|
||||
globals = d()
|
||||
globals['floatEqual'] = self._assert_equals
|
||||
globals['ref'] = ref
|
||||
globals['_ref'] = ref
|
||||
globals_dict = d()
|
||||
globals_dict['floatEqual'] = self._assert_equals
|
||||
globals_dict['ref'] = ref
|
||||
globals_dict['_ref'] = ref
|
||||
for test in rec.findall('./test'):
|
||||
f_expr = test.get("expr",'').encode('utf-8')
|
||||
expected_value = _eval_xml(self, test, self.pool, cr, uid, self.idref, context=context) or True
|
||||
expression_value = eval(f_expr, globals)
|
||||
expression_value = unsafe_eval(f_expr, globals_dict)
|
||||
if expression_value != expected_value: # assertion failed
|
||||
self.assert_report.record_assertion(False, severity)
|
||||
msg = 'assertion "%s" failed!\n' \
|
||||
@@ -682,7 +689,7 @@ form: module.record_id""" % (xml_id,)
|
||||
rec_id = rec.get("id",'').encode('ascii')
|
||||
rec_context = rec.get("context", None)
|
||||
if rec_context:
|
||||
rec_context = eval(rec_context)
|
||||
rec_context = unsafe_eval(rec_context)
|
||||
self._test_xml_id(rec_id)
|
||||
if self.isnoupdate(data_node) and self.mode != 'init':
|
||||
# check if the xml record has an id string
|
||||
@@ -722,7 +729,7 @@ form: module.record_id""" % (xml_id,)
|
||||
f_val = False
|
||||
|
||||
if f_search:
|
||||
q = eval(f_search, self.idref)
|
||||
q = unsafe_eval(f_search, self.idref)
|
||||
field = []
|
||||
assert f_model, 'Define an attribute model="..." in your .XML file !'
|
||||
f_obj = self.pool.get(f_model)
|
||||
|
||||
+237
-49
@@ -1,66 +1,254 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
# Copyright (C) 2004-2010 OpenERP s.a. (<http://www.openerp.com>).
|
||||
#
|
||||
# Copyright P. Christeas <p_christ@hol.gr> 2008,2009
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# WARNING: This program as such is intended to be used by professional
|
||||
# programmers who take the whole responsability of assessing all potential
|
||||
# consequences resulting from its eventual inadequacies and bugs
|
||||
# End users who are looking for a ready-to-use solution with commercial
|
||||
# garantees and support are strongly adviced to contract a Free Software
|
||||
# Service Company
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# This program is Free Software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
###############################################################################
|
||||
##############################################################################
|
||||
|
||||
__export_bis = {}
|
||||
import sys
|
||||
"""
|
||||
safe_eval module - methods intended to provide more restricted alternatives to
|
||||
evaluate simple and/or untrusted code.
|
||||
|
||||
def __init_ebis():
|
||||
global __export_bis
|
||||
Methods in this module are typically used as alternatives to eval() to parse
|
||||
OpenERP domain strings, conditions and expressions, mostly based on locals
|
||||
condition/math builtins.
|
||||
"""
|
||||
|
||||
_evars = [ 'abs', 'all', 'any', 'basestring' , 'bool',
|
||||
'chr', 'cmp','complex', 'dict', 'divmod', 'enumerate',
|
||||
'float', 'frozenset', 'getattr', 'hasattr', 'hash',
|
||||
'hex', 'id','int', 'iter', 'len', 'list', 'long', 'map', 'max',
|
||||
'min', 'oct', 'ord','pow', 'range', 'reduce', 'repr',
|
||||
'reversed', 'round', 'set', 'setattr', 'slice','sorted', 'str',
|
||||
'sum', 'tuple','type', 'unichr','unicode', 'xrange',
|
||||
'True','False', 'None', 'NotImplemented', 'Ellipsis', ]
|
||||
# Module partially ripped from/inspired by several different sources:
|
||||
# - http://code.activestate.com/recipes/286134/
|
||||
# - safe_eval in lp:~xrg/openobject-server/optimize-5.0
|
||||
# - safe_eval in tryton http://hg.tryton.org/hgwebdir.cgi/trytond/rev/bbb5f73319ad
|
||||
# - python 2.6's ast.literal_eval
|
||||
|
||||
if sys.version_info[0:2] >= (2,6):
|
||||
_evars.extend(['bin', 'format', 'next'])
|
||||
for v in _evars:
|
||||
__export_bis[v] = __builtins__[v]
|
||||
from opcode import HAVE_ARGUMENT, opmap, opname
|
||||
from types import CodeType
|
||||
|
||||
__all__ = ['test_expr', 'literal_eval', 'safe_eval', 'const_eval', 'ext_eval' ]
|
||||
|
||||
_CONST_OPCODES = set(opmap[x] for x in [
|
||||
'POP_TOP', 'ROT_TWO', 'ROT_THREE', 'ROT_FOUR', 'DUP_TOP',
|
||||
'BUILD_LIST', 'BUILD_MAP', 'BUILD_TUPLE',
|
||||
'LOAD_CONST', 'RETURN_VALUE', 'STORE_SUBSCR'] if x in opmap)
|
||||
|
||||
_EXPR_OPCODES = _CONST_OPCODES.union(set(opmap[x] for x in [
|
||||
'UNARY_POSITIVE', 'UNARY_NEGATIVE', 'UNARY_NOT',
|
||||
'UNARY_INVERT', 'BINARY_POWER', 'BINARY_MULTIPLY',
|
||||
'BINARY_DIVIDE', 'BINARY_FLOOR_DIVIDE', 'BINARY_TRUE_DIVIDE',
|
||||
'BINARY_MODULO', 'BINARY_ADD', 'BINARY_SUBTRACT', 'BINARY_SUBSCR',
|
||||
'BINARY_LSHIFT', 'BINARY_RSHIFT', 'BINARY_AND', 'BINARY_XOR',
|
||||
'BINARY_OR'] 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',
|
||||
] if x in opmap))
|
||||
|
||||
|
||||
__init_ebis()
|
||||
def _get_opcodes(codeobj):
|
||||
"""_get_opcodes(codeobj) -> [opcodes]
|
||||
|
||||
Extract the actual opcodes as a list from a code object
|
||||
|
||||
>>> c = compile("[1 + 2, (1,2)]", "", "eval")
|
||||
>>> _get_opcodes(c)
|
||||
[100, 100, 23, 100, 100, 102, 103, 83]
|
||||
"""
|
||||
i = 0
|
||||
opcodes = []
|
||||
byte_codes = codeobj.co_code
|
||||
while i < len(byte_codes):
|
||||
code = ord(byte_codes[i])
|
||||
opcodes.append(code)
|
||||
if code >= HAVE_ARGUMENT:
|
||||
i += 3
|
||||
else:
|
||||
i += 1
|
||||
return opcodes
|
||||
|
||||
def test_expr(expr, allowed_codes, mode="eval"):
|
||||
"""test_expr(expression, allowed_codes[, mode]) -> code_object
|
||||
|
||||
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.
|
||||
"""
|
||||
try:
|
||||
code_obj = compile(expr, "", mode)
|
||||
except:
|
||||
raise ValueError("%s is not a valid expression" % expr)
|
||||
for code in _get_opcodes(code_obj):
|
||||
if code not in allowed_codes:
|
||||
raise ValueError("opcode %s not allowed (%r)" % (opname[code], expr))
|
||||
return code_obj
|
||||
|
||||
|
||||
def safe_eval(expr,sglobals,slocals = None):
|
||||
""" A little safer version of eval().
|
||||
This one, will use fewer builtin functions, so that only
|
||||
arithmetic and logic expressions can really work """
|
||||
def const_eval(expr):
|
||||
"""const_eval(expression) -> value
|
||||
|
||||
global __export_bis
|
||||
Safe Python constant evaluation
|
||||
|
||||
if not sglobals.has_key('__builtins__'):
|
||||
# we copy, because we wouldn't want successive calls to safe_eval
|
||||
# to be able to alter the builtins.
|
||||
sglobals['__builtins__'] = __export_bis.copy()
|
||||
Evaluates a string that contains an expression describing
|
||||
a Python constant. Strings that are not valid Python expressions
|
||||
or that contain other code besides the constant raise ValueError.
|
||||
|
||||
return eval(expr,sglobals,slocals)
|
||||
>>> const_eval("10")
|
||||
10
|
||||
>>> const_eval("[1,2, (3,4), {'foo':'bar'}]")
|
||||
[1, 2, (3, 4), {'foo': 'bar'}]
|
||||
>>> const_eval("1+2")
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: opcode BINARY_ADD not allowed
|
||||
"""
|
||||
c = test_expr(expr, _CONST_OPCODES)
|
||||
return eval(c)
|
||||
|
||||
#eof
|
||||
def expr_eval(expr):
|
||||
"""expr_eval(expression) -> value
|
||||
|
||||
Restricted Python expression evaluation
|
||||
|
||||
Evaluates a string that contains an expression that only
|
||||
uses Python constants. This can be used to e.g. evaluate
|
||||
a numerical expression from an untrusted source.
|
||||
|
||||
>>> expr_eval("1+2")
|
||||
3
|
||||
>>> expr_eval("[1,2]*2")
|
||||
[1, 2, 1, 2]
|
||||
>>> expr_eval("__import__('sys').modules")
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: opcode LOAD_NAME not allowed
|
||||
"""
|
||||
c = test_expr(expr, _EXPR_OPCODES)
|
||||
return eval(c)
|
||||
|
||||
|
||||
# Port of Python 2.6's ast.literal_eval for use under Python 2.5
|
||||
SAFE_CONSTANTS = {'None': None, 'True': True, 'False': False}
|
||||
|
||||
try:
|
||||
# first, try importing directly
|
||||
from ast import literal_eval
|
||||
except ImportError:
|
||||
import _ast as ast
|
||||
|
||||
def _convert(node):
|
||||
if isinstance(node, ast.Str):
|
||||
return node.s
|
||||
elif isinstance(node, ast.Num):
|
||||
return node.n
|
||||
elif isinstance(node, ast.Tuple):
|
||||
return tuple(map(_convert, node.elts))
|
||||
elif isinstance(node, ast.List):
|
||||
return list(map(_convert, node.elts))
|
||||
elif isinstance(node, ast.Dict):
|
||||
return dict((_convert(k), _convert(v)) for k, v
|
||||
in zip(node.keys, node.values))
|
||||
elif isinstance(node, ast.Name):
|
||||
if node.id in SAFE_CONSTANTS:
|
||||
return SAFE_CONSTANTS[node.id]
|
||||
raise ValueError('malformed or disallowed expression')
|
||||
|
||||
def parse(expr, filename='<unknown>', mode='eval'):
|
||||
"""parse(source[, filename], mode]] -> code object
|
||||
Parse an expression into an AST node.
|
||||
Equivalent to compile(expr, filename, mode, PyCF_ONLY_AST).
|
||||
"""
|
||||
return compile(expr, filename, mode, ast.PyCF_ONLY_AST)
|
||||
|
||||
def literal_eval(node_or_string):
|
||||
"""literal_eval(expression) -> value
|
||||
Safely evaluate an expression node or a string containing a Python
|
||||
expression. The string or node provided may only consist of the
|
||||
following Python literal structures: strings, numbers, tuples,
|
||||
lists, dicts, booleans, and None.
|
||||
|
||||
>>> literal_eval('[1,True,"spam"]')
|
||||
[1, True, 'spam']
|
||||
|
||||
>>> literal_eval('1+3')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: malformed or disallowed expression
|
||||
"""
|
||||
if isinstance(node_or_string, basestring):
|
||||
node_or_string = parse(node_or_string)
|
||||
if isinstance(node_or_string, ast.Expression):
|
||||
node_or_string = node_or_string.body
|
||||
return _convert(node_or_string)
|
||||
|
||||
|
||||
|
||||
def safe_eval(expr, globals_dict=None, locals_dict=None, mode="eval", nocopy=False):
|
||||
"""safe_eval(expression[, globals[, locals[, mode[, nocopy]]]]) -> result
|
||||
|
||||
System-restricted Python expression evaluation
|
||||
|
||||
Evaluates a string that contains an expression that mostly
|
||||
uses Python constants, arithmetic expressions and the
|
||||
objects directly provided in context.
|
||||
|
||||
This can be used to e.g. evaluate
|
||||
an OpenERP domain expression from an untrusted source.
|
||||
|
||||
>>> safe_eval("__import__('sys').modules")
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: opcode LOAD_NAME not allowed
|
||||
|
||||
"""
|
||||
if isinstance(expr, CodeType):
|
||||
raise ValueError("safe_eval does not allow direct evaluation of code objects.")
|
||||
|
||||
if '__subclasses__' in expr:
|
||||
raise ValueError('expression not allowed (__subclasses__)')
|
||||
|
||||
if globals_dict is None:
|
||||
globals_dict = {}
|
||||
|
||||
# prevent altering the globals/locals from within the sandbox
|
||||
# by taking a copy.
|
||||
if not nocopy:
|
||||
# isinstance() does not work below, we want *exactly* the dict class
|
||||
if (globals_dict is not None and type(globals_dict) is not dict) \
|
||||
or (locals_dict is not None and type(locals_dict) is not dict):
|
||||
logging.getLogger('safe_eval').warning('Looks like you are trying to pass a dynamic environment,"\
|
||||
"you should probably pass nocopy=True to safe_eval()')
|
||||
|
||||
globals_dict = dict(globals_dict)
|
||||
if locals_dict is not None:
|
||||
locals_dict = dict(locals_dict)
|
||||
|
||||
globals_dict.update(
|
||||
__builtins__ = {
|
||||
'True': True,
|
||||
'False': False,
|
||||
'None': None,
|
||||
'str': str,
|
||||
'globals': locals,
|
||||
'locals': locals,
|
||||
'bool': bool,
|
||||
'dict': dict,
|
||||
'list': list,
|
||||
'tuple': tuple,
|
||||
}
|
||||
)
|
||||
|
||||
return eval(test_expr(expr,_SAFE_OPCODES, mode=mode), globals_dict, locals_dict)
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -8,9 +8,13 @@ import netsvc
|
||||
import misc
|
||||
from config import config
|
||||
import yaml_tag
|
||||
|
||||
import yaml
|
||||
|
||||
# YAML import needs both safe and unsafe eval, but let's
|
||||
# default to /safe/.
|
||||
unsafe_eval = eval
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
|
||||
logger_channel = 'tests'
|
||||
|
||||
class YamlImportException(Exception):
|
||||
@@ -234,8 +238,9 @@ class YamlInterpreter(object):
|
||||
record = model.browse(self.cr, self.uid, id, context)
|
||||
for test in expressions:
|
||||
try:
|
||||
success = eval(test, self.eval_context, RecordDictWrapper(record))
|
||||
success = unsafe_eval(test, self.eval_context, RecordDictWrapper(record))
|
||||
except Exception, e:
|
||||
self.logger.debug('Exception during evaluation of !assert block in yaml_file %s.', self.filename, exc_info=True)
|
||||
raise YamlImportAbortion(e)
|
||||
if not success:
|
||||
msg = 'Assertion "%s" FAILED\ntest: %s\n'
|
||||
@@ -367,12 +372,13 @@ class YamlInterpreter(object):
|
||||
code_context = {'model': model, 'cr': self.cr, 'uid': self.uid, 'log': log, 'context': self.context}
|
||||
code_context.update({'self': model}) # remove me when no !python block test uses 'self' anymore
|
||||
try:
|
||||
code = compile(statements, self.filename, 'exec')
|
||||
eval(code, {'ref': self.get_id}, code_context)
|
||||
code_obj = compile(statements, self.filename, 'exec')
|
||||
unsafe_eval(code_obj, {'ref': self.get_id}, code_context)
|
||||
except AssertionError, e:
|
||||
self._log_assert_failure(python.severity, 'AssertionError in Python code %s: %s', python.name, e)
|
||||
return
|
||||
except Exception, e:
|
||||
self.logger.debug('Exception during evaluation of !python block in yaml_file %s.', self.filename, exc_info=True)
|
||||
raise YamlImportAbortion(e)
|
||||
else:
|
||||
self.assert_report.record(True, python.severity)
|
||||
|
||||
@@ -23,6 +23,7 @@ import sys
|
||||
import netsvc
|
||||
import osv as base
|
||||
import pooler
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
|
||||
class Env(dict):
|
||||
def __init__(self, cr, uid, model, ids):
|
||||
@@ -54,7 +55,7 @@ def _eval_expr(cr, ident, workitem, action):
|
||||
ret=False
|
||||
else:
|
||||
env = Env(cr, uid, model, ids)
|
||||
ret = eval(line, env)
|
||||
ret = eval(line, env, nocopy=True)
|
||||
return ret
|
||||
|
||||
def execute_action(cr, ident, workitem, activity):
|
||||
|
||||
Reference in New Issue
Block a user