Bug 511521 - downloading file with RTL override (RLO) presents conflicting filenames; r=smontagu sr=bzbarsky a1914=dveditz SEAMONKEY_2_0b2_BUILD1 SEAMONKEY_2_0b2_RELEASE
authorEhsan Akhgari <ehsan.akhgari@gmail.com>
Tue, 25 Aug 2009 13:52:47 +0430
changeset 26318 ac59325cf0a9acd9005a03085591e550ab26668b
parent 26317 f9e9ccea893dcf5cb8de7ceac6febcbf3ecb3bc4
child 26319 4eb7bf33094d0ffd6493e38df5226cff00c5c09e
child 26326 af54ebb954de3f63704130bc33fc5984332e5869
push id1937
push userehsan.akhgari@gmail.com
push dateThu, 03 Sep 2009 07:34:45 +0000
reviewerssmontagu, bzbarsky
bugs511521
milestone1.9.1.4pre
Bug 511521 - downloading file with RTL override (RLO) presents conflicting filenames; r=smontagu sr=bzbarsky a1914=dveditz
uriloader/exthandler/nsExternalHelperAppService.cpp
uriloader/exthandler/tests/mochitest/Makefile.in
uriloader/exthandler/tests/mochitest/test_unsafeBidiChars.xhtml
uriloader/exthandler/tests/mochitest/unsafeBidiFileName.sjs
--- a/uriloader/exthandler/nsExternalHelperAppService.cpp
+++ b/uriloader/exthandler/nsExternalHelperAppService.cpp
@@ -1117,16 +1117,29 @@ nsExternalAppHandler::nsExternalAppHandl
   // make sure the extention includes the '.'
   if (!aTempFileExtension.IsEmpty() && aTempFileExtension.First() != '.')
     mTempFileExtension = PRUnichar('.');
   AppendUTF8toUTF16(aTempFileExtension, mTempFileExtension);
 
   // replace platform specific path separator and illegal characters to avoid any confusion
   mSuggestedFileName.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS, '_');
   mTempFileExtension.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS, '_');
+
+  // Remove unsafe bidi characters which might have spoofing implications (bug 511521).
+  const PRUnichar unsafeBidiCharacters[] = {
+    PRUnichar(0x202a), // Left-to-Right Embedding
+    PRUnichar(0x202b), // Right-to-Left Embedding
+    PRUnichar(0x202c), // Pop Directional Formatting
+    PRUnichar(0x202d), // Left-to-Right Override
+    PRUnichar(0x202e)  // Right-to-Left Override
+  };
+  for (int i = 0; i < NS_ARRAY_LENGTH(unsafeBidiCharacters); ++i) {
+    mSuggestedFileName.ReplaceChar(unsafeBidiCharacters[i], '_');
+    mTempFileExtension.ReplaceChar(unsafeBidiCharacters[i], '_');
+  }
   
   // Make sure extension is correct.
   EnsureSuggestedFileName();
 
   gExtProtSvc->AddRef();
 }
 
 nsExternalAppHandler::~nsExternalAppHandler()
--- a/uriloader/exthandler/tests/mochitest/Makefile.in
+++ b/uriloader/exthandler/tests/mochitest/Makefile.in
@@ -41,14 +41,16 @@ srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir	= uriloader/exthandler/tests/mochitest 
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES =	\
 		test_handlerApps.xhtml \
+		test_unsafeBidiChars.xhtml \
 		handlerApps.js \
 		handlerApp.xhtml \
