# HG changeset patch # User Augie Fackler # Date 1510071744 18000 # Node ID cabc840ffdee8a72f3689fb77dd74d04fdc2bc04 # Parent 0f5521e56b77fff228d109497a5cba9169224ce1# Parent 1a314176da9c62e843e90d2e40b14ba6cd32c4cb stable: merge with security patches diff -r 0f5521e56b77 -r cabc840ffdee mercurial/configitems.py --- 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, diff -r 0f5521e56b77 -r cabc840ffdee mercurial/help/config.txt --- 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`` ----------------- diff -r 0f5521e56b77 -r cabc840ffdee mercurial/subrepo.py --- 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 diff -r 0f5521e56b77 -r cabc840ffdee tests/test-audit-subrepo.t --- /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 diff -r 0f5521e56b77 -r cabc840ffdee tests/test-convert-git.t --- 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 < [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 diff -r 0f5521e56b77 -r cabc840ffdee tests/test-mq-subrepo-svn.t --- 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 diff -r 0f5521e56b77 -r cabc840ffdee tests/test-subrepo-git.t --- 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 < [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 diff -r 0f5521e56b77 -r cabc840ffdee tests/test-subrepo-svn.t --- 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 < [subrepos] + > svn:allowed = true + > EOF + $ hg ci -m1 make sure we avoid empty commits (issue2445) diff -r 0f5521e56b77 -r cabc840ffdee tests/test-subrepo.t --- 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