##// END OF EJS Templates
forks: prevent XSS in datagrid of forks data.
marcink -
r2996:7441eff4 default
parent child Browse files
Show More
@@ -1,263 +1,263 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2018 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22 import datetime
23 23 import formencode
24 24 import formencode.htmlfill
25 25
26 26 from pyramid.httpexceptions import HTTPFound
27 27 from pyramid.view import view_config
28 28 from pyramid.renderers import render
29 29 from pyramid.response import Response
30 30
31 31 from rhodecode import events
32 32 from rhodecode.apps._base import RepoAppView, DataGridAppView
33 33 from rhodecode.lib.auth import (
34 34 LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous,
35 35 HasRepoPermissionAny, HasPermissionAnyDecorator, CSRFRequired)
36 36 import rhodecode.lib.helpers as h
37 37 from rhodecode.lib.celerylib.utils import get_task_id
38 38 from rhodecode.model.db import coalesce, or_, Repository, RepoGroup
39 39 from rhodecode.model.repo import RepoModel
40 40 from rhodecode.model.forms import RepoForkForm
41 41 from rhodecode.model.scm import ScmModel, RepoGroupList
42 42 from rhodecode.lib.utils2 import safe_int, safe_unicode
43 43
44 44 log = logging.getLogger(__name__)
45 45
46 46
47 47 class RepoForksView(RepoAppView, DataGridAppView):
48 48
49 49 def load_default_context(self):
50 50 c = self._get_local_tmpl_context(include_app_defaults=True)
51 51 c.rhodecode_repo = self.rhodecode_vcs_repo
52 52
53 53 acl_groups = RepoGroupList(
54 54 RepoGroup.query().all(),
55 55 perm_set=['group.write', 'group.admin'])
56 56 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
57 57 c.repo_groups_choices = map(lambda k: safe_unicode(k[0]), c.repo_groups)
58 58 choices, c.landing_revs = ScmModel().get_repo_landing_revs(
59 59 self.request.translate)
60 60 c.landing_revs_choices = choices
61 61 c.personal_repo_group = c.rhodecode_user.personal_repo_group
62 62
63 63 return c
64 64
65 65 @LoginRequired()
66 66 @HasRepoPermissionAnyDecorator(
67 67 'repository.read', 'repository.write', 'repository.admin')
68 68 @view_config(
69 69 route_name='repo_forks_show_all', request_method='GET',
70 70 renderer='rhodecode:templates/forks/forks.mako')
71 71 def repo_forks_show_all(self):
72 72 c = self.load_default_context()
73 73 return self._get_template_context(c)
74 74
75 75 @LoginRequired()
76 76 @HasRepoPermissionAnyDecorator(
77 77 'repository.read', 'repository.write', 'repository.admin')
78 78 @view_config(
79 79 route_name='repo_forks_data', request_method='GET',
80 80 renderer='json_ext', xhr=True)
81 81 def repo_forks_data(self):
82 82 _ = self.request.translate
83 83 self.load_default_context()
84 84 column_map = {
85 85 'fork_name': 'repo_name',
86 86 'fork_date': 'created_on',
87 87 'last_activity': 'updated_on'
88 88 }
89 89 draw, start, limit = self._extract_chunk(self.request)
90 90 search_q, order_by, order_dir = self._extract_ordering(
91 91 self.request, column_map=column_map)
92 92
93 93 acl_check = HasRepoPermissionAny(
94 94 'repository.read', 'repository.write', 'repository.admin')
95 95 repo_id = self.db_repo.repo_id
96 96 allowed_ids = [-1]
97 97 for f in Repository.query().filter(Repository.fork_id == repo_id):
98 98 if acl_check(f.repo_name, 'get forks check'):
99 99 allowed_ids.append(f.repo_id)
100 100
101 101 forks_data_total_count = Repository.query()\
102 102 .filter(Repository.fork_id == repo_id)\
103 103 .filter(Repository.repo_id.in_(allowed_ids))\
104 104 .count()
105 105
106 106 # json generate
107 107 base_q = Repository.query()\
108 108 .filter(Repository.fork_id == repo_id)\
109 109 .filter(Repository.repo_id.in_(allowed_ids))\
110 110
111 111 if search_q:
112 112 like_expression = u'%{}%'.format(safe_unicode(search_q))
113 113 base_q = base_q.filter(or_(
114 114 Repository.repo_name.ilike(like_expression),
115 115 Repository.description.ilike(like_expression),
116 116 ))
117 117
118 118 forks_data_total_filtered_count = base_q.count()
119 119
120 120 sort_col = getattr(Repository, order_by, None)
121 121 if sort_col:
122 122 if order_dir == 'asc':
123 123 # handle null values properly to order by NULL last
124 124 if order_by in ['last_activity']:
125 125 sort_col = coalesce(sort_col, datetime.date.max)
126 126 sort_col = sort_col.asc()
127 127 else:
128 128 # handle null values properly to order by NULL last
129 129 if order_by in ['last_activity']:
130 130 sort_col = coalesce(sort_col, datetime.date.min)
131 131 sort_col = sort_col.desc()
132 132
133 133 base_q = base_q.order_by(sort_col)
134 134 base_q = base_q.offset(start).limit(limit)
135 135
136 136 fork_list = base_q.all()
137 137
138 138 def fork_actions(fork):
139 139 url_link = h.route_path(
140 140 'repo_compare',
141 141 repo_name=fork.repo_name,
142 142 source_ref_type=self.db_repo.landing_rev[0],
143 143 source_ref=self.db_repo.landing_rev[1],
144 144 target_ref_type=self.db_repo.landing_rev[0],
145 145 target_ref=self.db_repo.landing_rev[1],
146 146 _query=dict(merge=1, target_repo=f.repo_name))
147 147 return h.link_to(_('Compare fork'), url_link, class_='btn-link')
148 148
149 149 def fork_name(fork):
150 150 return h.link_to(fork.repo_name,
151 151 h.route_path('repo_summary', repo_name=fork.repo_name))
152 152
153 153 forks_data = []
154 154 for fork in fork_list:
155 155 forks_data.append({
156 156 "username": h.gravatar_with_user(self.request, fork.user.username),
157 157 "fork_name": fork_name(fork),
158 "description": fork.description,
158 "description": fork.description_safe,
159 159 "fork_date": h.age_component(fork.created_on, time_is_local=True),
160 160 "last_activity": h.format_date(fork.updated_on),
161 161 "action": fork_actions(fork),
162 162 })
163 163
164 164 data = ({
165 165 'draw': draw,
166 166 'data': forks_data,
167 167 'recordsTotal': forks_data_total_count,
168 168 'recordsFiltered': forks_data_total_filtered_count,
169 169 })
170 170
171 171 return data
172 172
173 173 @LoginRequired()
174 174 @NotAnonymous()
175 175 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
176 176 @HasRepoPermissionAnyDecorator(
177 177 'repository.read', 'repository.write', 'repository.admin')
178 178 @view_config(
179 179 route_name='repo_fork_new', request_method='GET',
180 180 renderer='rhodecode:templates/forks/forks.mako')
181 181 def repo_fork_new(self):
182 182 c = self.load_default_context()
183 183
184 184 defaults = RepoModel()._get_defaults(self.db_repo_name)
185 185 # alter the description to indicate a fork
186 186 defaults['description'] = (
187 187 'fork of repository: %s \n%s' % (
188 188 defaults['repo_name'], defaults['description']))
189 189 # add suffix to fork
190 190 defaults['repo_name'] = '%s-fork' % defaults['repo_name']
191 191
192 192 data = render('rhodecode:templates/forks/fork.mako',
193 193 self._get_template_context(c), self.request)
194 194 html = formencode.htmlfill.render(
195 195 data,
196 196 defaults=defaults,
197 197 encoding="UTF-8",
198 198 force_defaults=False
199 199 )
200 200 return Response(html)
201 201
202 202 @LoginRequired()
203 203 @NotAnonymous()
204 204 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
205 205 @HasRepoPermissionAnyDecorator(
206 206 'repository.read', 'repository.write', 'repository.admin')
207 207 @CSRFRequired()
208 208 @view_config(
209 209 route_name='repo_fork_create', request_method='POST',
210 210 renderer='rhodecode:templates/forks/fork.mako')
211 211 def repo_fork_create(self):
212 212 _ = self.request.translate
213 213 c = self.load_default_context()
214 214
215 215 _form = RepoForkForm(self.request.translate, old_data={'repo_type': self.db_repo.repo_type},
216 216 repo_groups=c.repo_groups_choices,
217 217 landing_revs=c.landing_revs_choices)()
218 218 post_data = dict(self.request.POST)
219 219
220 220 # forbid injecting other repo by forging a request
221 221 post_data['fork_parent_id'] = self.db_repo.repo_id
222 222
223 223 form_result = {}
224 224 task_id = None
225 225 try:
226 226 form_result = _form.to_python(post_data)
227 227 # create fork is done sometimes async on celery, db transaction
228 228 # management is handled there.
229 229 task = RepoModel().create_fork(
230 230 form_result, c.rhodecode_user.user_id)
231 231
232 232 task_id = get_task_id(task)
233 233 except formencode.Invalid as errors:
234 234 c.rhodecode_db_repo = self.db_repo
235 235
236 236 data = render('rhodecode:templates/forks/fork.mako',
237 237 self._get_template_context(c), self.request)
238 238 html = formencode.htmlfill.render(
239 239 data,
240 240 defaults=errors.value,
241 241 errors=errors.error_dict or {},
242 242 prefix_error=False,
243 243 encoding="UTF-8",
244 244 force_defaults=False
245 245 )
246 246 return Response(html)
247 247 except Exception:
248 248 log.exception(
249 249 u'Exception while trying to fork the repository %s',
250 250 self.db_repo_name)
251 251 msg = (
252 252 _('An error occurred during repository forking %s') % (
253 253 self.db_repo_name, ))
254 254 h.flash(msg, category='error')
255 255
256 256 repo_name = form_result.get('repo_name_full', self.db_repo_name)
257 257
258 258 events.trigger(events.UserPermissionsChange([self._rhodecode_user.user_id]))
259 259
260 260 raise HTTPFound(
261 261 h.route_path('repo_creating',
262 262 repo_name=repo_name,
263 263 _query=dict(task_id=task_id)))
General Comments 0
You need to be logged in to leave comments. Login now