+		unsafeBidiFileName.sjs \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/uriloader/exthandler/tests/mochitest/test_unsafeBidiChars.xhtml
@@ -0,0 +1,135 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <title>Test for Handling of unsafe bidi chars</title>
+  <script type="text/javascript" src="/MochiKit/packed.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<iframe id="test"></iframe>
+<script type="text/javascript">
+<![CDATA[
+
+var unsafeBidiChars = {
+  LRE: "\xe2\x80\xaa",
+  RLE: "\xe2\x80\xab",
+  PDF: "\xe2\x80\xac",
+  LRO: "\xe2\x80\xad",
+  RLO: "\xe2\x80\xae"
+};
+
+var tests = [
+  "{1}.test",
+  "{1}File.test",
+  "Fi{1}le.test",
+  "File{1}.test",
+  "File.{1}test",
+  "File.te{1}st",
+  "File.test{1}",
+  "File.{1}",
+];
+
+function replace(name, x) {
+  return name.replace(/\{1\}/, x);
+}
+
+function sanitize(name) {
+  return replace(name, '_');
+}
+
+var gTests = [];
+function make_test(param, expected) {
+  gTests.push({
+    param: param,
+    expected: expected,
+  });
+}
+
+var iframe = document.getElementById("test");
+var gCallback = null;
+function run_test(test, cb) {
+  iframe.src = "unsafeBidiFileName.sjs?name=" + encodeURIComponent(test.param);
+  gCallback = cb;
+}
+
+var gCounter = -1;
+function run_next_test() {
+  if (++gCounter == gTests.length)
+    finish_test();
+  else
+    run_test(gTests[gCounter], run_next_test);
+}
+
+SimpleTest.waitForExplicitFinish();
+
+netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+
+const HELPERAPP_DIALOG_CID = "@mozilla.org/helperapplauncherdialog;1";
+const HELPERAPP_DIALOG_ID = Components.ID(Components.classes[HELPERAPP_DIALOG_CID].number);
+const HELPERAPP_DIALOG_DESCRIPTION = "Helper App Dialog Test Service";
+
+function HelperAppLauncherDialog() {}
+HelperAppLauncherDialog.prototype = {
+  REASON_CANTHANDLE: 0,
+  REASON_SERVERREQUEST: 1,
+  REASON_TYPESNIFFED: 2,
+  show: function(aLauncher, aWindowContext, aReason) {
+    netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+    var test = gTests[gCounter];
+    is(aLauncher.suggestedFileName, test.expected,
+       "The filename should be correctly sanitized");
+    gCallback();
+  },
+  promptForSaveToFile: function(aLauncher, aWindowContext, aDefaultFileName, aSuggestedFileExtension, aForcePrompt) {
+    return null;
+  },
+  QueryInterface: function(aIID) {
+    netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+    if (aIID.equals(Components.interfaces.nsISupports) ||
+        aIID.equals(Components.interfaces.nsIHelperAppLauncherDialog))
+      return this;
+    throw Components.results.NS_ERROR_NO_INTERFACE;
+  }
+};
+
+var factory = {
+  createInstance: function(aOuter, aIID) {
+    netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+    if (aOuter != null)
+      throw Components.results.NS_ERROR_NO_AGGREGATION;
+    return new HelperAppLauncherDialog().QueryInterface(aIID);
+  }
+};
+
+Components.manager
+          .QueryInterface(Components.interfaces.nsIComponentRegistrar)
+          .registerFactory(HELPERAPP_DIALOG_ID,
+                           HELPERAPP_DIALOG_DESCRIPTION,
+                           HELPERAPP_DIALOG_CID,
+                           factory);
+
+function finish_test() {
+  Components.manager
+            .QueryInterface(Components.interfaces.nsIComponentRegistrar)
+            .unregisterFactory(HELPERAPP_DIALOG_ID,
+                               factory);
+  SimpleTest.finish();
+}
+
+var i,j;
+
+for (i = 0; i < tests.length; ++i) {
+  for (j in unsafeBidiChars) {
+    make_test(replace(tests[i], unsafeBidiChars[j]),
+              sanitize(tests[i]));
+  }
+}
+
+run_next_test();
+
+]]>
+</script>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/uriloader/exthandler/tests/mochitest/unsafeBidiFileName.sjs
@@ -0,0 +1,47 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Handler Apps Tests.
+ *
+ * The Initial Developer of the Original Code is
+ * Ehsan Akhgari.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ehsan Akhgari <ehsan.akhgari@gmail.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+function handleRequest(request, response) {
+  response.setStatusLine(request.httpVersion, 200, "OK");
+
+  if (!request.queryString.match(/^name=/))
+    return;
+  var name = decodeURIComponent(request.queryString.substring(5));
+
+  response.setHeader("Content-Type", "application/octet-stream; name=\"" + name + "\"");
+  response.setHeader("Content-Disposition", "inline; filename=\"" + name + "\"");
+}