changeset 34990:cabc840ffdee stable 4.4.1

stable: merge with security patches
author Augie Fackler <augie@google.com>
date Tue, 07 Nov 2017 11:22:24 -0500
parents 0f5521e56b77 (current diff) 1a314176da9c (diff)
children 929bf8390056
files mercurial/configitems.py
diffstat 9 files changed, 318 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/configitems.py	Mon Nov 06 10:33:40 2017 -0800
+++ b/mercurial/configitems.py	Tue Nov 07 11:22:24 2017 -0500
@@ -817,6 +817,18 @@
 coreconfigitem('sparse', 'missingwarning',
     default=True,
 )
+coreconfigitem('subrepos', 'allowed',
+    default=dynamicdefault,  # to make backporting simpler
+)
+coreconfigitem('subrepos', 'hg:allowed',
+    default=dynamicdefault,
+)
+coreconfigitem('subrepos', 'git:allowed',
+    default=dynamicdefault,
+)
+coreconfigitem('subrepos', 'svn:allowed',
+    default=dynamicdefault,
+)
 coreconfigitem('templates', '.*',
     default=None,
     generic=True,
--- a/mercurial/help/config.txt	Mon Nov 06 10:33:40 2017 -0800
+++ b/mercurial/help/config.txt	Tue Nov 07 11:22:24 2017 -0500
@@ -1893,6 +1893,47 @@
 doesn't match the full path, an attempt is made to apply it on the
 relative path alone. The rules are applied in definition order.
 
+``subrepos``
+------------
+
+This section contains options that control the behavior of the
+subrepositories feature. See also :hg:`help subrepos`.
+
+Security note: auditing in Mercurial is known to be insufficient to
+prevent clone-time code execution with carefully constructed Git
+subrepos. It is unknown if a similar detect is present in Subversion
+subrepos. Both Git and Subversion subrepos are disabled by default
+out of security concerns. These subrepo types can be enabled using
+the respective options below.
+
+``allowed``
+    Whether subrepositories are allowed in the working directory.
+
+    When false, commands involving subrepositories (like :hg:`update`)
+    will fail for all subrepository types.
+    (default: true)
+
+``hg:allowed``
+    Whether Mercurial subrepositories are allowed in the working
+    directory. This option only has an effect if ``subrepos.allowed``
+    is true.
+    (default: true)
+
+``git:allowed``
+    Whether Git subrepositories are allowed in the working directory.
+    This option only has an effect if ``subrepos.allowed`` is true.
+
+    See the security note above before enabling Git subrepos.
+    (default: false)
+
+``svn:allowed``
+    Whether Subversion subrepositories are allowed in the working
+    directory. This option only has an effect if ``subrepos.allowed``
+    is true.
+
+    See the security note above before enabling Subversion subrepos.
+    (default: false)
+
 ``templatealias``
 -----------------
 
--- a/mercurial/subrepo.py	Mon Nov 06 10:33:40 2017 -0800
+++ b/mercurial/subrepo.py	Tue Nov 07 11:22:24 2017 -0500
@@ -359,6 +359,33 @@
                           "in '%s'\n") % vfs.join(dirname))
                 vfs.unlink(vfs.reljoin(dirname, f))
 
+def _auditsubrepopath(repo, path):
+    # auditor doesn't check if the path itself is a symlink
+    pathutil.pathauditor(repo.root)(path)
+    if repo.wvfs.islink(path):
+        raise error.Abort(_("subrepo '%s' traverses symbolic link") % path)
+
+SUBREPO_ALLOWED_DEFAULTS = {
+    'hg': True,
+    'git': False,
+    'svn': False,
+}
+
+def _checktype(ui, kind):
+    # subrepos.allowed is a master kill switch. If disabled, subrepos are
+    # disabled period.
+    if not ui.configbool('subrepos', 'allowed', True):
+        raise error.Abort(_('subrepos not enabled'),
+                          hint=_("see 'hg help config.subrepos' for details"))
+
+    default = SUBREPO_ALLOWED_DEFAULTS.get(kind, False)
+    if not ui.configbool('subrepos', '%s:allowed' % kind, default):
+        raise error.Abort(_('%s subrepos not allowed') % kind,
+                          hint=_("see 'hg help config.subrepos' for details"))
+
+    if kind not in types:
+        raise error.Abort(_('unknown subrepo type %s') % kind)
+
 def subrepo(ctx, path, allowwdir=False, allowcreate=True):
     """return instance of the right subrepo class for subrepo in path"""
     # subrepo inherently violates our import layering rules
