Merge "Clicking on CID link causes webview corruption" into ub-gmail-ur14-dev
diff --git a/src/com/android/mail/utils/HtmlSanitizer.java b/src/com/android/mail/utils/HtmlSanitizer.java
index da29345..ca831df 100644
--- a/src/com/android/mail/utils/HtmlSanitizer.java
+++ b/src/com/android/mail/utils/HtmlSanitizer.java
@@ -116,10 +116,22 @@
     };
 
     /**
-     * Disallow the "mailto:" url on images so that "Show pictures" can't be used to start composing
-     * a bajillion emails.
+     * Disallow "cid:" and "mailto:" urls on all tags not <a> or <img>.
      */
-    private static final AttributePolicy NO_MAILTO_URL =
+    private static final AttributePolicy URL_PROTOCOLS =
+            new FilterUrlByProtocolAttributePolicy(ImmutableList.of("http", "https"));
+
+    /**
+     * Disallow the "cid:" url on links. Do allow "mailto:" urls to support sending mail.
+     */
+    private static final AttributePolicy A_HREF_PROTOCOLS =
+            new FilterUrlByProtocolAttributePolicy(ImmutableList.of("mailto", "http", "https"));
+
+    /**
+     * Disallow the "mailto:" url on images so that "Show pictures" can't be used to start composing
+     * a bajillion emails. Do allow "cid:" urls to support inline image attachments.
+     */
+    private static final AttributePolicy IMG_SRC_PROTOCOLS =
             new FilterUrlByProtocolAttributePolicy(ImmutableList.of("cid", "http", "https"));
 
     /**
@@ -180,17 +192,20 @@
             .allowUrlProtocols("cid", "http", "https", "mailto")
             .allowStyling(CssSchema.union(CssSchema.DEFAULT, ADDITIONAL_CSS))
             .disallowTextIn("applet", "frameset", "object", "script", "style", "title")
-            .allowElements("a").allowAttributes("coords", "href", "name", "shape").onElements("a")
+            .allowElements("a")
+                .allowAttributes("coords", "name", "shape").onElements("a")
+                .allowAttributes("href").matching(A_HREF_PROTOCOLS).onElements("a")
             .allowElements("abbr").allowAttributes("title").onElements("abbr")
             .allowElements("acronym").allowAttributes("title").onElements("acronym")
             .allowElements("address")
             .allowElements("area")
-                .allowAttributes("alt", "coords", "href", "nohref", "name", "shape")
-            .onElements("area")
+                .allowAttributes("alt", "coords", "nohref", "name", "shape").onElements("area")
+                .allowAttributes("href").matching(URL_PROTOCOLS).onElements("area")
             .allowElements("article")
             .allowElements("aside")
             .allowElements("b")
-            .allowElements("base").allowAttributes("href").onElements("base")
+            .allowElements("base")
+                .allowAttributes("href").matching(URL_PROTOCOLS).onElements("base")
             .allowElements("bdi").allowAttributes("dir").onElements("bdi")
             .allowElements("bdo").allowAttributes("dir").onElements("bdo")
             .allowElements("big")
@@ -207,7 +222,7 @@
             .allowElements("cite")
             .allowElements("code")
             .allowElements("col")
-            .   allowAttributes("align", "bgcolor", "char", "charoff", "span", "valign", "width")
+                .allowAttributes("align", "bgcolor", "char", "charoff", "span", "valign", "width")
             .onElements("col")
             .allowElements("colgroup")
                 .allowAttributes("align", "char", "charoff", "span", "valign", "width")
@@ -247,18 +262,23 @@
             .onElements("hr")
             .allowElements("i")
             .allowElements("img")
+                .allowAttributes("src").matching(IMG_SRC_PROTOCOLS).onElements("img")
+                .allowAttributes("longdesc").matching(URL_PROTOCOLS).onElements("img")
                 .allowAttributes("align", "alt", "border", "crossorigin", "height", "hspace",
-                        "ismap", "longdesc", "usemap", "vspace", "width")
+                        "ismap", "usemap", "vspace", "width")
             .onElements("img")
-            .allowAttributes("src").matching(NO_MAILTO_URL).onElements("img")
             .allowElements("input")
+                .allowAttributes("src").matching(URL_PROTOCOLS).onElements("input")
+                .allowAttributes("formaction").matching(URL_PROTOCOLS).onElements("input")
                 .allowAttributes("accept", "align", "alt", "autocomplete", "autofocus", "checked",
-                        "disabled", "form", "formaction", "formenctype", "formmethod",
-                        "formnovalidate", "formtarget", "height", "list", "max", "maxlength", "min",
-                        "multiple", "name", "pattern", "placeholder", "readonly", "required",
-                        "size", "src", "step", "type", "value", "width")
+                        "disabled", "form", "formenctype", "formmethod", "formnovalidate",
+                        "formtarget", "height", "list", "max", "maxlength", "min", "multiple",
+                        "name", "pattern", "placeholder", "readonly", "required", "size", "step",
+                        "type", "value", "width")
             .onElements("input")
-            .allowElements("ins").allowAttributes("cite", "datetime").onElements("ins")
+            .allowElements("ins")
+                .allowAttributes("cite").matching(URL_PROTOCOLS).onElements("ins")
+                .allowAttributes("datetime").onElements("ins")
             .allowElements("kbd")
             .allowElements("keygen")
                 .allowAttributes("autofocus", "challenge", "disabled", "form", "keytype", "name")
@@ -271,9 +291,9 @@
             .allowElements("mark")
             .allowElements("menu").allowAttributes("label", "type").onElements("menu")
             .allowElements("menuitem")
-                .allowAttributes("checked", "command", "default", "disabled", "icon", "label",
-                        "type", "radiogroup")
-            .onElements("menuitem")
+                .allowAttributes("icon").matching(URL_PROTOCOLS).onElements("menuitem")
+                .allowAttributes("checked", "command", "default", "disabled", "label", "type",
+                        "radiogroup").onElements("menuitem")
             .allowElements("meter")
                 .allowAttributes("form", "high", "low", "max", "min", "optimum", "value")
             .onElements("meter")
@@ -289,7 +309,7 @@
             .allowElements("p").allowAttributes("align").onElements("p")
             .allowElements("pre").allowAttributes("width").onElements("pre")
             .allowElements("progress").allowAttributes("max", "value").onElements("progress")
-            .allowElements("q").allowAttributes("cite").onElements("q")
+            .allowElements("q").allowAttributes("cite").matching(URL_PROTOCOLS).onElements("q")
             .allowElements("rp")
             .allowElements("rt")
             .allowElements("ruby")
diff --git a/tests/src/com/android/mail/utils/AdvancedHtmlSanitizerTest.java b/tests/src/com/android/mail/utils/AdvancedHtmlSanitizerTest.java
index 578ce4a..0e5715b 100644
--- a/tests/src/com/android/mail/utils/AdvancedHtmlSanitizerTest.java
+++ b/tests/src/com/android/mail/utils/AdvancedHtmlSanitizerTest.java
@@ -73,7 +73,9 @@
     }
 
     /**
-     * Technically, RFC 2392 doesn't limit where CID urls may appear; they are accepted everywhere.
+     * Technically, RFC 2392 doesn't limit where CID urls may appear. But, Webview is unhappy
+     * handling them within link tags, so we only allow them in img src attributes until we see a
+     * reason to expand their acceptance.
      */
     public void testCIDurls() {
         sanitize("<img src=\"http://www.here.com/awesome.png\"/>",
@@ -87,8 +89,7 @@
                 "<a href=\"http://www.here.com/awesome.png\"></a>");
         sanitize("<a href=\"https://www.here.com/awesome.png\"/>",
                 "<a href=\"https://www.here.com/awesome.png\"></a>");
-        sanitize("<a href=\"cid:ii_145bda161daf6f9c\"/>",
-                "<a href=\"cid:ii_145bda161daf6f9c\"></a>");
+        sanitize("<a href=\"cid:ii_145bda161daf6f9c\"/>", "");
     }
 
     // todo the stock CssSchema in OWASP does NOT allow the float property; I experiment with adding
