changeset 7715:a444c46a0649

middleware: fix handling of Git 'info/refs' command to give correct access control For a pull, the Git client first sends an 'info/refs' command with a 'service=git-upload-pack' query, then it sends the actual 'git-upload-pack' command. For a push, the Git client first sends an 'info/refs' command with a 'service=git-receive-pack' query, then it sends the actual 'git-receive-pack' command. Before, the 'info/refs' commands would fall back to the default of trying to use the action of the previous request. That seems wrong. Instead, authorize the 'info/refs' command just like the actual command it references. path_info will now be checked more than before. Mainly because that is more correct and more explicit and "better" to do it that way. It might also give some safety.
author Mads Kiilerich <mads@kiilerich.com>
date Mon, 07 Jan 2019 01:58:16 +0100
parents ce2a4ef8cd5f
children a4a4bcc09ac5
files kallithea/lib/middleware/simplegit.py
diffstat 1 files changed, 18 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- a/kallithea/lib/middleware/simplegit.py	Mon Jan 07 01:50:56 2019 +0100
+++ b/kallithea/lib/middleware/simplegit.py	Mon Jan 07 01:58:16 2019 +0100
@@ -148,22 +148,25 @@
 
     def __get_action(self, environ):
         """
-        Maps Git request commands into a pull or push command.
-
-        :param environ:
+        Maps Git request commands into 'pull' or 'push'.
         """
-        service = environ['QUERY_STRING'].split('=')
-
-        if len(service) > 1:
-            service_cmd = service[1]
-            mapping = {
-                'git-receive-pack': 'push',
-                'git-upload-pack': 'pull',
-            }
-            op = mapping[service_cmd]
-            self._git_stored_op = op
-            # try to fallback to stored variable as we don't know if the last
-            # operation is pull/push
+        mapping = {
+            'git-receive-pack': 'push',
+            'git-upload-pack': 'pull',
+        }
+        path_info = environ.get('PATH_INFO', '')
+        m = GIT_PROTO_PAT.match(path_info)
+        if m is not None:
+            cmd = m.group(2)
+            if cmd == 'info/refs':
+                service = environ['QUERY_STRING'].split('=')
+                cmd = service[1] if len(service) > 1 else None
+                if cmd in mapping:
+                    self._git_stored_op = mapping[cmd]
+            elif cmd in mapping:
+                self._git_stored_op = mapping[cmd]
+        # fallback to stored variable as we don't know if the last
+        # operation is pull/push
         return self._git_stored_op
 
     def _handle_githooks(self, repo_name, action, baseui, environ):