diff -r 23a125545c3d -r 2f3317d53d51 mercurial/revset.py --- a/mercurial/revset.py Mon May 21 14:24:24 2012 -0500 +++ b/mercurial/revset.py Sat May 19 17:18:29 2012 +0200 @@ -1283,6 +1283,27 @@ return w + wa, (op, x[1], ta) return 1, x +_aliasarg = ('func', ('symbol', '_aliasarg')) +def _getaliasarg(tree): + """If tree matches ('func', ('symbol', '_aliasarg'), ('string', X)) + return X, None otherwise. + """ + if (len(tree) == 3 and tree[:2] == _aliasarg + and tree[2][0] == 'string'): + return tree[2][1] + return None + +def _checkaliasarg(tree, known=None): + """Check tree contains no _aliasarg construct or only ones which + value is in known. Used to avoid alias placeholders injection. + """ + if isinstance(tree, tuple): + arg = _getaliasarg(tree) + if arg is not None and (not known or arg not in known): + raise error.ParseError(_("not a function: %s") % '_aliasarg') + for t in tree: + _checkaliasarg(t, known) + class revsetalias(object): funcre = re.compile('^([^(]+)\(([^)]+)\)$') args = None @@ -1299,7 +1320,9 @@ self.tree = ('func', ('symbol', m.group(1))) self.args = [x.strip() for x in m.group(2).split(',')] for arg in self.args: - value = value.replace(arg, repr(arg)) + # _aliasarg() is an unknown symbol only used separate + # alias argument placeholders from regular strings. + value = value.replace(arg, '_aliasarg(%r)' % (arg,)) else: self.name = name self.tree = ('symbol', name) @@ -1307,6 +1330,8 @@ self.replacement, pos = parse(value) if pos != len(value): raise error.ParseError(_('invalid token'), pos) + # Check for placeholder injection + _checkaliasarg(self.replacement, self.args) def _getalias(aliases, tree): """If tree looks like an unexpanded alias, return it. Return None @@ -1327,13 +1352,14 @@ return None def _expandargs(tree, args): - """Replace all occurences of ('string', name) with the - substitution value of the same name in args, recursively. + """Replace _aliasarg instances with the substitution value of the + same name in args, recursively. """ - if not isinstance(tree, tuple): + if not tree or not isinstance(tree, tuple): return tree - if len(tree) == 2 and tree[0] == 'string': - return args.get(tree[1], tree) + arg = _getaliasarg(tree) + if arg is not None: + return args[arg] return tuple(_expandargs(t, args) for t in tree) def _expandaliases(aliases, tree, expanding): @@ -1367,6 +1393,7 @@ return result def findaliases(ui, tree): + _checkaliasarg(tree) aliases = {} for k, v in ui.configitems('revsetalias'): alias = revsetalias(k, v)