changeset 7397:3ee4ac068369 stable

hg: explicit handling of the 'batch' protocol command - consider it a "push" command if any of the batch commands are This change mitigates some privilege escalation problems like CVE-2018-1000132 which was fixed in Mercurial 4.5.1 and currently is described on https://www.mercurial-scm.org/wiki/WhatsNew#Mercurial_4.5.1_.2F_4.5.2_.282018-03-06.29 .
author Mads Kiilerich <mads@kiilerich.com>
date Sun, 21 Oct 2018 15:18:43 +0200
parents 21084a951cd9
children ba444b73e01a
files kallithea/lib/middleware/simplehg.py
diffstat 1 files changed, 30 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/kallithea/lib/middleware/simplehg.py	Thu Oct 11 02:06:50 2018 +0200
+++ b/kallithea/lib/middleware/simplehg.py	Sun Oct 21 15:18:43 2018 +0200
@@ -31,6 +31,7 @@
 import os
 import logging
 import traceback
+import urllib
 
 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \
@@ -66,6 +67,24 @@
     return ishg_path
 
 
+def get_header_hgarg(environ):
+    """Decode the special Mercurial encoding of big requests over multiple headers.
+    >>> get_header_hgarg({})
+    ''
+    >>> get_header_hgarg({'HTTP_X_HGARG_0': ' ', 'HTTP_X_HGARG_1': 'a','HTTP_X_HGARG_2': '','HTTP_X_HGARG_3': 'b+c %20'})
+    'ab+c %20'
+    """
+    chunks = []
+    i = 1
+    while True:
+        v = environ.get('HTTP_X_HGARG_%d' % i)
+        if v is None:
+            break
+        chunks.append(v)
+        i += 1
+    return ''.join(chunks)
+
+
 class SimpleHg(BaseVCSController):
 
     def _handle_request(self, environ, start_response):
@@ -278,6 +297,17 @@
             parts = qry.split('=', 1)
             if len(parts) == 2 and parts[0] == 'cmd':
                 cmd = parts[1]
+                if cmd == 'batch':
+                    hgarg = get_header_hgarg(environ)
+                    if not hgarg.startswith('cmds='):
+                        return 'push' # paranoid and safe
+                    for cmd_arg in hgarg[5:].split(';'):
+                        cmd, _args = urllib.unquote_plus(cmd_arg).split(' ', 1)
+                        op = mapping.get(cmd, 'pull')
+                        if op != 'pull':
+                            assert op == 'push'
+                            return 'push'
+                    return 'pull'
                 return mapping.get(cmd, 'pull')
 
         raise Exception('Unable to detect pull/push action !!'