Mercurial > p > roundup > code
comparison test/test_templating.py @ 6282:d30501bafdfb
issue2551098: markdown links missing rel="noreferer nofollow"
Links generated by all markdown backends are missing the noopener and
nofollow relation that roundup's normal text -> html core adds to
prevent security issues and link spam.
Now rel="nofollow" is added to links generated by markdown2 backends
and rel="nofollow noopener" for mistune and markdown backends.
Markdown2 isn't as programable as the other two backends so I used the
built-in nofollow support. This means that a user that generates a
link that opens in a new window can manpulate the parent window.
| author | John Rouillard <rouilj@ieee.org> |
|---|---|
| date | Sat, 31 Oct 2020 14:51:16 -0400 |
| parents | 6ed5152a92d0 |
| children | 3f7538316724 |
comparison
equal
deleted
inserted
replaced
| 6281:042c50d5e06e | 6282:d30501bafdfb |
|---|---|
| 418 input=input_xhtml(**attrs) | 418 input=input_xhtml(**attrs) |
| 419 self.assertEqual(input, '<input class="required" disabled="disabled" size="30" type="text"/>') | 419 self.assertEqual(input, '<input class="required" disabled="disabled" size="30" type="text"/>') |
| 420 | 420 |
| 421 # common markdown test cases | 421 # common markdown test cases |
| 422 class MarkdownTests: | 422 class MarkdownTests: |
| 423 def mangleMarkdown2(self, s): | |
| 424 ''' markdown2's rel=nofollow support on 'a' tags isn't programmable. | |
| 425 So we are using it's builtin nofollow support. Mangle the string | |
| 426 so that it matches the test case. | |
| 427 | |
| 428 turn: <a rel="nofollow" href="foo"> into | |
| 429 <a href="foo" rel="nofollow noopener"> | |
| 430 | |
| 431 Also if it is a mailto url, we don't expect rel="nofollow", | |
| 432 so delete it. | |
| 433 | |
| 434 turn: <a rel="nofollow" href="mailto:foo"> into | |
| 435 <a href="mailto:foo"> | |
| 436 | |
| 437 Also when a title is present it is put in a different place | |
| 438 from markdown, so fix it to normalize. | |
| 439 | |
| 440 turn: | |
| 441 | |
| 442 <a rel="nofollow" href="http://example.com/" title="a title"> | |
| 443 into | |
| 444 <a href="http://example.com/" rel="nofollow noopener" title="a title"> | |
| 445 ''' | |
| 446 if type(self) == Markdown2TestCase and s.find('a rel="nofollow"') != -1: | |
| 447 if s.find('href="mailto:') == -1: | |
| 448 # not a mailto url | |
| 449 if 'rel="nofollow"' in s: | |
| 450 if 'title="' in s: | |
| 451 s = s.replace(' rel="nofollow" ', ' ').replace(' title=', ' rel="nofollow noopener" title=') | |
| 452 else: | |
| 453 s = s.replace(' rel="nofollow" ', ' ').replace('">', '" rel="nofollow noopener">') | |
| 454 | |
| 455 return s | |
| 456 else: | |
| 457 # a mailto url | |
| 458 return s.replace(' rel="nofollow" ', ' ') | |
| 459 return s | |
| 460 | |
| 461 | |
| 423 def test_string_markdown(self): | 462 def test_string_markdown(self): |
| 424 p = StringHTMLProperty(self.client, 'test', '1', None, 'test', u2s(u'A string with <br> *embedded* \u00df')) | 463 p = StringHTMLProperty(self.client, 'test', '1', None, 'test', u2s(u'A string with <br> *embedded* \u00df')) |
| 425 self.assertEqual(p.markdown().strip(), u2s(u'<p>A string with <br> <em>embedded</em> \u00df</p>')) | 464 self.assertEqual(p.markdown().strip(), u2s(u'<p>A string with <br> <em>embedded</em> \u00df</p>')) |
| 426 | 465 |
| 427 def test_string_markdown_link(self): | 466 def test_string_markdown_link(self): |
| 435 except ImportError: | 474 except ImportError: |
| 436 from HTMLParser import HTMLParser | 475 from HTMLParser import HTMLParser |
| 437 html_unescape = HTMLParser().unescape | 476 html_unescape = HTMLParser().unescape |
| 438 | 477 |
| 439 p = StringHTMLProperty(self.client, 'test', '1', None, 'test', u2s(u'A link <cmeerw@example.com>')) | 478 p = StringHTMLProperty(self.client, 'test', '1', None, 'test', u2s(u'A link <cmeerw@example.com>')) |
| 440 self.assertEqual(html_unescape(p.markdown().strip()), u2s(u'<p>A link <a href="mailto:cmeerw@example.com">cmeerw@example.com</a></p>')) | 479 m = html_unescape(p.markdown().strip()) |
| 480 m = self.mangleMarkdown2(m) | |
| 481 | |
| 482 self.assertEqual(m, u2s(u'<p>A link <a href="mailto:cmeerw@example.com">cmeerw@example.com</a></p>')) | |
| 441 | 483 |
| 442 def test_string_markdown_javascript_link(self): | 484 def test_string_markdown_javascript_link(self): |
| 443 # make sure we don't get a "javascript:" link | 485 # make sure we don't get a "javascript:" link |
| 444 p = StringHTMLProperty(self.client, 'test', '1', None, 'test', u2s(u'<javascript:alert(1)>')) | 486 p = StringHTMLProperty(self.client, 'test', '1', None, 'test', u2s(u'<javascript:alert(1)>')) |
| 445 self.assertTrue(p.markdown().find('href="javascript:') == -1) | 487 self.assertTrue(p.markdown().find('href="javascript:') == -1) |
| 455 # of text<br> | 497 # of text<br> |
| 456 # etc. | 498 # etc. |
| 457 # Rather than using a different result for each | 499 # Rather than using a different result for each |
| 458 # renderer, look for '<br' and require three of them. | 500 # renderer, look for '<br' and require three of them. |
| 459 m = p.markdown() | 501 m = p.markdown() |
| 502 print(m) | |
| 460 self.assertEqual(3, m.count('<br')) | 503 self.assertEqual(3, m.count('<br')) |
| 461 | 504 |
| 462 def test_string_markdown_code_block(self): | 505 def test_string_markdown_code_block(self): |
| 463 ''' also verify that embedded html is escaped ''' | 506 ''' also verify that embedded html is escaped ''' |
| 464 p = StringHTMLProperty(self.client, 'test', '1', None, 'test', u2s(u'embedded code block <pre>\n\n```\nline 1\nline 2\n```\n\nnew </pre> paragraph')) | 507 p = StringHTMLProperty(self.client, 'test', '1', None, 'test', u2s(u'embedded code block <pre>\n\n```\nline 1\nline 2\n```\n\nnew </pre> paragraph')) |
| 496 def test_markdown_hyperlinked_url(self): | 539 def test_markdown_hyperlinked_url(self): |
| 497 # classic markdown does not emit a \n at end of rendered string | 540 # classic markdown does not emit a \n at end of rendered string |
| 498 # so rstrip \n. | 541 # so rstrip \n. |
| 499 p = StringHTMLProperty(self.client, 'test', '1', None, 'test', u2s(u'http://example.com/')) | 542 p = StringHTMLProperty(self.client, 'test', '1', None, 'test', u2s(u'http://example.com/')) |
| 500 m = p.markdown(hyperlink=1) | 543 m = p.markdown(hyperlink=1) |
| 501 self.assertEqual(m.rstrip('\n'), '<p><a href="http://example.com/">http://example.com/</a></p>') | 544 m = self.mangleMarkdown2(m) |
| 545 print(m) | |
| 546 self.assertEqual(m.rstrip('\n'), '<p><a href="http://example.com/" rel="nofollow noopener">http://example.com/</a></p>') | |
| 502 | 547 |
| 503 p = StringHTMLProperty(self.client, 'test', '1', None, 'test', u2s(u'<http://example.com/>')) | 548 p = StringHTMLProperty(self.client, 'test', '1', None, 'test', u2s(u'<http://example.com/>')) |
| 504 m = p.markdown(hyperlink=1) | 549 m = p.markdown(hyperlink=1) |
| 505 self.assertEqual(m.rstrip('\n'), '<p><a href="http://example.com/">http://example.com/</a></p>') | 550 m = self.mangleMarkdown2(m) |
| 551 self.assertEqual(m.rstrip('\n'), '<p><a href="http://example.com/" rel="nofollow noopener">http://example.com/</a></p>') | |
| 552 | |
| 553 p = StringHTMLProperty(self.client, 'test', '1', None, 'test', u2s(u'[label](http://example.com/ "a title")')) | |
| 554 m = p.markdown(hyperlink=1) | |
| 555 m = self.mangleMarkdown2(m) | |
| 556 self.assertEqual(m.rstrip('\n'), '<p><a href="http://example.com/" rel="nofollow noopener" title="a title">label</a></p>') | |
| 506 | 557 |
| 507 p = StringHTMLProperty(self.client, 'test', '1', None, 'test', u2s(u'')) | 558 p = StringHTMLProperty(self.client, 'test', '1', None, 'test', u2s(u'')) |
| 508 m = p.markdown(hyperlink=1) | 559 m = p.markdown(hyperlink=1) |
| 560 m = self.mangleMarkdown2(m) | |
| 509 self.assertIn(m, [ | 561 self.assertIn(m, [ |
| 510 '<p><img src="http://example.com/" alt=""/></p>\n', | 562 '<p><img src="http://example.com/" alt=""/></p>\n', |
| 511 '<p><img src="http://example.com/" alt="" /></p>\n', | 563 '<p><img src="http://example.com/" alt="" /></p>\n', |
| 512 '<p><img src="http://example.com/" alt=""></p>\n', | 564 '<p><img src="http://example.com/" alt=""></p>\n', |
| 513 '<p><img alt="" src="http://example.com/" /></p>', # markdown | 565 '<p><img alt="" src="http://example.com/" /></p>', # markdown |
| 514 ]) | 566 ]) |
| 515 | 567 |
| 516 p = StringHTMLProperty(self.client, 'test', '1', None, 'test', u2s(u'An URL http://example.com/ with text')) | 568 p = StringHTMLProperty(self.client, 'test', '1', None, 'test', u2s(u'An URL http://example.com/ with text')) |
| 517 m = p.markdown(hyperlink=1) | 569 m = p.markdown(hyperlink=1) |
| 518 self.assertEqual(m.rstrip('\n'), '<p>An URL <a href="http://example.com/">http://example.com/</a> with text</p>') | 570 m = self.mangleMarkdown2(m) |
| 571 self.assertEqual(m.rstrip('\n'), '<p>An URL <a href="http://example.com/" rel="nofollow noopener">http://example.com/</a> with text</p>') | |
| 519 | 572 |
| 520 p = StringHTMLProperty(self.client, 'test', '1', None, 'test', u2s(u'An URL https://example.com/path with text')) | 573 p = StringHTMLProperty(self.client, 'test', '1', None, 'test', u2s(u'An URL https://example.com/path with text')) |
| 521 m = p.markdown(hyperlink=1) | 574 m = p.markdown(hyperlink=1) |
| 522 self.assertEqual(m.rstrip('\n'), '<p>An URL <a href="https://example.com/path">https://example.com/path</a> with text</p>') | 575 m = self.mangleMarkdown2(m) |
| 576 self.assertEqual(m.rstrip('\n'), '<p>An URL <a href="https://example.com/path" rel="nofollow noopener">https://example.com/path</a> with text</p>') | |
| 523 | 577 |
| 524 @skip_mistune | 578 @skip_mistune |
| 525 class MistuneTestCase(TemplatingTestCase, MarkdownTests) : | 579 class MistuneTestCase(TemplatingTestCase, MarkdownTests) : |
| 526 def setUp(self): | 580 def setUp(self): |
| 527 TemplatingTestCase.setUp(self) | 581 TemplatingTestCase.setUp(self) |
