Bug 854849 - Sanitize displayable fields in manifests r=vingtetun a=tef+
authorFabrice Desré <fabrice@mozilla.com>
Thu, 18 Apr 2013 03:18:30 -0700
changeset 119130 54bdac994f98969781ce435ec81dc9337d71a7ff
parent 119129 5044b1c6392283847231f9361beab291aa26a4b4
child 119131 c5d79650594275eca07f6a4dc2dbf67dd4edfa41
push id688
push userfdesre@mozilla.com
push dateFri, 19 Apr 2013 09:11:30 +0000
reviewersvingtetun, tef
bugs854849
milestone18.0
Bug 854849 - Sanitize displayable fields in manifests r=vingtetun a=tef+
dom/apps/src/AppsUtils.jsm
dom/apps/tests/Makefile.in
dom/apps/tests/unit/test_manifestSanitizer.js
dom/apps/tests/unit/xpcshell.ini
dom/tests/mochitest/webapps/apps/utf8.webapp
dom/tests/mochitest/webapps/test_install_utf8.xul
testing/xpcshell/xpcshell.ini
--- a/dom/apps/src/AppsUtils.jsm
+++ b/dom/apps/src/AppsUtils.jsm
@@ -201,23 +201,72 @@ this.AppsUtils = {
     isCoreApp = app.basePath == this.getCoreAppsBasePath();
 #endif
     debug(app.name + " isCoreApp: " + isCoreApp);
     return { "basePath":  app.basePath + "/",
              "isCoreApp": isCoreApp };
   },
 
   /**
+    * Remove potential HTML tags from displayable fields in the manifest.
+    * We check name, description, developer name, and permission description
+    */
+  sanitizeManifest: function(aManifest) {
+    let sanitizer = Cc["@mozilla.org/parserutils;1"]
+                      .getService(Ci.nsIParserUtils);
+    if (!sanitizer) {
+      return;
+    }
+
+    function sanitize(aStr) {
+      return sanitizer.convertToPlainText(aStr,
+               Ci.nsIDocumentEncoder.OutputRaw, 0);
+    }
+
+    function sanitizeEntryPoint(aRoot) {
+      aRoot.name = sanitize(aRoot.name);
+
+      if (aRoot.description) {
+        aRoot.description = sanitize(aRoot.description);
+      }
+
+      if (aRoot.developer && aRoot.developer.name) {
+        aRoot.developer.name = sanitize(aRoot.developer.name);
+      }
+
+      if (aRoot.permissions) {
+        for (let permission in aRoot.permissions) {
+          if (aRoot.permissions[permission].description) {
+            aRoot.permissions[permission].description =
+             sanitize(aRoot.permissions[permission].description);
+          }
+        }
+      }
+    }
+
+    // First process the main section, then the entry points.
+    sanitizeEntryPoint(aManifest);
+
+    if (aManifest.entry_points) {
+      for (let entry in aManifest.entry_points) {
+        sanitizeEntryPoint(aManifest.entry_points[entry]);
+      }
+    }
+  },
+
+  /**
    * From https://developer.mozilla.org/en/OpenWebApps/The_Manifest
    * Only the name property is mandatory.
    */
   checkManifest: function(aManifest, app) {
     if (aManifest.name == undefined)
       return false;
 
+    this.sanitizeManifest(aManifest);
+
     // launch_path, entry_points launch paths, message hrefs, and activity hrefs can't be absolute
     if (aManifest.launch_path && isAbsoluteURI(aManifest.launch_path))
       return false;
 
     function checkAbsoluteEntryPoints(entryPoints) {
       for (let name in entryPoints) {
         if (entryPoints[name].launch_path && isAbsoluteURI(entryPoints[name].launch_path)) {
           return true;
--- a/dom/apps/tests/Makefile.in
+++ b/dom/apps/tests/Makefile.in
@@ -13,9 +13,11 @@ include $(DEPTH)/config/autoconf.mk
 
 DIRS = \
   $(NULL)
 
 MOCHITEST_CHROME_FILES = \
   test_apps_service.xul \
   $(NULL)
 
+XPCSHELL_TESTS = unit
+
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/apps/tests/unit/test_manifestSanitizer.js
@@ -0,0 +1,63 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function testEntryPoint(aRoot) {
+  do_check_true(aRoot.name == "hello world");
+  do_check_true(aRoot.description == "A bold name");
+  do_check_true(aRoot.developer.name == "Blink Inc.");
+
+  let permissions = aRoot.permissions;
+  do_check_true(permissions.contacts.description == "Required for autocompletion in the share screen");
+  do_check_true(permissions.alarms.description == "Required to schedule notifications");
+}
+
+function run_test() {
+  Components.utils.import("resource:///modules/AppsUtils.jsm");
+
+  do_check_true(!!AppsUtils);
+
+  // Test manifest, with one entry point.
+  let manifest = {
+    name: "hello <b>world</b>",
+    description: "A bold name",
+    developer: {
+      name: "<blink>Blink</blink> Inc.",
+      url: "http://blink.org"
+    },
+    permissions : {
+      "contacts": {
+        "description": "Required for autocompletion in the <a href='http://shareme.com'>share</a> screen",
+        "access": "readcreate"
+        },
+      "alarms": {
+        "description": "Required to schedule notifications"
+      }
+    },
+
+    entry_points: {
+      "subapp": {
+        name: "hello <b>world</b>",
+        description: "A bold name",
+        developer: {
+          name: "<blink>Blink</blink> Inc.",
+          url: "http://blink.org"
+        },
+        permissions : {
+          "contacts": {
+            "description": "Required for autocompletion in the <a href='http://shareme.com'>share</a> screen",
+            "access": "readcreate"
+            },
+          "alarms": {
+            "description": "Required to schedule notifications"
+          }
+        }
+      }
+    }
+  }
+
+  AppsUtils.sanitizeManifest(manifest);
+
+  // Check the main section and the subapp entry point.
+  testEntryPoint(manifest);
+  testEntryPoint(manifest.entry_points.subapp);
+}
new file mode 100644
--- /dev/null
+++ b/dom/apps/tests/unit/xpcshell.ini
@@ -0,0 +1,5 @@
+[DEFAULT]
+head =
+tail =
+
+[test_manifestSanitizer.js]
--- a/dom/tests/mochitest/webapps/apps/utf8.webapp
+++ b/dom/tests/mochitest/webapps/apps/utf8.webapp
@@ -1,5 +1,5 @@
 {
- "name": "TheBOM  ゲゴケ゚セニツ゚ヅヂチ",
+ "name": "TheBOM ゲゴケ゚セニツ゚ヅヂチ",
  "description": "This App is THE BOM, yo. ヅヂチ",
  "installs_allowed_from": ["*"]
 }
--- a/dom/tests/mochitest/webapps/test_install_utf8.xul
+++ b/dom/tests/mochitest/webapps/test_install_utf8.xul
@@ -19,17 +19,17 @@
 <script>
 
 SimpleTest.waitForExplicitFinish();
 
 var url = "http://test/chrome/dom/tests/mochitest/webapps/apps/utf8.webapp";
 
 confirmNextInstall();
 navigator.mozApps.install(url, null).onsuccess = function onInstall() {
-  is(this.result.manifest.name, "TheBOM  ゲゴケ゚セニツ゚ヅヂチ", "manifest.name");
+  is(this.result.manifest.name, "TheBOM ゲゴケ゚セニツ゚ヅヂチ", "manifest.name");
   is(this.result.manifest.description, "This App is THE BOM, yo. ヅヂチ",
      "manifest.description");
 
   navigator.mozApps.mgmt.uninstall(this.result).onsuccess = function onUninstall() {
     SimpleTest.finish();
   }
 };
 
--- a/testing/xpcshell/xpcshell.ini
+++ b/testing/xpcshell/xpcshell.ini
@@ -5,16 +5,17 @@
 [include:chrome/test/unit/xpcshell.ini]
 [include:intl/locale/tests/unit/xpcshell.ini]
 [include:netwerk/cookie/test/unit/xpcshell.ini]
 [include:modules/libjar/zipwriter/test/unit/xpcshell.ini]
 [include:uriloader/exthandler/tests/unit/xpcshell.ini]
 [include:parser/xml/test/unit/xpcshell.ini]
 [include:image/test/unit/xpcshell.ini]
 [include:dom/activities/tests/unit/xpcshell.ini]
+[include:dom/apps/tests/unit/xpcshell.ini]
 [include:dom/encoding/test/unit/xpcshell.ini]
 [include:dom/plugins/test/unit/xpcshell.ini]
 [include:dom/mobilemessage/tests/xpcshell.ini]
 [include:dom/mms/tests/xpcshell.ini]
 [include:dom/network/tests/unit/xpcshell.ini]
 [include:dom/network/tests/unit_ipc/xpcshell.ini]
 [include:dom/network/tests/unit_stats/xpcshell.ini]
 [include:dom/payment/tests/unit/xpcshell.ini]