Show More
@@ -67,3 +67,4 b' a84d40e9481fcea4dafadee86b03f0dd401527d6' | |||||
67 | 64ea7ea0923618a0c117acebb816a6f0d162bfdb 0.3.3 |
|
67 | 64ea7ea0923618a0c117acebb816a6f0d162bfdb 0.3.3 | |
68 | cf635c823ea059cc3a1581b82d8672e46b682384 0.3.4 |
|
68 | cf635c823ea059cc3a1581b82d8672e46b682384 0.3.4 | |
69 | 4cca4cc6a0a97f4c4763317184cd41aca4297630 0.3.5 |
|
69 | 4cca4cc6a0a97f4c4763317184cd41aca4297630 0.3.5 | |
|
70 | 082c9b8f0f17bd34740eb90c69bdc4c80d4b5b31 0.3.6 |
@@ -328,7 +328,7 b' class BaseVCSController(object):' | |||||
328 | Checks permissions using action (push/pull) user and repository |
|
328 | Checks permissions using action (push/pull) user and repository | |
329 | name |
|
329 | name | |
330 |
|
330 | |||
331 | :param action: push or pull action |
|
331 | :param action: 'push' or 'pull' action | |
332 | :param user: `User` instance |
|
332 | :param user: `User` instance | |
333 | :param repo_name: repository name |
|
333 | :param repo_name: repository name | |
334 | """ |
|
334 | """ |
@@ -30,6 +30,9 b' import re' | |||||
30 | import logging |
|
30 | import logging | |
31 | import traceback |
|
31 | import traceback | |
32 |
|
32 | |||
|
33 | import markdown as markdown_mod | |||
|
34 | import bleach | |||
|
35 | ||||
33 | from kallithea.lib.utils2 import safe_unicode, MENTIONS_REGEX |
|
36 | from kallithea.lib.utils2 import safe_unicode, MENTIONS_REGEX | |
34 |
|
37 | |||
35 | log = logging.getLogger(__name__) |
|
38 | log = logging.getLogger(__name__) | |
@@ -138,17 +141,43 b' class MarkupRenderer(object):' | |||||
138 |
|
141 | |||
139 | @classmethod |
|
142 | @classmethod | |
140 | def markdown(cls, source, safe=True, flavored=False): |
|
143 | def markdown(cls, source, safe=True, flavored=False): | |
|
144 | """ | |||
|
145 | Convert Markdown (possibly GitHub Flavored) to XSS safe HTML, possibly | |||
|
146 | with "safe" fall-back to plaintext. | |||
|
147 | ||||
|
148 | >>> MarkupRenderer.markdown('''<img id="a" style="margin-top:-1000px;color:red" src="http://example.com/test.jpg">''') | |||
|
149 | u'<p><img id="a" src="http://example.com/test.jpg" style="color: red;"></p>' | |||
|
150 | >>> MarkupRenderer.markdown('''<img class="c d" src="file://localhost/test.jpg">''') | |||
|
151 | u'<p><img class="c d"></p>' | |||
|
152 | >>> MarkupRenderer.markdown('''<a href="foo">foo</a>''') | |||
|
153 | u'<p><a href="foo">foo</a></p>' | |||
|
154 | >>> MarkupRenderer.markdown('''<script>alert(1)</script>''') | |||
|
155 | u'<script>alert(1)</script>' | |||
|
156 | >>> MarkupRenderer.markdown('''<div onclick="alert(2)">yo</div>''') | |||
|
157 | u'<div>yo</div>' | |||
|
158 | >>> MarkupRenderer.markdown('''<a href="javascript:alert(3)">yo</a>''') | |||
|
159 | u'<p><a>yo</a></p>' | |||
|
160 | """ | |||
141 | source = safe_unicode(source) |
|
161 | source = safe_unicode(source) | |
142 | try: |
|
162 | try: | |
143 | import markdown as __markdown |
|
|||
144 | if flavored: |
|
163 | if flavored: | |
145 | source = cls._flavored_markdown(source) |
|
164 | source = cls._flavored_markdown(source) | |
146 |
|
|
165 | markdown_html = markdown_mod.markdown(source, | |
147 | extensions=['codehilite', 'extra'], |
|
166 | extensions=['codehilite', 'extra'], | |
148 | extension_configs={'codehilite': {'css_class': 'code-highlight'}}) |
|
167 | extension_configs={'codehilite': {'css_class': 'code-highlight'}}) | |
149 | except ImportError: |
|
168 | # Allow most HTML, while preventing XSS issues: | |
150 | log.warning('Install markdown to use this function') |
|
169 | # no <script> tags, no onclick attributes, no javascript | |
151 | return cls.plain(source) |
|
170 | # "protocol", and also limit styling to prevent defacing. | |
|
171 | return bleach.clean(markdown_html, | |||
|
172 | tags=['a', 'abbr', 'b', 'blockquote', 'br', 'code', 'dd', | |||
|
173 | 'div', 'dl', 'dt', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', | |||
|
174 | 'h6', 'hr', 'i', 'img', 'li', 'ol', 'p', 'pre', 'span', | |||
|
175 | 'strong', 'sub', 'sup', 'table', 'tbody', 'td', 'th', | |||
|
176 | 'thead', 'tr', 'ul'], | |||
|
177 | attributes=['class', 'id', 'style', 'label', 'title', 'alt', 'href', 'src'], | |||
|
178 | styles=['color'], | |||
|
179 | protocols=['http', 'https', 'mailto'], | |||
|
180 | ) | |||
152 | except Exception: |
|
181 | except Exception: | |
153 | log.error(traceback.format_exc()) |
|
182 | log.error(traceback.format_exc()) | |
154 | if safe: |
|
183 | if safe: |
@@ -31,6 +31,7 b' Original author and date, and relevant c' | |||||
31 | import os |
|
31 | import os | |
32 | import logging |
|
32 | import logging | |
33 | import traceback |
|
33 | import traceback | |
|
34 | import urllib | |||
34 |
|
35 | |||
35 | from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \ |
|
36 | from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \ | |
36 | HTTPNotAcceptable, HTTPBadRequest |
|
37 | HTTPNotAcceptable, HTTPBadRequest | |
@@ -63,6 +64,24 b' def is_mercurial(environ):' | |||||
63 | return ishg_path |
|
64 | return ishg_path | |
64 |
|
65 | |||
65 |
|
66 | |||
|
67 | def get_header_hgarg(environ): | |||
|
68 | """Decode the special Mercurial encoding of big requests over multiple headers. | |||
|
69 | >>> get_header_hgarg({}) | |||
|
70 | '' | |||
|
71 | >>> get_header_hgarg({'HTTP_X_HGARG_0': ' ', 'HTTP_X_HGARG_1': 'a','HTTP_X_HGARG_2': '','HTTP_X_HGARG_3': 'b+c %20'}) | |||
|
72 | 'ab+c %20' | |||
|
73 | """ | |||
|
74 | chunks = [] | |||
|
75 | i = 1 | |||
|
76 | while True: | |||
|
77 | v = environ.get('HTTP_X_HGARG_%d' % i) | |||
|
78 | if v is None: | |||
|
79 | break | |||
|
80 | chunks.append(v) | |||
|
81 | i += 1 | |||
|
82 | return ''.join(chunks) | |||
|
83 | ||||
|
84 | ||||
66 | class SimpleHg(BaseVCSController): |
|
85 | class SimpleHg(BaseVCSController): | |
67 |
|
86 | |||
68 | def _handle_request(self, environ, start_response): |
|
87 | def _handle_request(self, environ, start_response): | |
@@ -205,16 +224,55 b' class SimpleHg(BaseVCSController):' | |||||
205 |
|
224 | |||
206 | def __get_action(self, environ): |
|
225 | def __get_action(self, environ): | |
207 | """ |
|
226 | """ | |
208 |
Maps Mercurial request commands into |
|
227 | Maps Mercurial request commands into 'pull' or 'push'. | |
209 |
|
228 | |||
210 | Raises HTTPBadRequest if the request environment doesn't look like a hg client. |
|
229 | Raises HTTPBadRequest if the request environment doesn't look like a hg client. | |
211 | """ |
|
230 | """ | |
212 |
mapping = { |
|
231 | mapping = { | |
213 | 'pushkey': 'push'} |
|
232 | # 'batch' is not in this list - it is handled explicitly | |
|
233 | 'between': 'pull', | |||
|
234 | 'branches': 'pull', | |||
|
235 | 'branchmap': 'pull', | |||
|
236 | 'capabilities': 'pull', | |||
|
237 | 'changegroup': 'pull', | |||
|
238 | 'changegroupsubset': 'pull', | |||
|
239 | 'changesetdata': 'pull', | |||
|
240 | 'clonebundles': 'pull', | |||
|
241 | 'debugwireargs': 'pull', | |||
|
242 | 'filedata': 'pull', | |||
|
243 | 'getbundle': 'pull', | |||
|
244 | 'getlfile': 'pull', | |||
|
245 | 'heads': 'pull', | |||
|
246 | 'hello': 'pull', | |||
|
247 | 'known': 'pull', | |||
|
248 | 'lheads': 'pull', | |||
|
249 | 'listkeys': 'pull', | |||
|
250 | 'lookup': 'pull', | |||
|
251 | 'manifestdata': 'pull', | |||
|
252 | 'narrow_widen': 'pull', | |||
|
253 | 'protocaps': 'pull', | |||
|
254 | 'statlfile': 'pull', | |||
|
255 | 'stream_out': 'pull', | |||
|
256 | 'pushkey': 'push', | |||
|
257 | 'putlfile': 'push', | |||
|
258 | 'unbundle': 'push', | |||
|
259 | } | |||
214 | for qry in environ['QUERY_STRING'].split('&'): |
|
260 | for qry in environ['QUERY_STRING'].split('&'): | |
215 |
|
|
261 | parts = qry.split('=', 1) | |
216 | cmd = qry.split('=')[-1] |
|
262 | if len(parts) == 2 and parts[0] == 'cmd': | |
217 | return mapping.get(cmd, 'pull') |
|
263 | cmd = parts[1] | |
|
264 | if cmd == 'batch': | |||
|
265 | hgarg = get_header_hgarg(environ) | |||
|
266 | if not hgarg.startswith('cmds='): | |||
|
267 | return 'push' # paranoid and safe | |||
|
268 | for cmd_arg in hgarg[5:].split(';'): | |||
|
269 | cmd, _args = urllib.unquote_plus(cmd_arg).split(' ', 1) | |||
|
270 | op = mapping.get(cmd, 'push') | |||
|
271 | if op != 'pull': | |||
|
272 | assert op == 'push' | |||
|
273 | return 'push' | |||
|
274 | return 'pull' | |||
|
275 | return mapping.get(cmd, 'push') | |||
218 |
|
276 | |||
219 | # Note: the client doesn't get the helpful error message |
|
277 | # Note: the client doesn't get the helpful error message | |
220 | raise HTTPBadRequest('Unable to detect pull/push action! Are you using non standard command or client?') |
|
278 | raise HTTPBadRequest('Unable to detect pull/push action! Are you using non standard command or client?') |
@@ -60,6 +60,7 b' requirements = [' | |||||
60 | "mercurial >= 4.1.1, < 4.9", |
|
60 | "mercurial >= 4.1.1, < 4.9", | |
61 | "decorator >= 3.3.2, < 4.4", |
|
61 | "decorator >= 3.3.2, < 4.4", | |
62 | "Paste >= 2.0.3, < 3", |
|
62 | "Paste >= 2.0.3, < 3", | |
|
63 | "bleach >= 3.0, < 3.1", | |||
63 | ] |
|
64 | ] | |
64 |
|
65 | |||
65 | if sys.version_info < (2, 7): |
|
66 | if sys.version_info < (2, 7): |
General Comments 0
You need to be logged in to leave comments.
Login now