]> git.vanrenterghem.biz Git - git.ikiwiki.info.git/commitdiff
backport htmlscrubber javascript uri sanitisation fix from master
authorJoey Hess <joey@kodama.kitenet.net>
Sun, 10 Feb 2008 18:36:17 +0000 (13:36 -0500)
committerJoey Hess <joey@kodama.kitenet.net>
Sun, 10 Feb 2008 18:36:17 +0000 (13:36 -0500)
IkiWiki/Plugin/htmlscrubber.pm
debian/changelog
doc/plugins/htmlscrubber.mdwn
t/htmlize.t

index ae3ec44569d3e666ca5abe60f9afc19e5b6643c6..c4a0d60af9e63c46cad83a5754233b26018ee003 100644 (file)
@@ -18,6 +18,28 @@ my $_scrubber;
 sub scrubber { #{{{
        return $_scrubber if defined $_scrubber;
        
 sub scrubber { #{{{
        return $_scrubber if defined $_scrubber;
        
+       # Only known uri schemes are allowed to avoid all the ways of
+       # embedding javascrpt.
+       # List at http://en.wikipedia.org/wiki/URI_scheme
+       my $uri_schemes=join("|",
+               # IANA registered schemes
+               "http", "https", "ftp", "mailto", "file", "telnet", "gopher",
+               "aaa", "aaas", "acap",  "cap", "cid", "crid", 
+               "dav", "dict", "dns", "fax", "go", "h323", "im", "imap",
+               "ldap", "mid", "news", "nfs", "nntp", "pop", "pres",
+               "sip", "sips", "snmp", "tel", "urn", "wais", "xmpp",
+               "z39.50r", "z39.50s",
+               # data is a special case. Allow data:text/<image>, but
+               # disallow data:text/javascript and everything else.
+               qr/data:text\/(?:png|gif|jpeg)/,
+               # Selected unofficial schemes
+               "about", "aim", "callto", "cvs", "ed2k", "feed", "fish", "gg",
+               "irc", "ircs", "lastfm", "ldaps", "magnet", "mms",
+               "msnim", "notes", "rsync", "secondlife", "skype", "ssh",
+               "sftp", "sms", "steam", "webcal", "ymsgr",
+       );
+       my $link=qr/^(?:$uri_schemes:|[^:]+$)/i;
+
        eval q{use HTML::Scrubber};
        error($@) if $@;
        # Lists based on http://feedparser.org/docs/html-sanitization.html
        eval q{use HTML::Scrubber};
        error($@) if $@;
        # Lists based on http://feedparser.org/docs/html-sanitization.html
@@ -32,19 +54,22 @@ sub scrubber { #{{{
                        tfoot th thead tr tt u ul var
                }],
                default => [undef, { map { $_ => 1 } qw{
                        tfoot th thead tr tt u ul var
                }],
                default => [undef, { map { $_ => 1 } qw{
-                       abbr accept accept-charset accesskey action
+                       abbr accept accept-charset accesskey
                        align alt axis border cellpadding cellspacing
                        char charoff charset checked cite class
                        clear cols colspan color compact coords
                        datetime dir disabled enctype for frame
                        align alt axis border cellpadding cellspacing
                        char charoff charset checked cite class
                        clear cols colspan color compact coords
                        datetime dir disabled enctype for frame
-                       headers height href hreflang hspace id ismap
+                       headers height hreflang hspace id ismap
                        label lang longdesc maxlength media method
                        multiple name nohref noshade nowrap prompt
                        readonly rel rev rows rowspan rules scope
                        label lang longdesc maxlength media method
                        multiple name nohref noshade nowrap prompt
                        readonly rel rev rows rowspan rules scope
-                       selected shape size span src start summary
+                       selected shape size span start summary
                        tabindex target title type usemap valign
                        value vspace width
                }, "/" => 1, # emit proper <hr /> XHTML
                        tabindex target title type usemap valign
                        value vspace width
                }, "/" => 1, # emit proper <hr /> XHTML
+               href => $link,
+               src => $link,
+               action => $link,
                }],
        );
        return $_scrubber;
                }],
        );
        return $_scrubber;
index 027785277e4da79e620ddbe079b9b98443d5ea8e..4776e146186434e649ab3d84bae65c34711174da 100644 (file)
@@ -1,3 +1,10 @@
+ikiwiki (1.33.4) testing-proposed-updates; urgency=medium
+
+  * htmlscrubber security fix: Block javascript in uris.
+  * Add htmlscrubber test suite.
+
+ -- Joey Hess <joeyh@debian.org>  Sun, 10 Feb 2008 13:34:28 -0500
+
 ikiwiki (1.33.3) testing-proposed-updates; urgency=medium
 
   * Fix a security hole that allowed insertion of unsafe content via the meta
 ikiwiki (1.33.3) testing-proposed-updates; urgency=medium
 
   * Fix a security hole that allowed insertion of unsafe content via the meta
