diff --git a/rhodecode/apps/admin/__init__.py b/rhodecode/apps/admin/__init__.py --- a/rhodecode/apps/admin/__init__.py +++ b/rhodecode/apps/admin/__init__.py @@ -49,22 +49,31 @@ def admin_routes(config): config.add_route( 'admin_security', - pattern=ADMIN_PREFIX + '/security') + pattern='/security') config.add_view( AdminSecurityView, - attr='security' , + attr='security', route_name='admin_security', request_method='GET', renderer='rhodecode:templates/admin/security/security.mako') config.add_route( name='admin_security_update', - pattern=ADMIN_PREFIX + '/security/update') + pattern='/security/update') config.add_view( AdminSecurityView, attr='security_update', route_name='admin_security_update', request_method='POST', renderer='rhodecode:templates/admin/security/security.mako') + config.add_route( + name='admin_security_modify_allowed_vcs_client_versions', + pattern='/security/modify/allowed_vcs_client_versions') + config.add_view( + AdminSecurityView, + attr='vcs_whitelisted_client_versions_edit', + route_name='admin_security_modify_allowed_vcs_client_versions', request_method=('GET', 'POST'), + renderer='rhodecode:templates/admin/security/edit_allowed_vcs_client_versions.mako') + config.add_route( name='admin_audit_logs', diff --git a/rhodecode/apps/admin/views/security.py b/rhodecode/apps/admin/views/security.py --- a/rhodecode/apps/admin/views/security.py +++ b/rhodecode/apps/admin/views/security.py @@ -17,8 +17,13 @@ # and proprietary license terms, please see https://rhodecode.com/licenses/ import logging +import formencode +from rhodecode import BACKENDS from rhodecode.apps._base import BaseAppView +from rhodecode.model.meta import Session +from rhodecode.model.settings import SettingsModel +from rhodecode.model.forms import WhitelistedVcsClientsForm from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator log = logging.getLogger(__name__) @@ -37,3 +42,31 @@ class AdminSecurityView(BaseAppView): c.active = 'security' return self._get_template_context(c) + @LoginRequired() + @HasPermissionAllDecorator('hg.admin') + def vcs_whitelisted_client_versions_edit(self): + _ = self.request.translate + c = self.load_default_context() + render_ctx = {} + settings = SettingsModel() + form = WhitelistedVcsClientsForm(_, )() + if self.request.method == 'POST': + try: + result = form.to_python(self.request.POST) + for k, v in result.items(): + if v: + setting = settings.create_or_update_setting(name=f'{k}_allowed_clients', val=v) + Session().add(setting) + Session().commit() + + except formencode.Invalid as errors: + render_ctx.update({ + 'errors': errors.error_dict + }) + for key in BACKENDS.keys(): + verbose_name = f"initial_{key}" + if existing := settings.get_setting_by_name(name=f'{key}_allowed_clients'): + render_ctx[verbose_name] = existing.app_settings_value + else: + render_ctx[verbose_name] = '*' + return self._get_template_context(c, **render_ctx) diff --git a/rhodecode/lib/exceptions.py b/rhodecode/lib/exceptions.py --- a/rhodecode/lib/exceptions.py +++ b/rhodecode/lib/exceptions.py @@ -102,6 +102,11 @@ class HTTPRequirementError(HTTPClientErr self.args = (message, ) +class ClientNotSupportedError(HTTPRequirementError): + title = explanation = 'Client Not Supported' + reason = None + + class HTTPLockedRC(HTTPClientError): """ Special Exception For locked Repos in RhodeCode, the return code can diff --git a/rhodecode/lib/hooks_base.py b/rhodecode/lib/hooks_base.py --- a/rhodecode/lib/hooks_base.py +++ b/rhodecode/lib/hooks_base.py @@ -30,7 +30,7 @@ from rhodecode.lib import helpers as h from rhodecode.lib import audit_logger from rhodecode.lib.utils2 import safe_str, user_agent_normalizer from rhodecode.lib.exceptions import ( - HTTPLockedRC, HTTPBranchProtected, UserCreationError) + HTTPLockedRC, HTTPBranchProtected, UserCreationError, ClientNotSupportedError) from rhodecode.model.db import Repository, User from rhodecode.lib.statsd_client import StatsdClient @@ -64,6 +64,18 @@ def is_shadow_repo(extras): return extras['is_shadow_repo'] +def check_vcs_client(extras): + """ + Checks if vcs client is allowed (Only works in enterprise edition) + """ + try: + from rc_ee.lib.security.utils import is_vcs_client_whitelisted + except ModuleNotFoundError: + is_vcs_client_whitelisted = lambda *x: True + backend = extras.get('scm') + if not is_vcs_client_whitelisted(extras.get('user_agent'), backend): + raise ClientNotSupportedError(f"Your {backend} client is forbidden") + def _get_scm_size(alias, root_path): if not alias.startswith('.'): @@ -108,6 +120,7 @@ def pre_push(extras): It bans pushing when the repository is locked. """ + check_vcs_client(extras) user = User.get_by_username(extras.username) output = '' if extras.locked_by[0] and user.user_id != int(extras.locked_by[0]): @@ -180,6 +193,7 @@ def pre_pull(extras): It bans pulling when the repository is locked. """ + check_vcs_client(extras) output = '' if extras.locked_by[0]: locked_by = User.get(extras.locked_by[0]).username diff --git a/rhodecode/lib/utils.py b/rhodecode/lib/utils.py --- a/rhodecode/lib/utils.py +++ b/rhodecode/lib/utils.py @@ -84,8 +84,11 @@ def adopt_for_celery(func): @wraps(func) def wrapper(extras): extras = AttributeDict(extras) - # HooksResponse implements to_json method which must be used there. - return func(extras).to_json() + try: + # HooksResponse implements to_json method which must be used there. + return func(extras).to_json() + except Exception as e: + return {'status': 128, 'exception': type(e).__name__, 'exception_args': e.args} return wrapper diff --git a/rhodecode/model/forms.py b/rhodecode/model/forms.py --- a/rhodecode/model/forms.py +++ b/rhodecode/model/forms.py @@ -129,6 +129,20 @@ def TOTPForm(localizer, user, allow_reco return _TOTPForm +def WhitelistedVcsClientsForm(localizer): + _ = localizer + + class _WhitelistedVcsClientsForm(formencode.Schema): + regexp = r'^(?:\s*[<>=~^!]*\s*\d{1,2}\.\d{1,2}(?:\.\d{1,2})?\s*|\*)\s*(?:,\s*[<>=~^!]*\s*\d{1,2}\.\d{1,2}(?:\.\d{1,2})?\s*|\s*\*\s*)*$' + allow_extra_fields = True + filter_extra_fields = True + git = v.Regex(regexp) + hg = v.Regex(regexp) + svn = v.Regex(regexp) + + return _WhitelistedVcsClientsForm + + def UserForm(localizer, edit=False, available_languages=None, old_data=None): old_data = old_data or {} available_languages = available_languages or [] diff --git a/rhodecode/public/js/rhodecode/routes.js b/rhodecode/public/js/rhodecode/routes.js --- a/rhodecode/public/js/rhodecode/routes.js +++ b/rhodecode/public/js/rhodecode/routes.js @@ -86,6 +86,7 @@ function registerRCRoutes() { pyroutes.register('admin_settings_vcs_update', '/_admin/settings/vcs/update', []); pyroutes.register('admin_settings_visual', '/_admin/settings/visual', []); pyroutes.register('admin_settings_visual_update', '/_admin/settings/visual/update', []); + pyroutes.register('admin_security_modify_allowed_vcs_client_versions', '/_admin/security/modify/allowed_vcs_client_versions', []); pyroutes.register('apiv2', '/_admin/api', []); pyroutes.register('atom_feed_home', '/%(repo_name)s/feed-atom', ['repo_name']); pyroutes.register('atom_feed_home_old', '/%(repo_name)s/feed/atom', ['repo_name']); diff --git a/rhodecode/templates/admin/security/edit_allowed_vcs_client_versions.mako b/rhodecode/templates/admin/security/edit_allowed_vcs_client_versions.mako new file mode 100644 --- /dev/null +++ b/rhodecode/templates/admin/security/edit_allowed_vcs_client_versions.mako @@ -0,0 +1,62 @@ + + +
+ ${h.text('git', class_="form-control", value=initial_git)}
++ ${h.text('hg', class_="form-control", value=initial_hg)}
++ ${h.text('svn', class_="form-control", value=initial_svn)}
+ %for k, v in errors.items(): + +${_('Set rules for allowed git, hg or svn client versions. You can set exact version (for example 2.0.9) or use comparison operators to set earliest or latest version (>=2.6.0)')}
+ + ${h.submit('send', _('Save'), class_="btn btn-primary")} + ${h.end_form()} +- You can scan your repositories for exposed secrets, passwords, etc + ${_('You can scan your repositories for exposed secrets, passwords, etc')}
+ ${_('Some outdated client versions may have security vulnerabilities. This section have rules for whitelisting versions of clients for Git, Mercurial and SVN.')} +
+ %else: +