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 &lt;br&gt; <em>embedded</em> \u00df</p>')) 464 self.assertEqual(p.markdown().strip(), u2s(u'<p>A string with &lt;br&gt; <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'![](http://example.com/)')) 558 p = StringHTMLProperty(self.client, 'test', '1', None, 'test', u2s(u'![](http://example.com/)'))
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)

Roundup Issue Tracker: http://roundup-tracker.org/