diff --git a/tests/src/com/android/mail/utils/BasicHtmlSanitizerTest.java b/tests/src/com/android/mail/utils/BasicHtmlSanitizerTest.java
index 1da1ce0..5916162 100644
--- a/tests/src/com/android/mail/utils/BasicHtmlSanitizerTest.java
+++ b/tests/src/com/android/mail/utils/BasicHtmlSanitizerTest.java
@@ -21,6 +21,7 @@
         // allowed attributes
         sanitize("<a coords=\"something\"></a>", "<a coords=\"something\"></a>");
         sanitize("<a href=\"http://www.here.com\"></a>", "<a href=\"http://www.here.com\"></a>");
+        sanitize("<a href=\"https://www.here.com\"></a>", "<a href=\"https://www.here.com\"></a>");
         sanitize("<a name=\"something\"></a>", "<a name=\"something\"></a>");
         sanitize("<a shape=\"something\"></a>", "<a shape=\"something\"></a>");
 
@@ -30,6 +31,7 @@
         sanitize("<a datasrc=\"something\"></a>", "");
         sanitize("<a download=\"something\"></a>", "");
         sanitize("<a href=\"javascript:badness()\"></a>", "");
+        sanitize("<a href=\"cid:ii_hyw5v8ej0\"></a>", "");
         sanitize("<a hreflang=\"something\"></a>", "");
         sanitize("<a media=\"something\"></a>", "");
         sanitize("<a methods=\"something\"></a>", "");
