Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/hook.py @ 28938:ea1fec3e9aba
hook: report untrusted hooks as failure (issue5110) (BC)
Before this patch, there was no way for a repository owner to ensure that
validation hooks would be run by people with write access. If someone had write
access but did not trust the user owning the repository, the config and its hook
would simply be ignored.
After this patch, hooks from untrusted configs are taken into account but never
actually run. Instead they are reported as failures right away. This will ensure
validation performed by a hook is not ignored.
As a side effect writer can be forced to trust a repository hgrc by adding a
'pretxnopen.trust=true' hook to the file.
This was discussed during the 3.8 sprint with Matt Mackall, Augie Fackler and
Kevin Bullock.
author | Pierre-Yves David <pierre-yves.david@ens-lyon.org> |
---|---|
date | Thu, 14 Apr 2016 02:41:15 -0700 |
parents | 3112c5e18835 |
children | a1259e502bdf |
comparison
equal
deleted
inserted
replaced
28937:3112c5e18835 | 28938:ea1fec3e9aba |
---|---|
159 if throw: | 159 if throw: |
160 raise error.HookAbort(_('%s hook %s') % (name, desc)) | 160 raise error.HookAbort(_('%s hook %s') % (name, desc)) |
161 ui.warn(_('warning: %s hook %s\n') % (name, desc)) | 161 ui.warn(_('warning: %s hook %s\n') % (name, desc)) |
162 return r | 162 return r |
163 | 163 |
164 # represent an untrusted hook command | |
165 _fromuntrusted = object() | |
166 | |
164 def _allhooks(ui): | 167 def _allhooks(ui): |
165 """return a list of (hook-id, cmd) pairs sorted by priority""" | 168 """return a list of (hook-id, cmd) pairs sorted by priority""" |
166 hooks = _hookitems(ui) | 169 hooks = _hookitems(ui) |
170 # Be careful in this section, propagating the real commands from untrusted | |
171 # sources would create a security vulnerability, make sure anything altered | |
172 # in that section uses "_fromuntrusted" as its command. | |
173 untrustedhooks = _hookitems(ui, _untrusted=True) | |
174 for name, value in untrustedhooks.items(): | |
175 trustedvalue = hooks.get(name, (None, None, name, _fromuntrusted)) | |
176 if value != trustedvalue: | |
177 (lp, lo, lk, lv) = trustedvalue | |
178 hooks[name] = (lp, lo, lk, _fromuntrusted) | |
179 # (end of the security sensitive section) | |
167 return [(k, v) for p, o, k, v in sorted(hooks.values())] | 180 return [(k, v) for p, o, k, v in sorted(hooks.values())] |
168 | 181 |
169 def _hookitems(ui): | 182 def _hookitems(ui, _untrusted=False): |
170 """return all hooks items ready to be sorted""" | 183 """return all hooks items ready to be sorted""" |
171 hooks = {} | 184 hooks = {} |
172 for name, cmd in ui.configitems('hooks'): | 185 for name, cmd in ui.configitems('hooks', untrusted=_untrusted): |
173 if not name.startswith('priority'): | 186 if not name.startswith('priority'): |
174 priority = ui.configint('hooks', 'priority.%s' % name, 0) | 187 priority = ui.configint('hooks', 'priority.%s' % name, 0) |
175 hooks[name] = (-priority, len(hooks), name, cmd) | 188 hooks[name] = (-priority, len(hooks), name, cmd) |
176 return hooks | 189 return hooks |
177 | 190 |
212 os.dup2(stderrno, stdoutno) | 225 os.dup2(stderrno, stdoutno) |
213 except (OSError, AttributeError): | 226 except (OSError, AttributeError): |
214 # files seem to be bogus, give up on redirecting (WSGI, etc) | 227 # files seem to be bogus, give up on redirecting (WSGI, etc) |
215 pass | 228 pass |
216 | 229 |
217 if callable(cmd): | 230 if cmd is _fromuntrusted: |
231 if throw: | |
232 raise error.HookAbort( | |
233 _('untrusted hook %s not executed') % name, | |
234 hint = _("see 'hg help config.trusted'")) | |
235 ui.warn(_('warning: untrusted hook %s not executed\n') % name) | |
236 r = 1 | |
237 raised = False | |
238 elif callable(cmd): | |
218 r, raised = _pythonhook(ui, repo, name, hname, cmd, args, throw) | 239 r, raised = _pythonhook(ui, repo, name, hname, cmd, args, throw) |
219 elif cmd.startswith('python:'): | 240 elif cmd.startswith('python:'): |
220 if cmd.count(':') >= 2: | 241 if cmd.count(':') >= 2: |
221 path, cmd = cmd[7:].rsplit(':', 1) | 242 path, cmd = cmd[7:].rsplit(':', 1) |
222 path = util.expandpath(path) | 243 path = util.expandpath(path) |