[PATCH] subrepo: config option to disable subrepositories

Gregory Szorc gregory.szorc at gmail.com
Fri Nov 3 20:28:27 EDT 2017


# HG changeset patch
# User Gregory Szorc <gregory.szorc at gmail.com>
# Date 1509755155 25200
#      Fri Nov 03 17:25:55 2017 -0700
# Branch stable
# Node ID f2390c369bfebf32f26f5a2e4aa5620224a7c8ea
# Parent  f445b10dc7fb3495d24d1c22b0996148864c77f7
subrepo: config option to disable subrepositories

Subrepositories are a lesser-used feature that has a history of security
vulnerabilities. Previous subrepo vulnerabilities have resulted in
arbitrary code execution during `hg clone`. This is one of the worst
kind of vulnerabilities a version control system can have.

Another security concern is that Mercurial supports non-Mercurial
subrepositories. (Git and Subversion are supported by default.) While
the Mercurial developers try to keep up with development of other
version control systems, it is effectively impossible for us to
keep tabs on all 3rd party changes and their security impact. Every
time Mercurial attempts to call out into another [version control]
tool, we run a higher risk of accidentally introducing a security
vulnerability. This is what's referred to as "surface area for
"attack" in computer security speak.

Since subrepos have a history of vulnerabilities, increase our
exposure to security issues in other tools, and aren't widely used,
or a critical feature to have enabled by default, it makes sense
for the feature to be optional.

This commit introduces a config flag to control whether subrepos are
enabled. The default of having them enabled remains unchanged.

This patch is simple and crude. As the added test demonstrates,
it does a few things sub-optimally.

One class of problems stems from the fact that the .hgsubstate file
is managed automatically. This code still runs in this patch and
the result is the content of .hgsubstate will get nuked by a client
with the feature disabled. If you have this file in your repo and
disable the feature and commit, you lose the file. That's obviously
not good.

I figure this patch is effective enough to send out, even though
it has problems. I'll continue to work on a more thorough solution...

