Mercurial > kallithea
changeset 7404:22c8f23cc75b
Merge stable
author | Mads Kiilerich <mads@kiilerich.com> |
---|---|
date | Mon, 05 Nov 2018 00:31:07 +0100 |
parents | 475d54df23f5 (current diff) d85ce8c88f0d (diff) |
children | e4b9a1d1fea1 |
files | kallithea/__init__.py kallithea/lib/base.py kallithea/lib/markup_renderer.py kallithea/lib/middleware/simplehg.py setup.py |
diffstat | 5 files changed, 101 insertions(+), 12 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgtags Mon Oct 29 01:20:21 2018 +0100 +++ b/.hgtags Mon Nov 05 00:31:07 2018 +0100 @@ -67,3 +67,4 @@ 64ea7ea0923618a0c117acebb816a6f0d162bfdb 0.3.3 cf635c823ea059cc3a1581b82d8672e46b682384 0.3.4 4cca4cc6a0a97f4c4763317184cd41aca4297630 0.3.5 +082c9b8f0f17bd34740eb90c69bdc4c80d4b5b31 0.3.6
--- a/kallithea/lib/base.py Mon Oct 29 01:20:21 2018 +0100 +++ b/kallithea/lib/base.py Mon Nov 05 00:31:07 2018 +0100 @@ -328,7 +328,7 @@ Checks permissions using action (push/pull) user and repository name - :param action: push or pull action + :param action: 'push' or 'pull' action :param user: `User` instance :param repo_name: repository name """
--- a/kallithea/lib/markup_renderer.py Mon Oct 29 01:20:21 2018 +0100 +++ b/kallithea/lib/markup_renderer.py Mon Nov 05 00:31:07 2018 +0100 @@ -30,6 +30,9 @@ import logging import traceback +import markdown as markdown_mod +import bleach + from kallithea.lib.utils2 import safe_unicode, MENTIONS_REGEX log = logging.getLogger(__name__) @@ -138,17 +141,43 @@ @classmethod def markdown(cls, source, safe=True, flavored=False): + """ + Convert Markdown (possibly GitHub Flavored) to XSS safe HTML, possibly + with "safe" fall-back to plaintext. + + >>> MarkupRenderer.markdown('''<img id="a" style="margin-top:-1000px;color:red" src="http://example.com/test.jpg">''') + u'<p><img id="a" src="http://example.com/test.jpg" style="color: red;"></p>' + >>> MarkupRenderer.markdown('''<img class="c d" src="file://localhost/test.jpg">''') + u'<p><img class="c d"></p>' + >>> MarkupRenderer.markdown('''<a href="foo">foo</a>''') + u'<p><a href="foo">foo</a></p>' + >>> MarkupRenderer.markdown('''<script>alert(1)</script>''') + u'<script>alert(1)</script>' + >>> MarkupRenderer.markdown('''<div onclick="alert(2)">yo</div>''') + u'<div>yo</div>' + >>> MarkupRenderer.markdown('''<a href="javascript:alert(3)">yo</a>''') + u'<p><a>yo</a></p>' + """ source = safe_unicode(source) try: - import markdown as __markdown if flavored: source = cls._flavored_markdown(source) - return __markdown.markdown(source, + markdown_html = markdown_mod.markdown(source, extensions=['codehilite', 'extra'], extension_configs={'codehilite': {'css_class': 'code-highlight'}}) - except ImportError: - log.warning('Install markdown to use this function') - return cls.plain(source) + # Allow most HTML, while preventing XSS issues: + # no <script> tags, no onclick attributes, no javascript + # "protocol", and also limit styling to prevent defacing. + return bleach.clean(markdown_html, + tags=['a', 'abbr', 'b', 'blockquote', 'br', 'code', 'dd', + 'div', 'dl', 'dt', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', + 'h6', 'hr', 'i', 'img', 'li', 'ol', 'p', 'pre', 'span', + 'strong', 'sub', 'sup', 'table', 'tbody', 'td', 'th', + 'thead', 'tr', 'ul'], + attributes=['class', 'id', 'style', 'label', 'title', 'alt', 'href', 'src'], + styles=['color'], + protocols=['http', 'https', 'mailto'], + ) except Exception: log.error(traceback.format_exc()) if safe:
--- a/kallithea/lib/middleware/simplehg.py Mon Oct 29 01:20:21 2018 +0100 +++ b/kallithea/lib/middleware/simplehg.py Mon Nov 05 00:31:07 2018 +0100 @@ -31,6 +31,7 @@ import os import logging import traceback +import urllib from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \ HTTPNotAcceptable, HTTPBadRequest @@ -63,6 +64,24 @@ return ishg_path +def get_header_hgarg(environ): + """Decode the special Mercurial encoding of big requests over multiple headers. + >>> get_header_hgarg({}) + '' + >>> get_header_hgarg({'HTTP_X_HGARG_0': ' ', 'HTTP_X_HGARG_1': 'a','HTTP_X_HGARG_2': '','HTTP_X_HGARG_3': 'b+c %20'}) + 'ab+c %20' + """ + chunks = [] + i = 1 + while True: + v = environ.get('HTTP_X_HGARG_%d' % i) + if v is None: + break + chunks.append(v) + i += 1 + return ''.join(chunks) + + class SimpleHg(BaseVCSController): def _handle_request(self, environ, start_response): @@ -205,16 +224,55 @@ def __get_action(self, environ): """ - Maps Mercurial request commands into a pull or push command. + Maps Mercurial request commands into 'pull' or 'push'. Raises HTTPBadRequest if the request environment doesn't look like a hg client. """ - mapping = {'unbundle': 'push', - 'pushkey': 'push'} + mapping = { + # 'batch' is not in this list - it is handled explicitly + 'between': 'pull', + 'branches': 'pull', + 'branchmap': 'pull', + 'capabilities': 'pull', + 'changegroup': 'pull', + 'changegroupsubset': 'pull', + 'changesetdata': 'pull', + 'clonebundles': 'pull', + 'debugwireargs': 'pull', + 'filedata': 'pull', + 'getbundle': 'pull', + 'getlfile': 'pull', + 'heads': 'pull', + 'hello': 'pull', + 'known': 'pull', + 'lheads': 'pull', + 'listkeys': 'pull', + 'lookup': 'pull', + 'manifestdata': 'pull', + 'narrow_widen': 'pull', + 'protocaps': 'pull', + 'statlfile': 'pull', + 'stream_out': 'pull', + 'pushkey': 'push', + 'putlfile': 'push', + 'unbundle': 'push', + } for qry in environ['QUERY_STRING'].split('&'): - if qry.startswith('cmd'): - cmd = qry.split('=')[-1] - return mapping.get(cmd, 'pull') + parts = qry.split('=', 1) + if len(parts) == 2 and parts[0] == 'cmd': + cmd = parts[1] + if cmd == 'batch': + hgarg = get_header_hgarg(environ) + if not hgarg.startswith('cmds='): + return 'push' # paranoid and safe + for cmd_arg in hgarg[5:].split(';'): + cmd, _args = urllib.unquote_plus(cmd_arg).split(' ', 1) + op = mapping.get(cmd, 'push') + if op != 'pull': + assert op == 'push' + return 'push' + return 'pull' + return mapping.get(cmd, 'push') # Note: the client doesn't get the helpful error message raise HTTPBadRequest('Unable to detect pull/push action! Are you using non standard command or client?')