@@ -70,6 +72,7 @@
         sanitize("<area alt=\"something\"/>", "<area alt=\"something\" />");
         sanitize("<area coords=\"something\"/>", "<area coords=\"something\" />");
         sanitize("<area href=\"http://www.here.com\"/>", "<area href=\"http://www.here.com\" />");
+        sanitize("<area href=\"https://www.here.com\"/>", "<area href=\"https://www.here.com\" />");
         sanitize("<area name=\"something\"/>", "<area name=\"something\" />");
         sanitize("<area nohref />", "<area nohref=\"nohref\" />");
         sanitize("<area shape=\"something\"/>", "<area shape=\"something\" />");
@@ -78,6 +81,7 @@
         sanitize("<area accessKey=\"A\"/>", "<area />");
         sanitize("<area download=\"something\"/>", "<area />");
         sanitize("<area href=\"javascript:badness()\"/>", "<area />");
+        sanitize("<area href=\"cid:ii_hyw5v8ej0\"/>", "<area />");
         sanitize("<area hreflang=\"something\"/>", "<area />");
         sanitize("<area media=\"something\"/>", "<area />");
         sanitize("<area rel=\"something\"/>", "<area />");
@@ -106,10 +110,13 @@
         // allowed attributes
         sanitize("<base href=\"http://www.example.com/\">",
                 "<base href=\"http://www.example.com/\" />");
+        sanitize("<base href=\"https://www.example.com/\">",
+                "<base href=\"https://www.example.com/\" />");
 
         // disallowed attributes
         sanitize("<base target=\"_blank\">", "<base />");
         sanitize("<base href=\"javascript:badness()\">", "<base />");
+        sanitize("<base href=\"cid:ii_hyw5v8ej0\">", "<base />");
         sanitize("<base href=\"javascript:alert('XSS');//\">", "<base />");
     }
 
@@ -485,11 +492,18 @@
         sanitize("<img width=\"22\"/>", "<img width=\"22\" />");
         sanitize("<img src=\"http://www.overhere.com/\"></img>",
                 "<img src=\"http://www.overhere.com/\" />");
+        sanitize("<img src=\"https://www.overhere.com/\"></img>",
+                "<img src=\"https://www.overhere.com/\" />");
+        sanitize("<img src=\"cid:ii_hyw5v8ej0\"></img>",
+                "<img src=\"cid:ii_hyw5v8ej0\" />");
         sanitize("<img longdesc=\"http://www.overhere.com/\"></img>",
                 "<img longdesc=\"http://www.overhere.com/\" />");
+        sanitize("<img longdesc=\"https://www.overhere.com/\"></img>",
+                "<img longdesc=\"https://www.overhere.com/\" />");
 
         sanitize("<img src=\"javascript:badness()\"></img>", "");
         sanitize("<img longdesc=\"javascript:badness()\"></img>", "");
+        sanitize("<img longdesc=\"cid:ii_hyw5v8ej0\"></img>", "");
         sanitize("<img src=javascript:alert('XSS')>", "");
         sanitize("<img src=JaVaScRiPt:alert('XSS')>", "");
         sanitize("<img src=javascript:alert(\"XSS\")>", "");
@@ -555,10 +569,16 @@
         sanitize("<input width=\"50\"/>", "<input width=\"50\" />");
         sanitize("<input src=\"http://www.overhere.com/\"></input>",
                 "<input src=\"http://www.overhere.com/\" />");
+        sanitize("<input src=\"https://www.overhere.com/\"></input>",
+                "<input src=\"https://www.overhere.com/\" />");
         sanitize("<input formaction=\"http://www.overhere.com/\"></input>",
                 "<input formaction=\"http://www.overhere.com/\" />");
+        sanitize("<input formaction=\"https://www.overhere.com/\"></input>",
+                "<input formaction=\"https://www.overhere.com/\" />");
 
+        sanitize("<input src=\"cid:ii_hyw5v8ej0\"></input>", "");
         sanitize("<input src=\"javascript:badness()\"></input>", "");
+        sanitize("<input formaction=\"cid:ii_hyw5v8ej0\"></input>", "");
         sanitize("<input formaction=\"javascript:badness()\"></input>", "");
         sanitize("<input type=\"image\" src=\"javascript:alert('XSS');\">",
                 "<input type=\"image\" />");
@@ -575,8 +595,13 @@
         sanitize("<ins cite=\"javascript:badness();\">something</ins>", "<ins>something</ins>");
         sanitize("<ins cite=\"http://www.reason.com/\">something</ins>",
                 "<ins cite=\"http://www.reason.com/\">something</ins>");
+        sanitize("<ins cite=\"https://www.reason.com/\">something</ins>",
+                "<ins cite=\"https://www.reason.com/\">something</ins>");
         sanitize("<ins datetime=\"something\">something</ins>",
                 "<ins datetime=\"something\">something</ins>");
+
+        sanitize("<ins cite=\"cid:ii_hyw5v8ej0\">something</ins>",
+                "<ins>something</ins>");
     }
 
     public void testKbd() {
@@ -614,6 +639,8 @@
     public void testLink() {
         sanitize("<link charset=\"utf8\"/>", "");
         sanitize("<link href=\"http://www.reason.com/\"/>", "");
+        sanitize("<link href=\"https://www.reason.com/\"/>", "");
+        sanitize("<link href=\"cid:ii_hyw5v8ej0\"/>", "");
         sanitize("<link hreflang=\"fr_CA\"/>", "");
         sanitize("<link media=\"tv\"/>", "");
         sanitize("<link rel=\"alternate\"/>", "");
@@ -655,6 +682,8 @@
                 "<menuitem disabled=\"disabled\"></menuitem>");
         sanitize("<menuitem icon=\"http://www.reason.com/\"></menuitem>",
                 "<menuitem icon=\"http://www.reason.com/\"></menuitem>");
+        sanitize("<menuitem icon=\"https://www.reason.com/\"></menuitem>",
+                "<menuitem icon=\"https://www.reason.com/\"></menuitem>");
         sanitize("<menuitem label=\"something\"></menuitem>",
                 "<menuitem label=\"something\"></menuitem>");
         sanitize("<menuitem type=\"checkbox\"></menuitem>",
@@ -662,6 +691,7 @@
         sanitize("<menuitem radiogroup=\"something\"></menuitem>",
                 "<menuitem radiogroup=\"something\"></menuitem>");
 
+        sanitize("<menuitem icon=\"cid:ii_hyw5v8ej0\"></menuitem>", "<menuitem></menuitem>");
         sanitize("<menuitem icon=\"javascript:badness()\"></menuitem>", "<menuitem></menuitem>");
     }
 
@@ -773,6 +803,10 @@
         sanitize("<q>something</q>", "<q>something</q>");
         sanitize("<q cite=\"http://www.reason.com/\">something</q>",
                 "<q cite=\"http://www.reason.com/\">something</q>");
+        sanitize("<q cite=\"https://www.reason.com/\">something</q>",
+                "<q cite=\"https://www.reason.com/\">something</q>");
+
+        sanitize("<q cite=\"cid:ii_hyw5v8ej0\">something</q>", "<q>something</q>");
         sanitize("<q cite=\"javascript:badness()\">something</q>", "<q>something</q>");
     }