@@ -369,10 +396,10 @@
     from . import hg as h
     hg = h
 
-    pathutil.pathauditor(ctx.repo().root)(path)
+    repo = ctx.repo()
+    _auditsubrepopath(repo, path)
     state = ctx.substate[path]
-    if state[2] not in types:
-        raise error.Abort(_('unknown subrepo type %s') % state[2])
+    _checktype(repo.ui, state[2])
     if allowwdir:
         state = (state[0], ctx.subrev(path), state[2])
     return types[state[2]](ctx, path, state[:2], allowcreate)
@@ -387,10 +414,10 @@
     from . import hg as h
     hg = h
 
-    pathutil.pathauditor(ctx.repo().root)(path)
+    repo = ctx.repo()
+    _auditsubrepopath(repo, path)
     state = ctx.substate[path]
-    if state[2] not in types:
-        raise error.Abort(_('unknown subrepo type %s') % state[2])
+    _checktype(repo.ui, state[2])
     subrev = ''
     if state[2] == 'hg':
         subrev = "0" * 40
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-audit-subrepo.t	Tue Nov 07 11:22:24 2017 -0500
@@ -0,0 +1,132 @@
+Test illegal name
+-----------------
+
+on commit:
+
+  $ hg init hgname
+  $ cd hgname
+  $ mkdir sub
+  $ hg init sub/.hg
+  $ echo 'sub/.hg = sub/.hg' >> .hgsub
+  $ hg ci -qAm 'add subrepo "sub/.hg"'
+  abort: path 'sub/.hg' is inside nested repo 'sub'
+  [255]
+
+prepare tampered repo (including the commit above):
+
+  $ hg import --bypass -qm 'add subrepo "sub/.hg"' - <<'EOF'
+  > diff --git a/.hgsub b/.hgsub
+  > new file mode 100644
+  > --- /dev/null
+  > +++ b/.hgsub
+  > @@ -0,0 +1,1 @@
+  > +sub/.hg = sub/.hg
+  > diff --git a/.hgsubstate b/.hgsubstate
+  > new file mode 100644
+  > --- /dev/null
+  > +++ b/.hgsubstate
+  > @@ -0,0 +1,1 @@
+  > +0000000000000000000000000000000000000000 sub/.hg
+  > EOF
+  $ cd ..
+
+on clone (and update):
+
+  $ hg clone -q hgname hgname2
+  abort: path 'sub/.hg' is inside nested repo 'sub'
+  [255]
+
+Test direct symlink traversal
+-----------------------------
+
+#if symlink
+
+on commit:
+
+  $ mkdir hgsymdir
+  $ hg init hgsymdir/root
+  $ cd hgsymdir/root
+  $ ln -s ../out
+  $ hg ci -qAm 'add symlink "out"'
+  $ hg init ../out
+  $ echo 'out = out' >> .hgsub
+  $ hg ci -qAm 'add subrepo "out"'
+  abort: subrepo 'out' traverses symbolic link
+  [255]
+
+prepare tampered repo (including the commit above):
+
+  $ hg import --bypass -qm 'add subrepo "out"' - <<'EOF'
+  > diff --git a/.hgsub b/.hgsub
+  > new file mode 100644
+  > --- /dev/null
+  > +++ b/.hgsub
+  > @@ -0,0 +1,1 @@
+  > +out = out
+  > diff --git a/.hgsubstate b/.hgsubstate
+  > new file mode 100644
+  > --- /dev/null
+  > +++ b/.hgsubstate
+  > @@ -0,0 +1,1 @@
+  > +0000000000000000000000000000000000000000 out
+  > EOF
+  $ cd ../..
+
+on clone (and update):
+
+  $ mkdir hgsymdir2
+  $ hg clone -q hgsymdir/root hgsymdir2/root
+  abort: subrepo 'out' traverses symbolic link
+  [255]
+  $ ls hgsymdir2
+  root
+
+#endif
+
+Test indirect symlink traversal
+-------------------------------
+
+#if symlink
+
+on commit:
+
+  $ mkdir hgsymin
+  $ hg init hgsymin/root
+  $ cd hgsymin/root
+  $ ln -s ../out
+  $ hg ci -qAm 'add symlink "out"'
+  $ mkdir ../out
+  $ hg init ../out/sub
+  $ echo 'out/sub = out/sub' >> .hgsub
+  $ hg ci -qAm 'add subrepo "out/sub"'
+  abort: path 'out/sub' traverses symbolic link 'out'
+  [255]
+
+prepare tampered repo (including the commit above):
+
+  $ hg import --bypass -qm 'add subrepo "out/sub"' - <<'EOF'
+  > diff --git a/.hgsub b/.hgsub
+  > new file mode 100644
+  > --- /dev/null
+  > +++ b/.hgsub
+  > @@ -0,0 +1,1 @@
+  > +out/sub = out/sub
+  > diff --git a/.hgsubstate b/.hgsubstate
+  > new file mode 100644
+  > --- /dev/null
+  > +++ b/.hgsubstate
+  > @@ -0,0 +1,1 @@
+  > +0000000000000000000000000000000000000000 out/sub
+  > EOF
+  $ cd ../..
+
+on clone (and update):
+
+  $ mkdir hgsymin2
+  $ hg clone -q hgsymin/root hgsymin2/root
+  abort: path 'out/sub' traverses symbolic link 'out'
+  [255]
+  $ ls hgsymin2
+  root
+
+#endif
--- a/tests/test-convert-git.t	Mon Nov 06 10:33:40 2017 -0800
+++ b/tests/test-convert-git.t	Tue Nov 07 11:22:24 2017 -0500
@@ -6,6 +6,10 @@
   $ echo "autocrlf = false" >> $HOME/.gitconfig
   $ echo "[extensions]" >> $HGRCPATH
   $ echo "convert=" >> $HGRCPATH
