subrepo: disallow symlink traversal across subrepo mount point (SEC)
It wasn't easy to extend the pathauditor to check symlink traversal across
subrepos because pathauditor._checkfs() rejects a directory having ".hg"
directory. That's why I added the explicit islink() check.
No idea if this patch is necessary after we've fixed the
issue5730 by
splitting submerge() into planning and execution phases.
--- a/mercurial/subrepo.py Fri Nov 03 19:17:25 2017 +0900
+++ b/mercurial/subrepo.py Fri Nov 03 20:12:50 2017 +0900
@@ -359,6 +359,12 @@
"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)
+
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,7 +375,7 @@
from . import hg as h
hg = h
- pathutil.pathauditor(ctx.repo().root)(path)
+ _auditsubrepopath(ctx.repo(), path)
state = ctx.substate[path]
if state[2] not in types:
raise error.Abort(_('unknown subrepo type %s') % state[2])
@@ -387,7 +393,7 @@
from . import hg as h
hg = h
- pathutil.pathauditor(ctx.repo().root)(path)
+ _auditsubrepopath(ctx.repo(), path)
state = ctx.substate[path]
if state[2] not in types:
raise error.Abort(_('unknown subrepo type %s') % state[2])
--- a/tests/test-audit-subrepo.t Fri Nov 03 19:17:25 2017 +0900
+++ b/tests/test-audit-subrepo.t Fri Nov 03 20:12:50 2017 +0900
@@ -50,17 +50,35 @@
$ hg ci -qAm 'add symlink "out"'
$ hg init ../out
$ echo 'out = out' >> .hgsub
-BROKEN: should fail
$ 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
-BROKEN: should fail to update
$ hg clone -q hgsymdir/root hgsymdir2/root
+ abort: subrepo 'out' traverses symbolic link
+ [255]
$ ls hgsymdir2
- out
root
#endif
--- a/tests/test-subrepo-git.t Fri Nov 03 19:17:25 2017 +0900
+++ b/tests/test-subrepo-git.t Fri Nov 03 20:12:50 2017 +0900
@@ -400,11 +400,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