--- 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