+  $ cat >> $HGRCPATH <<EOF
+  > [subrepos]
+  > git:allowed = true
+  > EOF
   $ GIT_AUTHOR_NAME='test'; export GIT_AUTHOR_NAME
   $ GIT_AUTHOR_EMAIL='test@example.org'; export GIT_AUTHOR_EMAIL
   $ GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0000"; export GIT_AUTHOR_DATE
--- a/tests/test-mq-subrepo-svn.t	Mon Nov 06 10:33:40 2017 -0800
+++ b/tests/test-mq-subrepo-svn.t	Tue Nov 07 11:22:24 2017 -0500
@@ -5,6 +5,9 @@
   > mq =
   > [diff]
   > nodates = 1
+  > [subrepos]
+  > allowed = true
+  > svn:allowed = true
   > EOF
 
 fn to create new repository, and cd into it
--- a/tests/test-subrepo-git.t	Mon Nov 06 10:33:40 2017 -0800
+++ b/tests/test-subrepo-git.t	Tue Nov 07 11:22:24 2017 -0500
@@ -41,7 +41,23 @@
   $ echo 's = [git]../gitroot' > .hgsub
   $ git clone -q ../gitroot s
   $ hg add .hgsub
+
+git subrepo is disabled by default
+
   $ hg commit -m 'new git subrepo'
+  abort: git subrepos not allowed
+  (see 'hg help config.subrepos' for details)
+  [255]
+
+so enable it
+
+  $ cat >> $HGRCPATH <<EOF
+  > [subrepos]
+  > git:allowed = true
+  > EOF
+
+  $ hg commit -m 'new git subrepo'
+
   $ hg debugsub
   path s
    source   ../gitroot