index e3652a847999f2cf7076bf0d3b99f05089af2abd..144594659d59616b2d7a4ea6244ddd4d586cf6ec 100644 (file)
@@ -29,6 +29,14 @@ browser.
 Some examples of embedded javascript that won't be let through when this
 plugin is active:
 
 Some examples of embedded javascript that won't be let through when this
 plugin is active:
 
+<<<<<<< HEAD:doc/plugins/htmlscrubber.mdwn
 * <span style="background: url(javascript:window.location='http://example.org/')">test</span>
 * <span style="&#x61;&#x6e;&#x79;&#x3a;&#x20;&#x65;&#x78;&#x70;&#x72;&#x65;&#x73;&#x73;&#x69;&#x6f;&#x6e;&#x28;&#x77;&#x69;&#x6e;&#x64;&#x6f;&#x77;&#x2e;&#x6c;&#x6f;&#x63;&#x61;&#x74;&#x69;&#x6f;&#x6e;&#x3d;&#x27;&#x68;&#x74;&#x74;&#x70;&#x3a;&#x2f;&#x2f;&#x65;&#x78;&#x61;&#x6d;&#x70;&#x6c;&#x65;&#x2e;&#x6f;&#x72;&#x67;&#x2f;&#x27;&#x29;">test</span>
 * <span style="&#97;&#110;&#121;&#58;&#32;&#101;&#120;&#112;&#114;&#101;&#115;&#115;&#105;&#111;&#110;&#40;&#119;&#105;&#110;&#100;&#111;&#119;&#46;&#108;&#111;&#99;&#97;&#116;&#105;&#111;&#110;&#61;&#39;&#104;&#116;&#116;&#112;&#58;&#47;&#47;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#111;&#114;&#103;&#47;&#39;&#41;">test</span>
 * <span style="background: url(javascript:window.location='http://example.org/')">test</span>
 * <span style="&#x61;&#x6e;&#x79;&#x3a;&#x20;&#x65;&#x78;&#x70;&#x72;&#x65;&#x73;&#x73;&#x69;&#x6f;&#x6e;&#x28;&#x77;&#x69;&#x6e;&#x64;&#x6f;&#x77;&#x2e;&#x6c;&#x6f;&#x63;&#x61;&#x74;&#x69;&#x6f;&#x6e;&#x3d;&#x27;&#x68;&#x74;&#x74;&#x70;&#x3a;&#x2f;&#x2f;&#x65;&#x78;&#x61;&#x6d;&#x70;&#x6c;&#x65;&#x2e;&#x6f;&#x72;&#x67;&#x2f;&#x27;&#x29;">test</span>
 * <span style="&#97;&#110;&#121;&#58;&#32;&#101;&#120;&#112;&#114;&#101;&#115;&#115;&#105;&#111;&#110;&#40;&#119;&#105;&#110;&#100;&#111;&#119;&#46;&#108;&#111;&#99;&#97;&#116;&#105;&#111;&#110;&#61;&#39;&#104;&#116;&#116;&#112;&#58;&#47;&#47;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#111;&#114;&#103;&#47;&#39;&#41;">test</span>
+=======
+* script tag test <script>window.location='http://example.org';</script>
+* <span style="background: url(javascript:window.location='http://example.org/')">CSS script test</span>
+* <span style="&#x61;&#x6e;&#x79;&#x3a;&#x20;&#x65;&#x78;&#x70;&#x72;&#x65;&#x73;&#x73;&#x69;&#x6f;&#x6e;&#x28;&#x77;&#x69;&#x6e;&#x64;&#x6f;&#x77;&#x2e;&#x6c;&#x6f;&#x63;&#x61;&#x74;&#x69;&#x6f;&#x6e;&#x3d;&#x27;&#x68;&#x74;&#x74;&#x70;&#x3a;&#x2f;&#x2f;&#x65;&#x78;&#x61;&#x6d;&#x70;&#x6c;&#x65;&#x2e;&#x6f;&#x72;&#x67;&#x2f;&#x27;&#x29;">entity-encoded CSS script test</span>
+* <span style="&#97;&#110;&#121;&#58;&#32;&#101;&#120;&#112;&#114;&#101;&#115;&#115;&#105;&#111;&#110;&#40;&#119;&#105;&#110;&#100;&#111;&#119;&#46;&#108;&#111;&#99;&#97;&#116;&#105;&#111;&#110;&#61;&#39;&#104;&#116;&#116;&#112;&#58;&#47;&#47;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#111;&#114;&#103;&#47;&#39;&#41;">entity-encoded CSS script test</span>
+* <a href="javascript&#x3A;alert('foo')">click me</a>
+>>>>>>> d7e0c03... * htmlscrubber security fix: Block javascript in uris.:doc/plugins/htmlscrubber.mdwn
index a9ccfedcbb184b9a31165b63d9009f012416cbf5..edf357010a0b551b58f895908cfd1858bc9e5f95 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/perl
 use warnings;
 use strict;
 #!/usr/bin/perl
 use warnings;
 use strict;