diff --git a/mercurial/configitems.py b/mercurial/configitems.py
--- a/mercurial/configitems.py
+++ b/mercurial/configitems.py
@@ -857,6 +857,9 @@ coreconfigitem('ui', 'debugger',
 coreconfigitem('ui', 'editor',
     default=dynamicdefault,
 )
+coreconfigitem('ui', 'enablesubrepos',
+    default=True,
+)
 coreconfigitem('ui', 'fallbackencoding',
     default=None,
 )
diff --git a/mercurial/help/config.txt b/mercurial/help/config.txt
--- a/mercurial/help/config.txt
+++ b/mercurial/help/config.txt
@@ -2012,6 +2012,11 @@ User interface controls.
 ``editor``
     The editor to use during a commit. (default: ``$EDITOR`` or ``vi``)
 
+``enablesubrepos``
+    Whether the subrepositories feature is enabled. If disabled,
+    subrepositories are effectively ignored by the Mercurial client.
+    (default: True)
+
 ``fallbackencoding``
     Encoding to try if it's not possible to decode the changelog using
     UTF-8. (default: ISO-8859-1)
diff --git a/mercurial/subrepo.py b/mercurial/subrepo.py
--- a/mercurial/subrepo.py
+++ b/mercurial/subrepo.py
@@ -85,6 +85,14 @@ def state(ctx, ui):
     to tuple: (source from .hgsub, revision from .hgsubstate, kind
     (key in types dict))
     """
+    # Blank out knowledge about subrepos when the feature is disabled.
+    # This effectively causes consumers of this data structure to think
+    # there are no subrepos. This causes problems with the automatic
+    # management of .hgsubstate (which is based on this parsed data)
+    # and the subrepo() revset.
+    if not ui.configbool('ui', 'enablesubrepos'):
+        return {}
+
     p = config.config()
     repo = ctx.repo()
     def read(f, sections=None, remap=None):
diff --git a/tests/test-subrepo-disable.t b/tests/test-subrepo-disable.t
new file mode 100644
--- /dev/null
+++ b/tests/test-subrepo-disable.t
@@ -0,0 +1,153 @@
+  $ hg init hgsub
+  $ cd hgsub
+  $ echo sub0 > foo
+  $ hg -q commit -A -m 'subrepo initial'
+  $ hg log -T '{node}\n'
+  45cc468b8f18bee314935a4651bad80f9cb3b540
+  $ cd ..
+
+  $ hg init testrepo0
+  $ cd testrepo0
+  $ cat >> .hg/hgrc << EOF
+  > [ui]
+  > enablesubrepos = false
+  > EOF
+
+  $ echo 0 > foo
+  $ hg -q commit -A -m initial
+
+Adding a .hgsub should result in no sign of the subrepo in the working directory
+
+  $ cat > .hgsub << EOF
+  > hgsub = ../hgsub
+  > EOF
+
+  $ hg add .hgsub
+  $ hg commit -m 'add .hgsub'
+
+  $ hg files --subrepos
+  .hgsub
+  foo
+
+No .hgsubstate should have been created
+
+  $ hg files
+  .hgsub
+  foo
+
+Adding a nested hg repo will be treated like a normal nested hg repo - it will be ignored
+
+  $ hg init hgsub
+  $ hg st
+  $ rm -rf hgsub
+
+subrepo() revset still works even if feature is disabled
+
+TODO buggy
+  $ hg log -r 'subrepo()'
+
+  $ cd ..
+
+Cloning this repo won't clone subrepo because no .hgsubstate
+
+  $ hg clone --pull testrepo0 no-substate-enabled
+  requesting all changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 2 changesets with 2 changes to 2 files
+  new changesets 68986213bd44:addf99df3e66
+  updating to branch default
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+Cloning with subrepos disabled should be identical to above because no .hgsubstate
+
+  $ hg --config ui.enablesubrepos=false clone --pull testrepo0 no-substate-disabled
+  requesting all changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 2 changesets with 2 changes to 2 files
+  new changesets 68986213bd44:addf99df3e66
+  updating to branch default
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+Add an .hgsubstate manually to simulate a real repo with subrepos
+
+  $ cd testrepo0
+  $ cat > .hgsubstate << EOF
+  > 45cc468b8f18bee314935a4651bad80f9cb3b540 hgsub
+  > EOF
+  $ hg add .hgsubstate
+
+TODO buggy because localrepository.commit() managed its file contents
+  $ hg commit -m 'manually add .hgsubstate'
+  nothing changed
+  [1]
+
+... but hg commit --amend works (this relies on a regression in 4.4)
+
+  $ hg commit --amend
+  saved backup bundle to $TESTTMP/testrepo0/.hg/strip-backup/addf99df3e66-ab5c9ff8-amend.hg (glob)
+
+  $ cd ..
+
+Cloning with feature enabled should clone subrepos
+
+  $ hg clone --pull testrepo0 with-substate-enabled
+  requesting all changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 2 changesets with 3 changes to 3 files
+  new changesets 68986213bd44:7645bb5a5a99
+  updating to branch default
+  cloning subrepo hgsub from $TESTTMP/hgsub
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+Cloning with feature disabled should not clone subrepos
+
+  $ hg --config ui.enablesubrepos=false clone --pull testrepo0 with-substate-disabled
+  requesting all changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 2 changesets with 3 changes to 3 files
+  new changesets 68986213bd44:7645bb5a5a99
+  updating to branch default
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+Test a client with subrepos enabled on a clone missing them.
+Important: the .hgsubstate file (which is normally automatically managed)
+should be present and unmodified.
+
+  $ cd with-substate-disabled
+  $ hg status
+  M .hgsubstate
+TODO buggy
+  $ hg diff
+  diff -r 7645bb5a5a99 .hgsubstate
+  --- a/.hgsubstate	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/.hgsubstate	Thu Jan 01 00:00:00 1970 +0000
+  @@ -1,1 +0,0 @@
+  -45cc468b8f18bee314935a4651bad80f9cb3b540 hgsub
+  $ cat .hgsubstate
+  $ hg up
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+  $ cd ..
+
+Test a client with subrepos disabled on a clone with subrepos
+
+  $ cd with-substate-enabled
+  $ hg --config ui.enablesubrepos=false status
+  $ cat .hgsubstate
+  45cc468b8f18bee314935a4651bad80f9cb3b540 hgsub
+  $ hg --config ui.enablesubrepos=false up
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+  $ hg st
+  $ cat .hgsubstate
+  45cc468b8f18bee314935a4651bad80f9cb3b540 hgsub
+
+  $ cd ..


More information about the Mercurial-devel mailing list