@@ -86,9 +102,29 @@
   path s
    source   ../gitroot
    revision 126f2a14290cd5ce061fdedc430170e8d39e1c5a
+  $ cd ..
+
+clone with subrepo disabled (update should fail)
+
+  $ hg clone t -U tc2 --config subrepos.allowed=false
+  $ hg update -R tc2 --config subrepos.allowed=false
+  abort: subrepos not enabled
+  (see 'hg help config.subrepos' for details)
+  [255]
+  $ ls tc2
+  a
+
+  $ hg clone t tc3 --config subrepos.allowed=false
+  updating to branch default
+  abort: subrepos not enabled
+  (see 'hg help config.subrepos' for details)
+  [255]
+  $ ls tc3
+  a
 
 update to previous substate
 
+  $ cd tc
   $ hg update 1 -q
   $ cat s/g
   g
@@ -400,11 +436,13 @@
 Don't crash if subrepo is a broken symlink
   $ ln -s broken s
   $ hg status -S
+  abort: subrepo 's' traverses symbolic link
+  [255]
   $ hg push -q
-  abort: subrepo s is missing (in subrepository "s")
+  abort: subrepo 's' traverses symbolic link
   [255]
   $ hg commit --subrepos -qm missing
-  abort: subrepo s is missing (in subrepository "s")
+  abort: subrepo 's' traverses symbolic link
   [255]
   $ rm s
 #endif
--- a/tests/test-subrepo-svn.t	Mon Nov 06 10:33:40 2017 -0800
+++ b/tests/test-subrepo-svn.t	Tue Nov 07 11:22:24 2017 -0500
@@ -57,6 +57,21 @@
   $ mkdir subdir
   $ svn co --quiet "$SVNREPOURL"/src subdir/s
   $ hg add .hgsub
+
+svn subrepo is disabled by default
+
+  $ hg ci -m1
+  abort: svn subrepos not allowed
+  (see 'hg help config.subrepos' for details)
+  [255]
+
+so enable it
+
+  $ cat >> $HGRCPATH <<EOF
+  > [subrepos]
+  > svn:allowed = true
+  > EOF
+
   $ hg ci -m1
 
 make sure we avoid empty commits (issue2445)
--- a/tests/test-subrepo.t	Mon Nov 06 10:33:40 2017 -0800
+++ b/tests/test-subrepo.t	Tue Nov 07 11:22:24 2017 -0500
@@ -484,9 +484,47 @@
   path t
    source   t
    revision 20a0db6fbf6c3d2836e6519a642ae929bfc67c0e
+  $ cd ..
+
+clone with subrepo disabled (update should fail)
+
+  $ hg clone t -U tc2 --config subrepos.allowed=false
+  $ hg update -R tc2 --config subrepos.allowed=false
+  abort: subrepos not enabled
+  (see 'hg help config.subrepos' for details)
+  [255]
+  $ ls tc2
+  a
+
+  $ hg clone t tc3 --config subrepos.allowed=false
+  updating to branch default
+  abort: subrepos not enabled
+  (see 'hg help config.subrepos' for details)
+  [255]
+  $ ls tc3
+  a
+
+And again with just the hg type disabled
+
+  $ hg clone t -U tc4 --config subrepos.hg:allowed=false
+  $ hg update -R tc4 --config subrepos.hg:allowed=false
+  abort: hg subrepos not allowed
+  (see 'hg help config.subrepos' for details)
+  [255]
+  $ ls tc4
+  a
+
+  $ hg clone t tc5 --config subrepos.hg:allowed=false
+  updating to branch default
+  abort: hg subrepos not allowed
+  (see 'hg help config.subrepos' for details)
+  [255]
+  $ ls tc5
+  a
 
 push
 
+  $ cd tc
   $ echo bah > t/t
   $ hg ci -m11
   committing subrepository t