Bug 1504470: Drop favicons above 2048x2048. r=mak
authorDave Townsend <dtownsend@oxymoronical.com>
Wed, 28 Nov 2018 13:38:42 -0800
changeset 508246 a8824f8d86c969f386003b6fa4c5cf7bb3fda5ec
parent 508245 4f3b16bba67cceea884be0ac9764bc2f8187ea41
child 508247 770d5f3d96f226d68137fe837c97e6d13239fe98
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmak
bugs1504470
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1504470: Drop favicons above 2048x2048. r=mak Differential Revision: https://phabricator.services.mozilla.com/D13362
browser/base/content/test/favicons/browser.ini
browser/base/content/test/favicons/browser_oversized.js
browser/base/content/test/favicons/large.png
browser/base/content/test/favicons/large_favicon.html
browser/modules/FaviconLoader.jsm
--- a/browser/base/content/test/favicons/browser.ini
+++ b/browser/base/content/test/favicons/browser.ini
@@ -61,8 +61,12 @@ support-files =
 support-files =
   file_with_slow_favicon.html
   blank.html
   file_favicon.png
 [browser_favicon_cache.js]
 support-files =
   cookie_favicon.sjs
   cookie_favicon.html
+[browser_oversized.js]
+support-files =
+  large_favicon.html
+  large.png
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/favicons/browser_oversized.js
@@ -0,0 +1,17 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const ROOT = "http://mochi.test:8888/browser/browser/base/content/test/favicons/";
+
+add_task(async () => {
+  await BrowserTestUtils.withNewTab({ gBrowser, url: "about:blank" }, async (browser) => {
+    let faviconPromise = waitForFaviconMessage();
+
+    BrowserTestUtils.loadURI(browser, ROOT + "large_favicon.html");
+    await BrowserTestUtils.browserLoaded(browser);
+
+    await Assert.rejects(faviconPromise, result => {
+      return result.iconURL == `${ROOT}large.png`;
+    }, "Should have failed to load the large icon.");
+  });
+});
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..37012cf9654185f73af0c253d445100f0c4b6821
GIT binary patch
literal 21237
zc%1Fpu}cDB9Eb7WrNkvz>TN-FHbhh0n42dTc_1RyZf!v{G$c(SAyHF<5Dtw&P!K_*
z4%#jC4>Z(JV^l-XP((#a_txAJT|9#L_4x4IJ0G`M$WKNi{Sgs~x>;vRMAtfBy}Ny7
zJGF2iVv6h7Gb=my7k0d9iFdU8n%Wz`t&Tq59WM^a=jWkO%=mNO()^XK^!Q7Lb85xr
zy8Knv4Rz6}sb!vo)r^m*wLy|3Ns=T<k|arzBuSDaNs=T<k|arzKTD3)OJ=D3Yn@)b
z3>KHdVkDHwW!18h>JH7y*8zY)AP@)y0)fE)4WG6iTk2haAe<Kn1OkCTATVGs#-m9f
z5C{YUfj}S-2m}IwKp+sv<4E9d!_~9uxkzHye;44-pBD%O0)apv5C{YUfj}S-7+_ea
nm(n4TvUT$|ed*b~a{Zm?+t*qH#=EzkY;^v*6M5$_GrRTya^m;t
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/favicons/large_favicon.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test file for bugs with favicons</title>
+
+  <!--Set a favicon; that's the whole point of this file.-->
+  <link rel="icon" href="large.png">
+</head>
+<body>
+  Test file for bugs with favicons
+</body>
+</html>
--- a/browser/modules/FaviconLoader.jsm
+++ b/browser/modules/FaviconLoader.jsm
@@ -41,16 +41,17 @@ const PREFERRED_WIDTH = 16;
 const LOCAL_FAVICON_SCHEMES = [
   "chrome",
   "about",
   "resource",
   "data",
 ];
 
 const MAX_FAVICON_EXPIRATION = 7 * 24 * 60 * 60 * 1000;
+const MAX_ICON_SIZE = 2048;
 
 const TYPE_ICO = "image/x-icon";
 const TYPE_SVG = "image/svg+xml";
 
 function promiseBlobAsDataURL(blob) {
   return new Promise((resolve, reject) => {
     let reader = new FileReader();
     reader.addEventListener("load", () => resolve(reader.result));
@@ -65,16 +66,31 @@ function promiseBlobAsOctets(blob) {
     reader.addEventListener("load", () => {
       resolve(Array.from(reader.result).map(c => c.charCodeAt(0)));
     });
     reader.addEventListener("error", reject);
     reader.readAsBinaryString(blob);
   });
 }
 
+function promiseImage(stream, type) {
+  return new Promise((resolve, reject) => {
+    let imgTools = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools);
+
+    imgTools.decodeImageAsync(stream, type, (image, result) => {
+      if (!Components.isSuccessCode(result)) {
+        reject();
+        return;
+      }
+
+      resolve(image);
+    }, Services.tm.currentThread);
+  });
+}
+
 class FaviconLoad {
   constructor(iconInfo) {
     this.icon = iconInfo;
 
     this.channel = Services.io.newChannelFromURI2(
       iconInfo.iconUri,
       iconInfo.node,
       iconInfo.node.nodePrincipal,
@@ -200,16 +216,27 @@ class FaviconLoad {
                       createInstance(Ci.nsIContentSniffer);
         type = sniffer.getMIMETypeFromContent(this.channel, octets, octets.length);
 
         if (!type) {
           throw Components.Exception(`Favicon at "${this.icon.iconUri.spec}" did not match a known mimetype.`, Cr.NS_ERROR_FAILURE);
         }
 
         blob = blob.slice(0, blob.size, type);
+
+        let image;
+        try {
+          image = await promiseImage(this.dataBuffer.newInputStream(0), type);
+        } catch (e) {
+          throw Components.Exception(`Favicon at "${this.icon.iconUri.spec}" could not be decoded.`, Cr.NS_ERROR_FAILURE);
+        }
+
+        if (image.width > MAX_ICON_SIZE || image.height > MAX_ICON_SIZE) {
+          throw Components.Exception(`Favicon at "${this.icon.iconUri.spec}" is too large.`, Cr.NS_ERROR_FAILURE);
+        }
       }
 
       let dataURL = await promiseBlobAsDataURL(blob);
 
       this._deferred.resolve({
         expiration,
         dataURL,
       });