"""Exercise the full legacy populate_markdown path, including readable URLs.""" from html.parser import HTMLParser import pytest from legacy_html_testutil import load_renderer RENDERER = load_renderer() def compose(text): item = {'description': text} RENDERER['populate_markdown'](item) return item['description'] class Elements(HTMLParser): def __init__(self, markup): super().__init__() self.elements = [] self.text = '' self.feed(markup) self.close() def handle_starttag(self, tag, attrs): self.elements.append((tag, dict(attrs))) def handle_data(self, text): self.text += text def anchor(markup): parsed = Elements(markup) assert [tag for tag, _ in parsed.elements] == ['a'] attrs = parsed.elements[0][1] assert set(attrs) == {'href', 'target', 'rel'} assert attrs['target'] == '_blank' assert set(attrs['rel'].split()) == {'noopener', 'noreferrer'} return attrs @pytest.mark.parametrize('url', [ "' onmouseover='alert`1`", '" onclick="alert`1`', 'https://example.com/a\">', ]) def test_attribute_breakout_is_inert(url): assert anchor(compose(f'[a/link]({url})'))['href'] == url @pytest.mark.parametrize('url', [ 'javascript:alert`1`', 'JaVaScRiPt:alert`1`', 'data:text/html,evil', 'vbscript:msgbox`1`', 'ftp://host/file', 'mailto:user@example.com', 'java\tscript:alert`1`', 'java\nscript:alert`1`', '\x01javascript:alert`1`', ]) def test_disallowed_schemes_are_rejected(url): assert anchor(compose(f'[a/link]({url})'))['href'] == '#' @pytest.mark.parametrize('url,expected', [ ('https://example.com/?q=', 'https://example.com/?q='), ('https://example.com/?a=1&b=2', 'https://example.com/?a=1&b=2'), ('https://example.com/?a=1¬ebook=2©=3', 'https://example.com/?a=1¬ebook=2©=3'), ('https://example.com/?q=¬it;', 'https://example.com/?q=¬it;'), ('https://example.com/?q=<Flux>', 'https://example.com/?q='), ('https://example.com/?q=&lt;', 'https://example.com/?q=<'), ('https://example.com/a**b**c', 'https://example.com/a**b**c'), ('https://example.com/%%white%%', 'https://example.com/%%white%%'), ('https://example.com/[w/note]', 'https://example.com/[w/note]'), ('https://example.com/a\nb', 'https://example.com/a\nb'), ('http://example.com/x', 'http://example.com/x'), ('./docs/readme.md', './docs/readme.md'), ('#section', '#section'), ('//host/path', '//host/path'), ('&#106;avascript:alert`1`', 'javascript:alert`1`'), ('javascript:alert`1`', 'javascript:alert`1`'), ]) def test_href_survives_entity_decoding_and_formatting(url, expected): assert anchor(compose(f'[a/link]({url})'))['href'] == expected def test_labels_prose_and_notes_keep_their_formatting(): out = compose(' [w/Read [a/****](https://example.com)]\n%%white%% [i/info]') parsed = Elements(out) assert [tag for tag, _ in parsed.elements] == ['p', 'a', 'b', 'br', 'font', 'p'] assert parsed.text == ' Read white info' assert "class='cm-warn-note'" in out and "class='cm-info-note'" in out @pytest.mark.parametrize('label', ['a tag', 'a <b> tag', 'a\x00 tag']) def test_link_label_is_escaped_once(label): out = compose(f'[a/{label}](https://example.com)') anchor(out) assert Elements(out).text == 'a tag' def test_source_cannot_forge_a_href_placeholder(): out = compose('\x00H0\x00 [a/link](https://example.com)') assert out.count('https://example.com') == 1 assert anchor(out)['href'] == 'https://example.com' def test_name_and_title_preserve_existing_server_contract(): item = {'name': 'Name ', 'title': 'Pack '} RENDERER['populate_markdown'](item) assert item == {'name': 'Name <Flux>', 'title': 'Pack <Flux>'} @pytest.mark.parametrize('url', [ 'javascript:alert`1`', 'javascript:alert`1`', 'javascript:alert`1`', 'javascript:alert`1`', 'javascript:alert`1`', 'javascript:alert`1`', 'data:text/html;base64,PHNjcmlwdD4=', 'java script:alert`1`', 'vbscript:msgbox`1`', ]) def test_entity_obfuscated_schemes_are_rejected(url): assert anchor(compose(f'[a/link]({url})'))['href'] == '#'