[FIX] base_import_module, tools: raise a clear error for unsupported file types
Also restrict XML data attribute evaluation context even for real module data files. This will prevent accidentally depending on context parameters that would not be available inside base_import_module.
This commit is contained in:
parent
ca18a1699c
commit
12634e1227
|
@ -41,6 +41,10 @@ class view(osv.osv):
|
||||||
|
|
||||||
for kind in ['data', 'init_xml', 'update_xml']:
|
for kind in ['data', 'init_xml', 'update_xml']:
|
||||||
for filename in terp[kind]:
|
for filename in terp[kind]:
|
||||||
|
ext = os.path.splitext(filename)[1].lower()
|
||||||
|
if ext not in ('.xml', '.csv', '.sql'):
|
||||||
|
_logger.info("module %s: skip unsupported file %s", module, filename)
|
||||||
|
continue
|
||||||
_logger.info("module %s: loading %s", module, filename)
|
_logger.info("module %s: loading %s", module, filename)
|
||||||
noupdate = False
|
noupdate = False
|
||||||
if filename.endswith('.csv') and kind in ('init', 'init_xml'):
|
if filename.endswith('.csv') and kind in ('init', 'init_xml'):
|
||||||
|
|
|
@ -61,11 +61,8 @@ from misc import pickle, unquote
|
||||||
|
|
||||||
from openerp import SUPERUSER_ID
|
from openerp import SUPERUSER_ID
|
||||||
|
|
||||||
# Import of XML records requires the unsafe eval as well,
|
from safe_eval import safe_eval as s_eval
|
||||||
# almost everywhere, which is ok because it supposedly comes
|
safe_eval = lambda expr, ctx={}: s_eval(expr, ctx, nocopy=True)
|
||||||
# from trusted data, but at least we make it obvious now.
|
|
||||||
unsafe_eval = eval
|
|
||||||
from safe_eval import safe_eval as eval
|
|
||||||
|
|
||||||
class ParseError(Exception):
|
class ParseError(Exception):
|
||||||
def __init__(self, msg, text, filename, lineno):
|
def __init__(self, msg, text, filename, lineno):
|
||||||
|
@ -130,7 +127,7 @@ def _eval_xml(self, node, pool, cr, uid, idref, context=None):
|
||||||
idref2 = {}
|
idref2 = {}
|
||||||
if f_search:
|
if f_search:
|
||||||
idref2 = _get_idref(self, cr, uid, f_model, context, idref)
|
idref2 = _get_idref(self, cr, uid, f_model, context, idref)
|
||||||
q = unsafe_eval(f_search, idref2)
|
q = safe_eval(f_search, idref2)
|
||||||
ids = pool[f_model].search(cr, uid, q)
|
ids = pool[f_model].search(cr, uid, q)
|
||||||
if f_use != 'id':
|
if f_use != 'id':
|
||||||
ids = map(lambda x: x[f_use], pool[f_model].read(cr, uid, ids, [f_use]))
|
ids = map(lambda x: x[f_use], pool[f_model].read(cr, uid, ids, [f_use]))
|
||||||
|
@ -147,7 +144,7 @@ def _eval_xml(self, node, pool, cr, uid, idref, context=None):
|
||||||
if a_eval:
|
if a_eval:
|
||||||
idref2 = _get_idref(self, cr, uid, f_model, context, idref)
|
idref2 = _get_idref(self, cr, uid, f_model, context, idref)
|
||||||
try:
|
try:
|
||||||
return unsafe_eval(a_eval, idref2)
|
return safe_eval(a_eval, idref2)
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.getLogger('openerp.tools.convert.init').error(
|
logging.getLogger('openerp.tools.convert.init').error(
|
||||||
'Could not eval(%s) for %s in %s', a_eval, node.get('name'), context)
|
'Could not eval(%s) for %s in %s', a_eval, node.get('name'), context)
|
||||||
|
@ -219,7 +216,7 @@ def _eval_xml(self, node, pool, cr, uid, idref, context=None):
|
||||||
# FIXME: should probably be exclusive
|
# FIXME: should probably be exclusive
|
||||||
if a_eval:
|
if a_eval:
|
||||||
idref['ref'] = lambda x: self.id_get(cr, x)
|
idref['ref'] = lambda x: self.id_get(cr, x)
|
||||||
args = unsafe_eval(a_eval, idref)
|
args = safe_eval(a_eval, idref)
|
||||||
for n in node:
|
for n in node:
|
||||||
return_val = _eval_xml(self,n, pool, cr, uid, idref, context)
|
return_val = _eval_xml(self,n, pool, cr, uid, idref, context)
|
||||||
if return_val is not None:
|
if return_val is not None:
|
||||||
|
@ -255,12 +252,12 @@ class xml_import(object):
|
||||||
for ctx in (data_node_context, node_context):
|
for ctx in (data_node_context, node_context):
|
||||||
if ctx:
|
if ctx:
|
||||||
try:
|
try:
|
||||||
ctx_res = unsafe_eval(ctx, eval_dict)
|
ctx_res = safe_eval(ctx, eval_dict)
|
||||||
if isinstance(context, dict):
|
if isinstance(context, dict):
|
||||||
context.update(ctx_res)
|
context.update(ctx_res)
|
||||||
else:
|
else:
|
||||||
context = ctx_res
|
context = ctx_res
|
||||||
except NameError:
|
except ValueError, NameError:
|
||||||
# Some contexts contain references that are only valid at runtime at
|
# Some contexts contain references that are only valid at runtime at
|
||||||
# client-side, so in that case we keep the original context string
|
# client-side, so in that case we keep the original context string
|
||||||
# as it is. We also log it, just in case.
|
# as it is. We also log it, just in case.
|
||||||
|
@ -299,7 +296,7 @@ form: module.record_id""" % (xml_id,)
|
||||||
if d_search:
|
if d_search:
|
||||||
idref = _get_idref(self, cr, self.uid, d_model, context={}, idref={})
|
idref = _get_idref(self, cr, self.uid, d_model, context={}, idref={})
|
||||||
try:
|
try:
|
||||||
ids = self.pool[d_model].search(cr, self.uid, unsafe_eval(d_search, idref))
|
ids = self.pool[d_model].search(cr, self.uid, safe_eval(d_search, idref))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
_logger.warning('Skipping deletion for failed search `%r`', d_search, exc_info=True)
|
_logger.warning('Skipping deletion for failed search `%r`', d_search, exc_info=True)
|
||||||
pass
|
pass
|
||||||
|
@ -332,14 +329,14 @@ form: module.record_id""" % (xml_id,)
|
||||||
if rec.get(field):
|
if rec.get(field):
|
||||||
res[dest] = rec.get(field).encode('utf8')
|
res[dest] = rec.get(field).encode('utf8')
|
||||||
if rec.get('auto'):
|
if rec.get('auto'):
|
||||||
res['auto'] = eval(rec.get('auto','False'))
|
res['auto'] = safe_eval(rec.get('auto','False'))
|
||||||
if rec.get('sxw'):
|
if rec.get('sxw'):
|
||||||
sxw_content = misc.file_open(rec.get('sxw')).read()
|
sxw_content = misc.file_open(rec.get('sxw')).read()
|
||||||
res['report_sxw_content'] = sxw_content
|
res['report_sxw_content'] = sxw_content
|
||||||
if rec.get('header'):
|
if rec.get('header'):
|
||||||
res['header'] = eval(rec.get('header','False'))
|
res['header'] = safe_eval(rec.get('header','False'))
|
||||||
|
|
||||||
res['multi'] = rec.get('multi') and eval(rec.get('multi','False'))
|
res['multi'] = rec.get('multi') and safe_eval(rec.get('multi','False'))
|
||||||
|
|
||||||
xml_id = rec.get('id','').encode('utf8')
|
xml_id = rec.get('id','').encode('utf8')
|
||||||
self._test_xml_id(xml_id)
|
self._test_xml_id(xml_id)
|
||||||
|
@ -359,12 +356,12 @@ form: module.record_id""" % (xml_id,)
|
||||||
id = self.pool['ir.model.data']._update(cr, self.uid, "ir.actions.report.xml", self.module, res, xml_id, noupdate=self.isnoupdate(data_node), mode=self.mode)
|
id = self.pool['ir.model.data']._update(cr, self.uid, "ir.actions.report.xml", self.module, res, xml_id, noupdate=self.isnoupdate(data_node), mode=self.mode)
|
||||||
self.idref[xml_id] = int(id)
|
self.idref[xml_id] = int(id)
|
||||||
|
|
||||||
if not rec.get('menu') or eval(rec.get('menu','False')):
|
if not rec.get('menu') or safe_eval(rec.get('menu','False')):
|
||||||
keyword = str(rec.get('keyword', 'client_print_multi'))
|
keyword = str(rec.get('keyword', 'client_print_multi'))
|
||||||
value = 'ir.actions.report.xml,'+str(id)
|
value = 'ir.actions.report.xml,'+str(id)
|
||||||
replace = rec.get('replace', True)
|
replace = rec.get('replace', True)
|
||||||
self.pool['ir.model.data'].ir_set(cr, self.uid, 'action', keyword, res['name'], [res['model']], value, replace=replace, isobject=True, xml_id=xml_id)
|
self.pool['ir.model.data'].ir_set(cr, self.uid, 'action', keyword, res['name'], [res['model']], value, replace=replace, isobject=True, xml_id=xml_id)
|
||||||
elif self.mode=='update' and eval(rec.get('menu','False'))==False:
|
elif self.mode=='update' and safe_eval(rec.get('menu','False'))==False:
|
||||||
# Special check for report having attribute menu=False on update
|
# Special check for report having attribute menu=False on update
|
||||||
value = 'ir.actions.report.xml,'+str(id)
|
value = 'ir.actions.report.xml,'+str(id)
|
||||||
self._remove_ir_values(cr, res['name'], value, res['model'])
|
self._remove_ir_values(cr, res['name'], value, res['model'])
|
||||||
|
@ -448,7 +445,7 @@ form: module.record_id""" % (xml_id,)
|
||||||
context = self.get_context(data_node, rec, eval_context)
|
context = self.get_context(data_node, rec, eval_context)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
domain = unsafe_eval(domain, eval_context)
|
domain = safe_eval(domain, eval_context)
|
||||||
except NameError:
|
except NameError:
|
||||||
# Some domains contain references that are only valid at runtime at
|
# Some domains contain references that are only valid at runtime at
|
||||||
# client-side, so in that case we keep the original domain string
|
# client-side, so in that case we keep the original domain string
|
||||||
|
@ -486,7 +483,7 @@ form: module.record_id""" % (xml_id,)
|
||||||
if rec.get('target'):
|
if rec.get('target'):
|
||||||
res['target'] = rec.get('target','')
|
res['target'] = rec.get('target','')
|
||||||
if rec.get('multi'):
|
if rec.get('multi'):
|
||||||
res['multi'] = eval(rec.get('multi', 'False'))
|
res['multi'] = safe_eval(rec.get('multi', 'False'))
|
||||||
id = self.pool['ir.model.data']._update(cr, self.uid, 'ir.actions.act_window', self.module, res, xml_id, noupdate=self.isnoupdate(data_node), mode=self.mode)
|
id = self.pool['ir.model.data']._update(cr, self.uid, 'ir.actions.act_window', self.module, res, xml_id, noupdate=self.isnoupdate(data_node), mode=self.mode)
|
||||||
self.idref[xml_id] = int(id)
|
self.idref[xml_id] = int(id)
|
||||||
|
|
||||||
|
@ -643,7 +640,7 @@ form: module.record_id""" % (xml_id,)
|
||||||
if rec_id:
|
if rec_id:
|
||||||
ids = [self.id_get(cr, rec_id)]
|
ids = [self.id_get(cr, rec_id)]
|
||||||
elif rec_src:
|
elif rec_src:
|
||||||
q = unsafe_eval(rec_src, eval_dict)
|
q = safe_eval(rec_src, eval_dict)
|
||||||
ids = self.pool[rec_model].search(cr, uid, q, context=context)
|
ids = self.pool[rec_model].search(cr, uid, q, context=context)
|
||||||
if rec_src_count:
|
if rec_src_count:
|
||||||
count = int(rec_src_count)
|
count = int(rec_src_count)
|
||||||
|
@ -674,7 +671,7 @@ form: module.record_id""" % (xml_id,)
|
||||||
for test in rec.findall('./test'):
|
for test in rec.findall('./test'):
|
||||||
f_expr = test.get("expr",'').encode('utf-8')
|
f_expr = test.get("expr",'').encode('utf-8')
|
||||||
expected_value = _eval_xml(self, test, self.pool, cr, uid, self.idref, context=context) or True
|
expected_value = _eval_xml(self, test, self.pool, cr, uid, self.idref, context=context) or True
|
||||||
expression_value = unsafe_eval(f_expr, globals_dict)
|
expression_value = safe_eval(f_expr, globals_dict)
|
||||||
if expression_value != expected_value: # assertion failed
|
if expression_value != expected_value: # assertion failed
|
||||||
self.assertion_report.record_failure()
|
self.assertion_report.record_failure()
|
||||||
msg = 'assertion "%s" failed!\n' \
|
msg = 'assertion "%s" failed!\n' \
|
||||||
|
@ -693,7 +690,7 @@ form: module.record_id""" % (xml_id,)
|
||||||
rec_id = rec.get("id",'').encode('ascii')
|
rec_id = rec.get("id",'').encode('ascii')
|
||||||
rec_context = rec.get("context", None)
|
rec_context = rec.get("context", None)
|
||||||
if rec_context:
|
if rec_context:
|
||||||
rec_context = unsafe_eval(rec_context)
|
rec_context = safe_eval(rec_context)
|
||||||
self._test_xml_id(rec_id)
|
self._test_xml_id(rec_id)
|
||||||
# in update mode, the record won't be updated if the data node explicitely
|
# in update mode, the record won't be updated if the data node explicitely
|
||||||
# opt-out using @noupdate="1". A second check will be performed in
|
# opt-out using @noupdate="1". A second check will be performed in
|
||||||
|
@ -732,7 +729,7 @@ form: module.record_id""" % (xml_id,)
|
||||||
f_val = False
|
f_val = False
|
||||||
|
|
||||||
if f_search:
|
if f_search:
|
||||||
q = unsafe_eval(f_search, self.idref)
|
q = safe_eval(f_search, self.idref)
|
||||||
assert f_model, 'Define an attribute model="..." in your .XML file !'
|
assert f_model, 'Define an attribute model="..." in your .XML file !'
|
||||||
f_obj = self.pool[f_model]
|
f_obj = self.pool[f_model]
|
||||||
# browse the objects searched
|
# browse the objects searched
|
||||||
|
|
Loading…
Reference in New Issue