@@ -41,6 +41,34 @@ def test_harmful_links(self):
4141 expected = '<p><a href="#harmful-link">h</a></p>'
4242 self .assertEqual (result .strip (), expected )
4343
44+ def test_harmful_links_variants (self ):
45+ # entity-decoded, alternate-scheme, and reference-link forms are all
46+ # routed to the #harmful-link sentinel.
47+ for text in [
48+ "[h](javascript:alert)" ,
49+ "[h](javascript:alert)" ,
50+ "[h](javascript:alert)" ,
51+ "[h](vbscript:msgbox)" ,
52+ "[h](file:///etc/passwd)" ,
53+ "[h](data:text/html,xss)" ,
54+ "[h](data:image/svg+xml,xss)" ,
55+ "[h][r]\n \n [r]: javascript:alert" ,
56+ ]:
57+ result = mistune .html (text )
58+ self .assertIn ('href="#harmful-link"' , result , text )
59+ self .assertNotIn ("javascript:alert" , result , text )
60+
61+ def test_control_char_scheme_not_executable (self ):
62+ # A control char or encoded colon inside the scheme is percent-encoded
63+ # by escape_url, so the rendered href is never an executable
64+ # javascript: scheme (regardless of the sentinel path).
65+ for text in [
66+ "[h](<java\t script:alert>)" ,
67+ "[h](<java	script:alert>)" ,
68+ "[h](javascript%3Aalert)" ,
69+ ]:
70+ self .assertNotIn ("javascript:alert" , mistune .html (text ), text )
71+
4472 def test_ref_link (self ):
4573 result = mistune .html ("[link][h]\n \n [h]: /foo" )
4674 expected = '<p><a href="/foo">link</a></p>'
0 commit comments