changeset 7296:caa482f8fb5f

repos: only allow api repo creation in existing groups Fix problem with '../something' paths being allowed; '..' will always exist and can't be created. This also introduce a small API change: Repository groups must now exist before repositories can be created. This makes the API more explicit and simpler. This issue was found and reported by Kacper Szurek https://security.szurek.pl/
author Mads Kiilerich <mads@kiilerich.com>
date Tue, 29 May 2018 12:25:41 +0200
parents d314edb04d11
children 552170092d06
files docs/api/api.rst kallithea/controllers/api/api.py kallithea/tests/api/api_base.py
diffstat 3 files changed, 43 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/docs/api/api.rst	Tue May 29 12:25:40 2018 +0200
+++ b/docs/api/api.rst	Tue May 29 12:25:41 2018 +0200
@@ -796,10 +796,12 @@
 create_repo
 ^^^^^^^^^^^
 
-Create a repository. If the repository name contains "/", all needed repository
-groups will be created. For example "foo/bar/baz" will create repository groups
-"foo", "bar" (with "foo" as parent), and create "baz" repository with
-"bar" as group.
+Create a repository. If the repository name contains "/", the repository will be
+created in the repository group indicated by that path. Any such repository
+groups need to exist before calling this method, or the call will fail.
+For example "foo/bar/baz" will create a repository "baz" inside the repository
+group "bar" which itself is in a repository group "foo", but both "foo" and
+"bar" already need to exist before calling this method.
 This command can only be executed using the api_key of a user with admin rights,
 or that of a regular user with create repository permission.
 Regular users cannot specify owner parameter.
--- a/kallithea/controllers/api/api.py	Tue May 29 12:25:40 2018 +0200
+++ b/kallithea/controllers/api/api.py	Tue May 29 12:25:41 2018 +0200
@@ -1350,9 +1350,9 @@
                     enable_downloads=Optional(False),
                     copy_permissions=Optional(False)):
         """
-        Creates a repository. If repository name contains "/", all needed repository
-        groups will be created. For example "foo/bar/baz" will create groups
-        "foo", "bar" (with "foo" as parent), and create "baz" repository with
+        Creates a repository. The repository name contains the full path, but the
+        parent repository group must exist. For example "foo/bar/baz" require the groups
+        "foo" and "bar" (with "foo" as parent), and create "baz" repository with
         "bar" as group. This command can be executed only using api_key
         belonging to user with admin rights or regular user that have create
         repository permission. Regular users cannot specify owner parameter
@@ -1432,11 +1432,15 @@
         copy_permissions = Optional.extract(copy_permissions)
 
         try:
-            repo_name_cleaned = repo_name.split('/')[-1]
-            # create structure of groups and return the last group
-            repo_group = map_groups(repo_name)
+            repo_name_parts = repo_name.split('/')
+            repo_group = None
+            if len(repo_name_parts) > 1:
+                group_name = '/'.join(repo_name_parts[:-1])
+                repo_group = RepoGroup.get_by_group_name(group_name)
+                if repo_group is None:
+                    raise JSONRPCError("repo group `%s` not found" % group_name)
             data = dict(
-                repo_name=repo_name_cleaned,
+                repo_name=repo_name_parts[-1],
                 repo_name_full=repo_name,
                 repo_type=repo_type,
                 repo_description=description,
@@ -1608,14 +1612,18 @@
         owner = get_user_or_error(owner)
 
         try:
-            # create structure of groups and return the last group
-            group = map_groups(fork_name)
-            fork_base_name = fork_name.rsplit('/', 1)[-1]
+            fork_name_parts = fork_name.split('/')
+            repo_group = None
+            if len(fork_name_parts) > 1:
+                group_name = '/'.join(fork_name_parts[:-1])
+                repo_group = RepoGroup.get_by_group_name(group_name)
+                if repo_group is None:
+                    raise JSONRPCError("repo group `%s` not found" % group_name)
 
             form_data = dict(
-                repo_name=fork_base_name,
+                repo_name=fork_name_parts[-1],
                 repo_name_full=fork_name,
-                repo_group=group,
+                repo_group=repo_group,
                 repo_type=repo.repo_type,
                 description=Optional.extract(description),
                 private=Optional.extract(private),
--- a/kallithea/tests/api/api_base.py	Tue May 29 12:25:40 2018 +0200
+++ b/kallithea/tests/api/api_base.py	Tue May 29 12:25:41 2018 +0200
@@ -1065,6 +1065,20 @@
         repo_group_name = u'my_gr'
         repo_name = u'%s/api-repo' % repo_group_name
 
+        # repo creation can no longer also create repo group
+        id_, params = _build_data(self.apikey, 'create_repo',
+                                  repo_name=repo_name,
+                                  owner=TEST_USER_ADMIN_LOGIN,
+                                  repo_type=self.REPO_TYPE,)
+        response = api_call(self, params)
+        expected = u'repo group `%s` not found' % repo_group_name
+        self._compare_error(id_, expected, given=response.body)
+        assert RepoModel().get_by_repo_name(repo_name) is None
+
+        # create group before creating repo
+        rg = fixture.create_repo_group(repo_group_name)
+        Session().commit()
+
         id_, params = _build_data(self.apikey, 'create_repo',
                                   repo_name=repo_name,
                                   owner=TEST_USER_ADMIN_LOGIN,
@@ -1191,7 +1205,7 @@
         self._compare_error(id_, expected, given=response.body)
 
     def test_api_create_repo_dot_dot(self):
-        # FIXME: it should only be possible to create repositories in existing repo groups - and '..' can't be used
+        # it is only possible to create repositories in existing repo groups - and '..' can't be used
         group_name = '%s/..' % TEST_REPO_GROUP
         repo_name = '%s/%s' % (group_name, 'could-be-outside')
         id_, params = _build_data(self.apikey, 'create_repo',
@@ -1199,12 +1213,8 @@
                                   owner=TEST_USER_ADMIN_LOGIN,
                                   repo_type=self.REPO_TYPE,)
         response = api_call(self, params)
-        expected = {
-            u'msg': u"Created new repository `%s`" % repo_name,
-            u'success': True,
-            u'task': None,
-        }
-        self._compare_ok(id_, expected, given=response.body)
+        expected = u'repo group `%s` not found' % group_name
+        self._compare_error(id_, expected, given=response.body)
         fixture.destroy_repo(repo_name)
 
     @mock.patch.object(RepoModel, 'create', crash)