diff --git a/mercurial/hook.py b/mercurial/hook.py --- a/mercurial/hook.py +++ b/mercurial/hook.py @@ -161,15 +161,28 @@ def _exthook(ui, repo, name, cmd, args, ui.warn(_('warning: %s hook %s\n') % (name, desc)) return r +# represent an untrusted hook command +_fromuntrusted = object() + def _allhooks(ui): """return a list of (hook-id, cmd) pairs sorted by priority""" hooks = _hookitems(ui) + # Be careful in this section, propagating the real commands from untrusted + # sources would create a security vulnerability, make sure anything altered + # in that section uses "_fromuntrusted" as its command. + untrustedhooks = _hookitems(ui, _untrusted=True) + for name, value in untrustedhooks.items(): + trustedvalue = hooks.get(name, (None, None, name, _fromuntrusted)) + if value != trustedvalue: + (lp, lo, lk, lv) = trustedvalue + hooks[name] = (lp, lo, lk, _fromuntrusted) + # (end of the security sensitive section) return [(k, v) for p, o, k, v in sorted(hooks.values())] -def _hookitems(ui): +def _hookitems(ui, _untrusted=False): """return all hooks items ready to be sorted""" hooks = {} - for name, cmd in ui.configitems('hooks'): + for name, cmd in ui.configitems('hooks', untrusted=_untrusted): if not name.startswith('priority'): priority = ui.configint('hooks', 'priority.%s' % name, 0) hooks[name] = (-priority, len(hooks), name, cmd) @@ -214,7 +227,15 @@ def runhooks(ui, repo, name, hooks, thro # files seem to be bogus, give up on redirecting (WSGI, etc) pass - if callable(cmd): + if cmd is _fromuntrusted: + if throw: + raise error.HookAbort( + _('untrusted hook %s not executed') % name, + hint = _("see 'hg help config.trusted'")) + ui.warn(_('warning: untrusted hook %s not executed\n') % name) + r = 1 + raised = False + elif callable(cmd): r, raised = _pythonhook(ui, repo, name, hname, cmd, args, throw) elif cmd.startswith('python:'): if cmd.count(':') >= 2: diff --git a/tests/test-hook.t b/tests/test-hook.t --- a/tests/test-hook.t +++ b/tests/test-hook.t @@ -794,7 +794,6 @@ new commits must be visible in pretxncha date: Thu Jan 01 00:00:00 1970 +0000 summary: b - $ cd .. pretxnclose hook failure should abort the transaction @@ -816,3 +815,62 @@ pretxnclose hook failure should abort th $ hg recover no interrupted transaction available [1] + $ cd .. + +Hook from untrusted hgrc are reported as failure +================================================ + + $ cat << EOF > $TESTTMP/untrusted.py + > from mercurial import scmutil, util + > def uisetup(ui): + > class untrustedui(ui.__class__): + > def _trusted(self, fp, f): + > if util.normpath(fp.name).endswith('untrusted/.hg/hgrc'): + > return False + > return super(untrustedui, self)._trusted(fp, f) + > ui.__class__ = untrustedui + > EOF + $ cat << EOF >> $HGRCPATH + > [extensions] + > untrusted=$TESTTMP/untrusted.py + > EOF + $ hg init untrusted + $ cd untrusted + +Non-blocking hook +----------------- + + $ cat << EOF >> .hg/hgrc + > [hooks] + > txnclose.testing=echo txnclose hook called + > EOF + $ touch a && hg commit -Aqm a + warning: untrusted hook txnclose not executed + $ hg log + changeset: 0:3903775176ed + tag: tip + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: a + + +Non-blocking hook +----------------- + + $ cat << EOF >> .hg/hgrc + > [hooks] + > pretxnclose.testing=echo pre-txnclose hook called + > EOF + $ touch b && hg commit -Aqm a + transaction abort! + rollback completed + abort: untrusted hook pretxnclose not executed + (see 'hg help config.trusted') + [255] + $ hg log + changeset: 0:3903775176ed + tag: tip + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: a +