diff -r 576b92928cc7 -r 0d572769046a mercurial/narrowspec.py --- a/mercurial/narrowspec.py Tue Sep 11 10:54:20 2018 -0700 +++ b/mercurial/narrowspec.py Tue Sep 11 15:25:35 2018 -0700 @@ -20,6 +20,15 @@ FILENAME = 'narrowspec' +# Pattern prefixes that are allowed in narrow patterns. This list MUST +# only contain patterns that are fast and safe to evaluate. Keep in mind +# that patterns are supplied by clients and executed on remote servers +# as part of wire protocol commands. +VALID_PREFIXES = ( + b'path:', + b'rootfilesin:', +) + def parseserverpatterns(text): """Parses the narrowspec format that's returned by the server.""" includepats = set() @@ -82,8 +91,42 @@ return '%s:%s' % normalizesplitpattern(kind, pat) def parsepatterns(pats): - """Parses a list of patterns into a typed pattern set.""" - return set(normalizepattern(p) for p in pats) + """Parses an iterable of patterns into a typed pattern set. + + Patterns are assumed to be ``path:`` if no prefix is present. + For safety and performance reasons, only some prefixes are allowed. + See ``validatepatterns()``. + + This function should be used on patterns that come from the user to + normalize and validate them to the internal data structure used for + representing patterns. + """ + res = {normalizepattern(orig) for orig in pats} + validatepatterns(res) + return res + +def validatepatterns(pats): + """Validate that patterns are in the expected data structure and format. + + And that is a set of normalized patterns beginning with ``path:`` or + ``rootfilesin:``. + + This function should be used to validate internal data structures + and patterns that are loaded from sources that use the internal, + prefixed pattern representation (but can't necessarily be fully trusted). + """ + if not isinstance(pats, set): + raise error.ProgrammingError('narrow patterns should be a set; ' + 'got %r' % pats) + + for pat in pats: + if not pat.startswith(VALID_PREFIXES): + # Use a Mercurial exception because this can happen due to user + # bugs (e.g. manually updating spec file). + raise error.Abort(_('invalid prefix on narrow pattern: %s') % pat, + hint=_('narrow patterns must begin with one of ' + 'the following: %s') % + ', '.join(VALID_PREFIXES)) def format(includes, excludes): output = '[include]\n'