diff --git a/contrib/hg-ssh b/contrib/hg-ssh index 5ec5100..c643f97 100755 --- a/contrib/hg-ssh +++ b/contrib/hg-ssh @@ -32,7 +32,7 @@ command="hg-ssh --read-only repos/*" # enable importing on demand to reduce startup time from mercurial import demandimport; demandimport.enable() -from mercurial import dispatch +from mercurial import dispatch, ui as uimod import sys, os, shlex @@ -61,14 +61,15 @@ def main(): repo = os.path.normpath(os.path.join(cwd, os.path.expanduser(path))) if repo in allowed_paths: cmd = ['-R', repo, 'serve', '--stdio'] + req = dispatch.request(cmd) if readonly: - cmd += [ - '--config', - 'hooks.prechangegroup.hg-ssh=python:__main__.rejectpush', - '--config', - 'hooks.prepushkey.hg-ssh=python:__main__.rejectpush' - ] - dispatch.dispatch(dispatch.request(cmd)) + if not req.ui: + req.ui = uimod.ui() + req.ui.setconfig('hooks', 'pretxnopen.hg-ssh', + 'python:__main__.rejectpush', 'hg-ssh') + req.ui.setconfig('hooks', 'prepushkey.hg-ssh', + 'python:__main__.rejectpush', 'hg-ssh') + dispatch.dispatch(req) else: sys.stderr.write('Illegal repository "%s"\n' % repo) sys.exit(255) diff --git a/mercurial/dispatch.py b/mercurial/dispatch.py index fa2532d..99e1d5b 100644 --- a/mercurial/dispatch.py +++ b/mercurial/dispatch.py @@ -87,6 +87,36 @@ def _runcatch(req): pass # happens if called in a thread try: + realcmd = None + try: + cmdargs = fancyopts.fancyopts(req.args[:], commands.globalopts, {}) + cmd = cmdargs[0] + aliases, entry = cmdutil.findcmd(cmd, commands.table, False) + realcmd = aliases[0] + except (error.UnknownCommand, error.AmbiguousCommand, + IndexError, fancyopts.getopt.GetoptError): + # Don't handle this here. We know the command is + # invalid, but all we're worried about for now is that + # it's not a command that server operators expect to + # be safe to offer to users in a sandbox. + pass + if realcmd == 'serve' and '--stdio' in cmdargs: + # We want to constrain 'hg serve --stdio' instances pretty + # closely, as many shared-ssh access tools want to grant + # access to run *only* 'hg -R $repo serve --stdio'. We + # restrict to exactly that set of arguments, and prohibit + # any repo name that starts with '--' to prevent + # shenanigans wherein a user does something like pass + # --debugger or --config=ui.debugger=1 as a repo + # name. This used to actually run the debugger. + if (len(req.args) != 4 or + req.args[0] != '-R' or + req.args[1].startswith('--') or + req.args[2] != 'serve' or + req.args[3] != '--stdio'): + raise error.Abort( + _('potentially unsafe serve --stdio invocation: %r') % + (req.args,)) try: # enter the debugger before command execution if '--debugger' in req.args: diff --git a/tests/test-hup.t b/tests/test-hup.t deleted file mode 100644 index 9745643..0000000 --- a/tests/test-hup.t +++ /dev/null @@ -1,28 +0,0 @@ -Test hangup signal in the middle of transaction - - $ "$TESTDIR/hghave" serve fifo || exit 80 - $ hg init - $ mkfifo p - $ hg serve --stdio < p 1>out 2>&1 & - $ P=$! - -Do test while holding fifo open - - $ ( - > echo lock - > echo addchangegroup - > while [ ! -s .hg/store/journal ]; do sleep 0; done - > kill -HUP $P - > ) > p - - $ wait - $ cat out - 0 - 0 - adding changesets - transaction abort! - rollback completed - killed! - - $ echo .hg/* .hg/store/* - .hg/00changelog.i .hg/journal.bookmarks .hg/journal.branch .hg/journal.desc .hg/journal.dirstate .hg/requires .hg/store .hg/store/00changelog.i .hg/store/00changelog.i.a .hg/store/journal.phaseroots diff --git a/tests/test-ssh.t b/tests/test-ssh.t index f1584af..ad60b0e 100644 --- a/tests/test-ssh.t +++ b/tests/test-ssh.t @@ -291,6 +291,19 @@ Test (non-)escaping of remote paths with spaces when cloning (issue3145): abort: destination 'a repo' is not empty [255] +Make sure hg is really paranoid in serve --stdio mode. It used to be +possible to get a debugger REPL by specifying a repo named --debugger. + $ hg -R --debugger serve --stdio + abort: potentially unsafe serve --stdio invocation: ['-R', '--debugger', 'serve', '--stdio'] + [255] + $ hg -R --config=ui.debugger=yes serve --stdio + abort: potentially unsafe serve --stdio invocation: ['-R', '--config=ui.debugger=yes', 'serve', '--stdio'] + [255] +Abbreviations of 'serve' also don't work, to avoid shenanigans. + $ hg -R narf serv --stdio + abort: potentially unsafe serve --stdio invocation: ['-R', 'narf', 'serv', '--stdio'] + [255] + Test hg-ssh using a helper script that will restore PYTHONPATH (which might have been cleared by a hg.exe wrapper) and invoke hg-ssh with the right parameters: @@ -382,3 +395,4 @@ Test hg-ssh in read-only mode: Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio + changegroup-in-remote hook: HG_NODE=f9f0a8007dba27625503c51182b535f85fd4bb6b HG_SOURCE=serve HG_URL=remote:ssh: