##// END OF EJS Templates
core: use application wide detection of invalid bytes sent via URL/GET/POST data.
marcink -
r3145:f9f61897 default
parent child Browse files
Show More
@@ -0,0 +1,45 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2018 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import pytest
22
23 from rhodecode.tests import TestController
24 from rhodecode.tests.fixture import Fixture
25
26
27 class TestBadRequestData(TestController):
28
29 def test_bad_get_data(self):
30 self.app.get(
31 '/', params={'f\xfc': '\xfc%f6%22%20onmouseover%3dveA2(9352)%20'},
32 status=400)
33
34 def test_bad_url_data(self):
35 self.app.post(
36 '/f\xfc',
37 status=400)
38
39 def test_bad_post_data(self, csrf_token, xhr_header):
40 self.app.post(
41 '/_markup_preview',
42 params={'f\xfc': '\xfc%f6%22%20onmouseover%3dveA2(9352)%20',
43 'csrf_token': csrf_token},
44 extra_environ=xhr_header,
45 status=400)
@@ -1,341 +1,327 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2018 RhodeCode GmbH
3 # Copyright (C) 2010-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 import io
20 import io
21 import re
21 import re
22 import os
22 import os
23 import datetime
23 import datetime
24 import logging
24 import logging
25 import Queue
25 import Queue
26 import subprocess32
26 import subprocess32
27
27
28
28
29 from dateutil.parser import parse
29 from dateutil.parser import parse
30 from pyramid.httpexceptions import HTTPBadRequest
31 from pyramid.threadlocal import get_current_request
30 from pyramid.threadlocal import get_current_request
32 from pyramid.interfaces import IRoutesMapper
31 from pyramid.interfaces import IRoutesMapper
33 from pyramid.settings import asbool
32 from pyramid.settings import asbool
34 from pyramid.path import AssetResolver
33 from pyramid.path import AssetResolver
35 from threading import Thread
34 from threading import Thread
36
35
37 from rhodecode.translation import _ as tsf
36 from rhodecode.translation import _ as tsf
38 from rhodecode.config.jsroutes import generate_jsroutes_content
37 from rhodecode.config.jsroutes import generate_jsroutes_content
39 from rhodecode.lib import auth
38 from rhodecode.lib import auth
40 from rhodecode.lib.base import get_auth_user
39 from rhodecode.lib.base import get_auth_user
41
40
42
43 import rhodecode
41 import rhodecode
44
42
45
43
46 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
47
45
48
46
49 def add_renderer_globals(event):
47 def add_renderer_globals(event):
50 from rhodecode.lib import helpers
48 from rhodecode.lib import helpers
51
49
52 # TODO: When executed in pyramid view context the request is not available
50 # TODO: When executed in pyramid view context the request is not available
53 # in the event. Find a better solution to get the request.
51 # in the event. Find a better solution to get the request.
54 request = event['request'] or get_current_request()
52 request = event['request'] or get_current_request()
55
53
56 # Add Pyramid translation as '_' to context
54 # Add Pyramid translation as '_' to context
57 event['_'] = request.translate
55 event['_'] = request.translate
58 event['_ungettext'] = request.plularize
56 event['_ungettext'] = request.plularize
59 event['h'] = helpers
57 event['h'] = helpers
60
58
61
59
62 def add_localizer(event):
60 def add_localizer(event):
63 request = event.request
61 request = event.request
64 try:
62 localizer = request.localizer
65 localizer = request.localizer
66 except Exception:
67 log.exception('Failed to get localizer')
68 # NOTE(marcink): Handle bug in pyramid by malformed GET params could crash
69 # this resulting on various odd errors on missing translators
70 # see: https://github.com/Pylons/pyramid/issues/3399
71
72 def dummy_translate(*args, **kwargs):
73 return ''.join(args)
74 request.translate = tsf
75 request.plularize = dummy_translate
76 raise HTTPBadRequest()
77
63
78 def auto_translate(*args, **kwargs):
64 def auto_translate(*args, **kwargs):
79 return localizer.translate(tsf(*args, **kwargs))
65 return localizer.translate(tsf(*args, **kwargs))
80
66
81 request.translate = auto_translate
67 request.translate = auto_translate
82 request.plularize = localizer.pluralize
68 request.plularize = localizer.pluralize
83
69
84
70
85 def set_user_lang(event):
71 def set_user_lang(event):
86 request = event.request
72 request = event.request
87 cur_user = getattr(request, 'user', None)
73 cur_user = getattr(request, 'user', None)
88
74
89 if cur_user:
75 if cur_user:
90 user_lang = cur_user.get_instance().user_data.get('language')
76 user_lang = cur_user.get_instance().user_data.get('language')
91 if user_lang:
77 if user_lang:
92 log.debug('lang: setting current user:%s language to: %s', cur_user, user_lang)
78 log.debug('lang: setting current user:%s language to: %s', cur_user, user_lang)
93 event.request._LOCALE_ = user_lang
79 event.request._LOCALE_ = user_lang
94
80
95
81
96 def add_request_user_context(event):
82 def add_request_user_context(event):
97 """
83 """
98 Adds auth user into request context
84 Adds auth user into request context
99 """
85 """
100 request = event.request
86 request = event.request
101 # access req_id as soon as possible
87 # access req_id as soon as possible
102 req_id = request.req_id
88 req_id = request.req_id
103
89
104 if hasattr(request, 'vcs_call'):
90 if hasattr(request, 'vcs_call'):
105 # skip vcs calls
91 # skip vcs calls
106 return
92 return
107
93
108 if hasattr(request, 'rpc_method'):
94 if hasattr(request, 'rpc_method'):
109 # skip api calls
95 # skip api calls
110 return
96 return
111
97
112 auth_user = get_auth_user(request)
98 auth_user = get_auth_user(request)
113 request.user = auth_user
99 request.user = auth_user
114 request.environ['rc_auth_user'] = auth_user
100 request.environ['rc_auth_user'] = auth_user
115 request.environ['rc_auth_user_id'] = auth_user.user_id
101 request.environ['rc_auth_user_id'] = auth_user.user_id
116 request.environ['rc_req_id'] = req_id
102 request.environ['rc_req_id'] = req_id
117
103
118
104
119 def inject_app_settings(event):
105 def inject_app_settings(event):
120 settings = event.app.registry.settings
106 settings = event.app.registry.settings
121 # inject info about available permissions
107 # inject info about available permissions
122 auth.set_available_permissions(settings)
108 auth.set_available_permissions(settings)
123
109
124
110
125 def scan_repositories_if_enabled(event):
111 def scan_repositories_if_enabled(event):
126 """
112 """
127 This is subscribed to the `pyramid.events.ApplicationCreated` event. It
113 This is subscribed to the `pyramid.events.ApplicationCreated` event. It
128 does a repository scan if enabled in the settings.
114 does a repository scan if enabled in the settings.
129 """
115 """
130 settings = event.app.registry.settings
116 settings = event.app.registry.settings
131 vcs_server_enabled = settings['vcs.server.enable']
117 vcs_server_enabled = settings['vcs.server.enable']
132 import_on_startup = settings['startup.import_repos']
118 import_on_startup = settings['startup.import_repos']
133 if vcs_server_enabled and import_on_startup:
119 if vcs_server_enabled and import_on_startup:
134 from rhodecode.model.scm import ScmModel
120 from rhodecode.model.scm import ScmModel
135 from rhodecode.lib.utils import repo2db_mapper, get_rhodecode_base_path
121 from rhodecode.lib.utils import repo2db_mapper, get_rhodecode_base_path
136 repositories = ScmModel().repo_scan(get_rhodecode_base_path())
122 repositories = ScmModel().repo_scan(get_rhodecode_base_path())
137 repo2db_mapper(repositories, remove_obsolete=False)
123 repo2db_mapper(repositories, remove_obsolete=False)
138
124
139
125
140 def write_metadata_if_needed(event):
126 def write_metadata_if_needed(event):
141 """
127 """
142 Writes upgrade metadata
128 Writes upgrade metadata
143 """
129 """
144 import rhodecode
130 import rhodecode
145 from rhodecode.lib import system_info
131 from rhodecode.lib import system_info
146 from rhodecode.lib import ext_json
132 from rhodecode.lib import ext_json
147
133
148 fname = '.rcmetadata.json'
134 fname = '.rcmetadata.json'
149 ini_loc = os.path.dirname(rhodecode.CONFIG.get('__file__'))
135 ini_loc = os.path.dirname(rhodecode.CONFIG.get('__file__'))
150 metadata_destination = os.path.join(ini_loc, fname)
136 metadata_destination = os.path.join(ini_loc, fname)
151
137
152 def get_update_age():
138 def get_update_age():
153 now = datetime.datetime.utcnow()
139 now = datetime.datetime.utcnow()
154
140
155 with open(metadata_destination, 'rb') as f:
141 with open(metadata_destination, 'rb') as f:
156 data = ext_json.json.loads(f.read())
142 data = ext_json.json.loads(f.read())
157 if 'created_on' in data:
143 if 'created_on' in data:
158 update_date = parse(data['created_on'])
144 update_date = parse(data['created_on'])
159 diff = now - update_date
145 diff = now - update_date
160 return diff.total_seconds() / 60.0
146 return diff.total_seconds() / 60.0
161
147
162 return 0
148 return 0
163
149
164 def write():
150 def write():
165 configuration = system_info.SysInfo(
151 configuration = system_info.SysInfo(
166 system_info.rhodecode_config)()['value']
152 system_info.rhodecode_config)()['value']
167 license_token = configuration['config']['license_token']
153 license_token = configuration['config']['license_token']
168
154
169 setup = dict(
155 setup = dict(
170 workers=configuration['config']['server:main'].get(
156 workers=configuration['config']['server:main'].get(
171 'workers', '?'),
157 'workers', '?'),
172 worker_type=configuration['config']['server:main'].get(
158 worker_type=configuration['config']['server:main'].get(
173 'worker_class', 'sync'),
159 'worker_class', 'sync'),
174 )
160 )
175 dbinfo = system_info.SysInfo(system_info.database_info)()['value']
161 dbinfo = system_info.SysInfo(system_info.database_info)()['value']
176 del dbinfo['url']
162 del dbinfo['url']
177
163
178 metadata = dict(
164 metadata = dict(
179 desc='upgrade metadata info',
165 desc='upgrade metadata info',
180 license_token=license_token,
166 license_token=license_token,
181 created_on=datetime.datetime.utcnow().isoformat(),
167 created_on=datetime.datetime.utcnow().isoformat(),
182 usage=system_info.SysInfo(system_info.usage_info)()['value'],
168 usage=system_info.SysInfo(system_info.usage_info)()['value'],
183 platform=system_info.SysInfo(system_info.platform_type)()['value'],
169 platform=system_info.SysInfo(system_info.platform_type)()['value'],
184 database=dbinfo,
170 database=dbinfo,
185 cpu=system_info.SysInfo(system_info.cpu)()['value'],
171 cpu=system_info.SysInfo(system_info.cpu)()['value'],
186 memory=system_info.SysInfo(system_info.memory)()['value'],
172 memory=system_info.SysInfo(system_info.memory)()['value'],
187 setup=setup
173 setup=setup
188 )
174 )
189
175
190 with open(metadata_destination, 'wb') as f:
176 with open(metadata_destination, 'wb') as f:
191 f.write(ext_json.json.dumps(metadata))
177 f.write(ext_json.json.dumps(metadata))
192
178
193 settings = event.app.registry.settings
179 settings = event.app.registry.settings
194 if settings.get('metadata.skip'):
180 if settings.get('metadata.skip'):
195 return
181 return
196
182
197 # only write this every 24h, workers restart caused unwanted delays
183 # only write this every 24h, workers restart caused unwanted delays
198 try:
184 try:
199 age_in_min = get_update_age()
185 age_in_min = get_update_age()
200 except Exception:
186 except Exception:
201 age_in_min = 0
187 age_in_min = 0
202
188
203 if age_in_min > 60 * 60 * 24:
189 if age_in_min > 60 * 60 * 24:
204 return
190 return
205
191
206 try:
192 try:
207 write()
193 write()
208 except Exception:
194 except Exception:
209 pass
195 pass
210
196
211
197
212 def write_js_routes_if_enabled(event):
198 def write_js_routes_if_enabled(event):
213 registry = event.app.registry
199 registry = event.app.registry
214
200
215 mapper = registry.queryUtility(IRoutesMapper)
201 mapper = registry.queryUtility(IRoutesMapper)
216 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
202 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
217
203
218 def _extract_route_information(route):
204 def _extract_route_information(route):
219 """
205 """
220 Convert a route into tuple(name, path, args), eg:
206 Convert a route into tuple(name, path, args), eg:
221 ('show_user', '/profile/%(username)s', ['username'])
207 ('show_user', '/profile/%(username)s', ['username'])
222 """
208 """
223
209
224 routepath = route.pattern
210 routepath = route.pattern
225 pattern = route.pattern
211 pattern = route.pattern
226
212
227 def replace(matchobj):
213 def replace(matchobj):
228 if matchobj.group(1):
214 if matchobj.group(1):
229 return "%%(%s)s" % matchobj.group(1).split(':')[0]
215 return "%%(%s)s" % matchobj.group(1).split(':')[0]
230 else:
216 else:
231 return "%%(%s)s" % matchobj.group(2)
217 return "%%(%s)s" % matchobj.group(2)
232
218
233 routepath = _argument_prog.sub(replace, routepath)
219 routepath = _argument_prog.sub(replace, routepath)
234
220
235 if not routepath.startswith('/'):
221 if not routepath.startswith('/'):
236 routepath = '/'+routepath
222 routepath = '/'+routepath
237
223
238 return (
224 return (
239 route.name,
225 route.name,
240 routepath,
226 routepath,
241 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
227 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
242 for arg in _argument_prog.findall(pattern)]
228 for arg in _argument_prog.findall(pattern)]
243 )
229 )
244
230
245 def get_routes():
231 def get_routes():
246 # pyramid routes
232 # pyramid routes
247 for route in mapper.get_routes():
233 for route in mapper.get_routes():
248 if not route.name.startswith('__'):
234 if not route.name.startswith('__'):
249 yield _extract_route_information(route)
235 yield _extract_route_information(route)
250
236
251 if asbool(registry.settings.get('generate_js_files', 'false')):
237 if asbool(registry.settings.get('generate_js_files', 'false')):
252 static_path = AssetResolver().resolve('rhodecode:public').abspath()
238 static_path = AssetResolver().resolve('rhodecode:public').abspath()
253 jsroutes = get_routes()
239 jsroutes = get_routes()
254 jsroutes_file_content = generate_jsroutes_content(jsroutes)
240 jsroutes_file_content = generate_jsroutes_content(jsroutes)
255 jsroutes_file_path = os.path.join(
241 jsroutes_file_path = os.path.join(
256 static_path, 'js', 'rhodecode', 'routes.js')
242 static_path, 'js', 'rhodecode', 'routes.js')
257
243
258 try:
244 try:
259 with io.open(jsroutes_file_path, 'w', encoding='utf-8') as f:
245 with io.open(jsroutes_file_path, 'w', encoding='utf-8') as f:
260 f.write(jsroutes_file_content)
246 f.write(jsroutes_file_content)
261 except Exception:
247 except Exception:
262 log.exception('Failed to write routes.js into %s', jsroutes_file_path)
248 log.exception('Failed to write routes.js into %s', jsroutes_file_path)
263
249
264
250
265 class Subscriber(object):
251 class Subscriber(object):
266 """
252 """
267 Base class for subscribers to the pyramid event system.
253 Base class for subscribers to the pyramid event system.
268 """
254 """
269 def __call__(self, event):
255 def __call__(self, event):
270 self.run(event)
256 self.run(event)
271
257
272 def run(self, event):
258 def run(self, event):
273 raise NotImplementedError('Subclass has to implement this.')
259 raise NotImplementedError('Subclass has to implement this.')
274
260
275
261
276 class AsyncSubscriber(Subscriber):
262 class AsyncSubscriber(Subscriber):
277 """
263 """
278 Subscriber that handles the execution of events in a separate task to not
264 Subscriber that handles the execution of events in a separate task to not
279 block the execution of the code which triggers the event. It puts the
265 block the execution of the code which triggers the event. It puts the
280 received events into a queue from which the worker process takes them in
266 received events into a queue from which the worker process takes them in
281 order.
267 order.
282 """
268 """
283 def __init__(self):
269 def __init__(self):
284 self._stop = False
270 self._stop = False
285 self._eventq = Queue.Queue()
271 self._eventq = Queue.Queue()
286 self._worker = self.create_worker()
272 self._worker = self.create_worker()
287 self._worker.start()
273 self._worker.start()
288
274
289 def __call__(self, event):
275 def __call__(self, event):
290 self._eventq.put(event)
276 self._eventq.put(event)
291
277
292 def create_worker(self):
278 def create_worker(self):
293 worker = Thread(target=self.do_work)
279 worker = Thread(target=self.do_work)
294 worker.daemon = True
280 worker.daemon = True
295 return worker
281 return worker
296
282
297 def stop_worker(self):
283 def stop_worker(self):
298 self._stop = False
284 self._stop = False
299 self._eventq.put(None)
285 self._eventq.put(None)
300 self._worker.join()
286 self._worker.join()
301
287
302 def do_work(self):
288 def do_work(self):
303 while not self._stop:
289 while not self._stop:
304 event = self._eventq.get()
290 event = self._eventq.get()
305 if event is not None:
291 if event is not None:
306 self.run(event)
292 self.run(event)
307
293
308
294
309 class AsyncSubprocessSubscriber(AsyncSubscriber):
295 class AsyncSubprocessSubscriber(AsyncSubscriber):
310 """
296 """
311 Subscriber that uses the subprocess32 module to execute a command if an
297 Subscriber that uses the subprocess32 module to execute a command if an
312 event is received. Events are handled asynchronously.
298 event is received. Events are handled asynchronously.
313 """
299 """
314
300
315 def __init__(self, cmd, timeout=None):
301 def __init__(self, cmd, timeout=None):
316 super(AsyncSubprocessSubscriber, self).__init__()
302 super(AsyncSubprocessSubscriber, self).__init__()
317 self._cmd = cmd
303 self._cmd = cmd
318 self._timeout = timeout
304 self._timeout = timeout
319
305
320 def run(self, event):
306 def run(self, event):
321 cmd = self._cmd
307 cmd = self._cmd
322 timeout = self._timeout
308 timeout = self._timeout
323 log.debug('Executing command %s.', cmd)
309 log.debug('Executing command %s.', cmd)
324
310
325 try:
311 try:
326 output = subprocess32.check_output(
312 output = subprocess32.check_output(
327 cmd, timeout=timeout, stderr=subprocess32.STDOUT)
313 cmd, timeout=timeout, stderr=subprocess32.STDOUT)
328 log.debug('Command finished %s', cmd)
314 log.debug('Command finished %s', cmd)
329 if output:
315 if output:
330 log.debug('Command output: %s', output)
316 log.debug('Command output: %s', output)
331 except subprocess32.TimeoutExpired as e:
317 except subprocess32.TimeoutExpired as e:
332 log.exception('Timeout while executing command.')
318 log.exception('Timeout while executing command.')
333 if e.output:
319 if e.output:
334 log.error('Command output: %s', e.output)
320 log.error('Command output: %s', e.output)
335 except subprocess32.CalledProcessError as e:
321 except subprocess32.CalledProcessError as e:
336 log.exception('Error while executing command.')
322 log.exception('Error while executing command.')
337 if e.output:
323 if e.output:
338 log.error('Command output: %s', e.output)
324 log.error('Command output: %s', e.output)
339 except:
325 except:
340 log.exception(
326 log.exception(
341 'Exception while executing command %s.', cmd)
327 'Exception while executing command %s.', cmd)
@@ -1,69 +1,117 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2018 RhodeCode GmbH
3 # Copyright (C) 2010-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 import logging
22 import logging
23 from pyramid.httpexceptions import HTTPException, HTTPBadRequest
23
24
24 from rhodecode.lib.middleware.vcs import (
25 from rhodecode.lib.middleware.vcs import (
25 detect_vcs_request, VCS_TYPE_KEY, VCS_TYPE_SKIP)
26 detect_vcs_request, VCS_TYPE_KEY, VCS_TYPE_SKIP)
26
27
27
28
28 log = logging.getLogger(__name__)
29 log = logging.getLogger(__name__)
29
30
30
31
31 def vcs_detection_tween_factory(handler, registry):
32 def vcs_detection_tween_factory(handler, registry):
32
33
33 def vcs_detection_tween(request):
34 def vcs_detection_tween(request):
34 """
35 """
35 Do detection of vcs type, and save results for other layers to re-use
36 Do detection of vcs type, and save results for other layers to re-use
36 this information
37 this information
37 """
38 """
38 vcs_server_enabled = request.registry.settings.get('vcs.server.enable')
39 vcs_server_enabled = request.registry.settings.get('vcs.server.enable')
39 vcs_handler = vcs_server_enabled and detect_vcs_request(
40 vcs_handler = vcs_server_enabled and detect_vcs_request(
40 request.environ, request.registry.settings.get('vcs.backends'))
41 request.environ, request.registry.settings.get('vcs.backends'))
41
42
42 if vcs_handler:
43 if vcs_handler:
43 # save detected VCS type for later re-use
44 # save detected VCS type for later re-use
44 request.environ[VCS_TYPE_KEY] = vcs_handler.SCM
45 request.environ[VCS_TYPE_KEY] = vcs_handler.SCM
45 request.vcs_call = vcs_handler.SCM
46 request.vcs_call = vcs_handler.SCM
46
47
47 log.debug('Processing request with `%s` handler', handler)
48 log.debug('Processing request with `%s` handler', handler)
48 return handler(request)
49 return handler(request)
49
50
50 # mark that we didn't detect an VCS, and we can skip detection later on
51 # mark that we didn't detect an VCS, and we can skip detection later on
51 request.environ[VCS_TYPE_KEY] = VCS_TYPE_SKIP
52 request.environ[VCS_TYPE_KEY] = VCS_TYPE_SKIP
52
53
53 log.debug('Processing request with `%s` handler', handler)
54 log.debug('Processing request with `%s` handler', handler)
54 return handler(request)
55 return handler(request)
55
56
56 return vcs_detection_tween
57 return vcs_detection_tween
57
58
58
59
60 def junk_encoding_detector(request):
61 """
62 Detect bad encoded GET params, and fail immediately with BadRequest
63 """
64
65 try:
66 request.GET.get("", None)
67 except UnicodeDecodeError:
68 raise HTTPBadRequest("Invalid bytes in query string.")
69
70
71 def bad_url_data_detector(request):
72 """
73 Detect invalid bytes in a path.
74 """
75 try:
76 request.path_info
77 except UnicodeDecodeError:
78 raise HTTPBadRequest("Invalid bytes in URL.")
79
80
81 def junk_form_data_detector(request):
82 """
83 Detect bad encoded POST params, and fail immediately with BadRequest
84 """
85
86 if request.method == "POST":
87 try:
88 request.POST.get("", None)
89 except ValueError:
90 raise HTTPBadRequest("Invalid bytes in form data.")
91
92
93 def sanity_check_factory(handler, registry):
94 def sanity_check(request):
95 try:
96 junk_encoding_detector(request)
97 bad_url_data_detector(request)
98 junk_form_data_detector(request)
99 except HTTPException as exc:
100 return exc
101
102 return handler(request)
103
104 return sanity_check
105
106
59 def includeme(config):
107 def includeme(config):
60 config.add_subscriber('rhodecode.subscribers.add_renderer_globals',
108 config.add_subscriber('rhodecode.subscribers.add_renderer_globals',
61 'pyramid.events.BeforeRender')
109 'pyramid.events.BeforeRender')
62 config.add_subscriber('rhodecode.subscribers.set_user_lang',
110 config.add_subscriber('rhodecode.subscribers.set_user_lang',
63 'pyramid.events.NewRequest')
111 'pyramid.events.NewRequest')
64 config.add_subscriber('rhodecode.subscribers.add_localizer',
112 config.add_subscriber('rhodecode.subscribers.add_localizer',
65 'pyramid.events.NewRequest')
113 'pyramid.events.NewRequest')
66 config.add_subscriber('rhodecode.subscribers.add_request_user_context',
114 config.add_subscriber('rhodecode.subscribers.add_request_user_context',
67 'pyramid.events.ContextFound')
115 'pyramid.events.ContextFound')
68
116 config.add_tween('rhodecode.tweens.sanity_check_factory')
69 config.add_tween('rhodecode.tweens.vcs_detection_tween_factory')
117 config.add_tween('rhodecode.tweens.vcs_detection_tween_factory')
General Comments 0
You need to be logged in to leave comments. Login now