-use Test::More tests => 4;
+use Test::More tests => 26;
 use Encode;
 
 BEGIN { use_ok("IkiWiki"); }
 use Encode;
 
 BEGIN { use_ok("IkiWiki"); }
@@ -19,3 +19,52 @@ is(IkiWiki::htmlize("foo", "mdwn", readfile("t/test1.mdwn")),
        "utf8; bug #373203");
 ok(IkiWiki::htmlize("foo", "mdwn", readfile("t/test2.mdwn")),
        "this file crashes markdown if it's fed in as decoded utf-8");
        "utf8; bug #373203");
 ok(IkiWiki::htmlize("foo", "mdwn", readfile("t/test2.mdwn")),
        "this file crashes markdown if it's fed in as decoded utf-8");
+
+sub gotcha {
+       my $html=IkiWiki::htmlize("foo", "mdwn", shift);
+       return $html =~ /GOTCHA/;
+}
+ok(!gotcha(q{<a href="javascript:alert('GOTCHA')">click me</a>}),
+       "javascript url");
+ok(!gotcha(q{<a href="javascript&#x3A;alert('GOTCHA')">click me</a>}),
+       "partially encoded javascript url");
+ok(!gotcha(q{<a href="jscript:alert('GOTCHA')">click me</a>}),
+       "jscript url");
+ok(!gotcha(q{<a href="vbscript:alert('GOTCHA')">click me</a>}),
+       "vbscrpt url");
+ok(!gotcha(q{<a href="java     script:alert('GOTCHA')">click me</a>}),
+       "java-tab-script url");
+ok(!gotcha(q{<span style="&#x61;&#x6e;&#x79;&#x3a;&#x20;&#x65;&#x78;&#x70;&#x72;&#x65;&#x73;&#x73;&#x69;&#x6f;(GOTCHA)&#x6e;&#x28;&#x77;&#x69;&#x6e;&#x64;&#x6f;&#x77;&#x2e;&#x6c;&#x6f;&#x63;&#x61;&#x74;&#x69;&#x6f;&#x6e;&#x3d;&#x27;&#x68;&#x74;&#x74;&#x70;&#x3a;&#x2f;&#x2f;&#x65;&#x78;&#x61;&#x6d;&#x70;&#x6c;&#x65;&#x2e;&#x6f;&#x72;&#x67;&#x2f;&#x27;&#x29;">foo</span>}),
+       "entity-encoded CSS script test");
+ok(!gotcha(q{<span style="&#97;&#110;&#121;&#58;&#32;&#101;&#120;&#112;&#114;&#101;&#115;&#115;&#105;&#111;&#110;(GOTCHA)&#40;&#119;&#105;&#110;&#100;&#111;&#119;&#46;&#108;&#111;&#99;&#97;&#116;&#105;&#111;&#110;&#61;&#39;&#104;&#116;&#116;&#112;&#58;&#47;&#47;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#111;&#114;&#103;&#47;&#39;&#41;">foo</span>}),
+       "another entity-encoded CSS script test");
+ok(!gotcha(q{<script>GOTCHA</script>}),
+       "script tag");
+ok(!gotcha(q{<form action="javascript:alert('GOTCHA')">foo</form>}),
+       "form action with javascript");
+ok(!gotcha(q{<video poster="javascript:alert('GOTCHA')" href="foo.avi">foo</video>}),
+       "video poster with javascript");
+ok(!gotcha(q{<span style="background: url(javascript:window.location=GOTCHA)">a</span>}),
+       "CSS script test");
+ok(! gotcha(q{<img src="data:text/javascript:GOTCHA">}),
+       "data:text/javascript (jeez!)");
+ok(gotcha(q{<img src="data:text/png:GOTCHA">}), "data:text/png");
+ok(gotcha(q{<img src="data:text/gif:GOTCHA">}), "data:text/gif");
+ok(gotcha(q{<img src="data:text/jpeg:GOTCHA">}), "data:text/jpeg");
+ok(gotcha(q{<p>javascript:alert('GOTCHA')</p>}),
+       "not javascript AFAIK (but perhaps some web browser would like to
+       be perverse and assume it is?)");
+ok(gotcha(q{<img src="javascript.png?GOTCHA">}), "not javascript");
+ok(gotcha(q{<a href="javascript.png?GOTCHA">foo</a>}), "not javascript");
+is(IkiWiki::htmlize("foo", "mdwn",
+       q{<img alt="foo" src="foo.gif">}),
+       q{<img alt="foo" src="foo.gif">}, "img with alt tag allowed");
+is(IkiWiki::htmlize("foo", "mdwn",
+       q{<a href="http://google.com/">}),
+       q{<a href="http://google.com/">}, "absolute url allowed");
+is(IkiWiki::htmlize("foo", "mdwn",
+       q{<a href="foo.html">}),
+       q{<a href="foo.html">}, "relative url allowed");
+is(IkiWiki::htmlize("foo", "mdwn",
+       q{<span class="foo">bar</span>}),
+       q{<span class="foo">bar</span>}, "class attribute allowed");