[FIX] base: avoid variable name collision with functions in QWeb

Bug
===
The arguments of the lambda function might be the same as other
variables in the QWeb. In that case the template rendering will crash.

To avoid this issue, we encapsulate the name of the argument to be sure
it's not the same as other variable in the QWeb engine (so the name of
the argument can no longer make the QWeb rendering crash)

Task-2674716

Closes #78392.

Forward-port of 96b09b127028e5c9a83c89acce86e1b6df904c6f
This commit is contained in:
std-odoo
2021-12-01 17:30:51 +01:00
parent 36abba30f6
commit 3b97f601be
3 changed files with 24 additions and 9 deletions
+19 -7
View File
@@ -295,7 +295,6 @@ class QWeb(object):
globals_dict['Mapping'] = Mapping
globals_dict['Markup'] = Markup
globals_dict['escape'] = escape
globals_dict['type'] = type
globals_dict['compile_options'] = options
globals_dict.update(self._available_objects)
return globals_dict
@@ -410,7 +409,7 @@ class QWeb(object):
code += f'.format({", ".join(values)})'
return code
def _compile_expr_tokens(self, tokens, allowed_keys, raise_on_missing=False):
def _compile_expr_tokens(self, tokens, allowed_keys, argument_names=None, raise_on_missing=False):
""" Transform the list of token coming into a python instruction in
textual form by adding the namepaces for the dynamic values.
@@ -436,6 +435,10 @@ class QWeb(object):
index = 0
open_bracket_index = -1
bracket_depth = 0
argument_name = '_arg_%s__'
argument_names = argument_names or []
while index < len(tokens):
t = tokens[index]
if t.exact_type in [token.LPAR, token.LSQB, token.LBRACE]:
@@ -449,7 +452,7 @@ class QWeb(object):
while i < len(tokens):
t = tokens[i]
if t.exact_type == token.NAME:
allowed_keys.append(t.string)
argument_names.append(t.string)
elif t.exact_type == token.COMMA:
pass
elif t.exact_type == token.COLON:
@@ -466,7 +469,7 @@ class QWeb(object):
if t.exact_type == token.NAME:
if t.string == 'in':
break
allowed_keys.append(t.string)
argument_names.append(t.string)
elif t.exact_type in [token.COMMA, token.LPAR, token.RPAR]:
pass
else:
@@ -495,7 +498,12 @@ class QWeb(object):
elif t.exact_type in [token.RPAR, token.RSQB, token.RBRACE]:
bracket_depth -= 1
if bracket_depth == 0:
code = self._compile_expr_tokens(tokens[open_bracket_index + 1:index], list(allowed_keys), raise_on_missing)
code = self._compile_expr_tokens(
tokens[open_bracket_index + 1:index],
list(allowed_keys),
list(argument_names),
raise_on_missing,
)
code = tokens[open_bracket_index].string + code + t.string
tokens[open_bracket_index:index + 1] = [tokenize.TokenInfo(token.QWEB, code, tokens[open_bracket_index].start, t.end, '')]
index = open_bracket_index
@@ -526,7 +534,9 @@ class QWeb(object):
index += 1
while index < len(tokens):
t = tokens[index]
if t.exact_type in [token.COMMA, token.NAME, token.COLON]:
if t.exact_type == token.NAME and t.string in argument_names:
code.append(argument_name % t.string)
if t.exact_type in [token.COMMA, token.COLON]:
code.append(t.string)
if t.exact_type == token.COLON:
break
@@ -535,6 +545,8 @@ class QWeb(object):
pos = (t.end[0], 0)
else:
pos = t.end
elif string in argument_names:
code.append(argument_name % t.string)
elif string in allowed_keys:
code.append(string)
elif index + 1 < len(tokens) and tokens[index + 1].exact_type == token.EQUAL: # function kw
@@ -1048,7 +1060,7 @@ class QWeb(object):
{t_foreach} = {self._compile_expr(expr_foreach)} or []
if isinstance({t_foreach}, Sized):
values[{repr(expr_as + '_size')}] = {size} = len({t_foreach})
elif type({t_foreach}) == int:
elif ({t_foreach}).__class__ == int:
values[{repr(expr_as + '_size')}] = {size} = {t_foreach}
{t_foreach} = range({size})
else:
+4 -1
View File
@@ -684,6 +684,7 @@ class TestQWebBasic(TransactionCase):
("fn(a=11, b=22) or a", {'a': 1, 'fn': lambda a,b: b}, 22),
("(lambda a: a)(5)", {}, 5),
("(lambda a: a[0])([5])", {}, 5),
("(lambda test: len(test))('aaa')", {}, 3),
("{'a': lambda a: a[0], 'b': 3}['a']([5])", {}, 5),
("list(map(lambda a: a[0], r))", {'r': [(1,11), (2,22)]}, [1, 2]),
("z + (head or 'z')", {'z': 'a'}, "az"),
@@ -693,8 +694,10 @@ class TestQWebBasic(TransactionCase):
("any({x == 5 for x in [1,2,3]})", {}, False),
("{x:y for x,y in [('a', 11),('b', 22)]}", {}, {'a': 11, 'b': 22}),
("[(y,x) for x,y in [(1, 11),(2, 22)]]", {}, [(11, 1), (22, 2)]),
("(lambda a: a + 5)(a=x)", {'x': 10}, 15),
("(lambda a: a + 5)(x)", {'x': 10}, 15),
("(lambda a: a + x)(5)", {'x': 10}, 15),
("sum(x for x in range(4)) + ((x))", {'x': 10}, 16),
("['test_' + x for x in ['a', 'b']]", {}, ['test_a', 'test_b'])
]
IrQweb = self.env['ir.qweb']
+1 -1
View File
@@ -37,7 +37,7 @@ __all__ = ['test_expr', 'safe_eval', 'const_eval']
# lp:703841), does import time.
_ALLOWED_MODULES = ['_strptime', 'math', 'time']
_UNSAFE_ATTRIBUTES = ['f_builtins', 'f_globals', 'f_locals', 'gi_frame',
_UNSAFE_ATTRIBUTES = ['f_builtins', 'f_globals', 'f_locals', 'gi_frame', 'gi_code',
'co_code', 'func_globals']
def to_opcodes(opnames, _opmap=opmap):