Mercurial > kallithea
changeset 7317:64d41568507c stable
repos: introduce low level slug check of repo and group names
The high level web forms already slug-ify repo and repo group names. It might
thus not create the exact repo that was created, but the name will be "safe".
For API, we would rather have it fail than not doing exactly what was requested.
Thus, always verify at low level that the provided name wouldn't be modified by
slugification. This makes sure the API provide allow the same actual names as
the web UI.
This will only influence creation and renaming of repositories and repo groups.
Existing repositories will continue working as before.
This is a slight API change, but it makes the system more stable and can
prevent some security issues - especially XSS attacks.
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:59 +0200 |
parents | 7d5e8894db6c |
children | 4cca4cc6a0a9 |
files | kallithea/model/repo.py kallithea/model/repo_group.py kallithea/tests/api/api_base.py |
diffstat | 3 files changed, 15 insertions(+), 10 deletions(-) [+] |
line wrap: on
line diff
--- a/kallithea/model/repo.py Tue May 29 12:25:43 2018 +0200 +++ b/kallithea/model/repo.py Tue May 29 12:25:59 2018 +0200 @@ -33,6 +33,7 @@ from datetime import datetime from sqlalchemy.orm import subqueryload +import kallithea.lib.utils from kallithea.lib.utils import make_ui, is_valid_repo_uri from kallithea.lib.vcs.backends import get_backend from kallithea.lib.compat import json @@ -342,7 +343,10 @@ cur_repo.clone_uri = clone_uri if 'repo_name' in kwargs: - cur_repo.repo_name = cur_repo.get_new_name(kwargs['repo_name']) + repo_name = kwargs['repo_name'] + if kallithea.lib.utils.repo_name_slug(repo_name) != repo_name: + raise Exception('invalid repo name %s' % repo_name) + cur_repo.repo_name = cur_repo.get_new_name(repo_name) #if private flag is set, reset default permission to NONE if kwargs.get('repo_private'): @@ -393,6 +397,8 @@ # with name and path of group repo_name_full = repo_name repo_name = repo_name.split(self.URL_SEPARATOR)[-1] + if kallithea.lib.utils.repo_name_slug(repo_name) != repo_name: + raise Exception('invalid repo name %s' % repo_name) new_repo = Repository() new_repo.repo_state = state
--- a/kallithea/model/repo_group.py Tue May 29 12:25:43 2018 +0200 +++ b/kallithea/model/repo_group.py Tue May 29 12:25:59 2018 +0200 @@ -32,6 +32,7 @@ import shutil import datetime +import kallithea.lib.utils from kallithea.lib.utils2 import LazyProperty from kallithea.model import BaseModel @@ -145,6 +146,9 @@ def create(self, group_name, group_description, owner, parent=None, just_db=False, copy_permissions=False): try: + if kallithea.lib.utils.repo_name_slug(group_name) != group_name: + raise Exception('invalid repo group name %s' % group_name) + user = self._get_user(owner) parent_group = self._get_repo_group(parent) new_repo_group = RepoGroup() @@ -296,7 +300,10 @@ repo_group.enable_locking = form_data['enable_locking'] repo_group.parent_group = RepoGroup.get(form_data['group_parent_id']) - repo_group.group_name = repo_group.get_new_name(form_data['group_name']) + group_name = form_data['group_name'] + if kallithea.lib.utils.repo_name_slug(group_name) != group_name: + raise Exception('invalid repo group name %s' % group_name) + repo_group.group_name = repo_group.get_new_name(group_name) new_path = repo_group.full_path self.sa.add(repo_group)
--- a/kallithea/tests/api/api_base.py Tue May 29 12:25:43 2018 +0200 +++ b/kallithea/tests/api/api_base.py Tue May 29 12:25:59 2018 +0200 @@ -1016,14 +1016,6 @@ if repo_name == '/': expected = "repo group `` not found" self._compare_error(id_, expected, given=response.body) - elif repo_name in [':', '<test>']: - # FIXME: special characters and XSS injection should not be allowed - expected = { - 'msg': 'Created new repository `%s`' % repo_name, - 'success': True, - 'task': None, - } - self._compare_ok(id_, expected, given=response.body) else: expected = "failed to create repository `%s`" % repo_name self._compare_error(id_, expected, given=response.body)