Bug 380994 - "Fix for bug 367428 lets through escaped slashes on Linux (windows too on trunk)" [r=biesi]
authorDaniel Veditz <dveditz@cruzio.com>
Wed, 01 Oct 2008 00:15:58 -0500
changeset 20026 6dad95d60106a7c3b0c52707f14fe8e519c13b85
parent 20025 6601dacc93f74df988a5ea414efa5504b73d0d2c
child 20027 1eccc541661cf86ef522cbfcbaf858df1682dc78
push id2583
push userreed@reedloden.com
push dateWed, 01 Oct 2008 05:17:03 +0000
treeherdermozilla-central@6dad95d60106 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbiesi
bugs380994, 367428
milestone1.9.1b1pre
Bug 380994 - "Fix for bug 367428 lets through escaped slashes on Linux (windows too on trunk)" [r=biesi]
netwerk/protocol/res/src/nsResProtocolHandler.cpp
netwerk/test/unit/test_bug380994.js
--- a/netwerk/protocol/res/src/nsResProtocolHandler.cpp
+++ b/netwerk/protocol/res/src/nsResProtocolHandler.cpp
@@ -17,16 +17,17 @@
  * The Initial Developer of the Original Code is
  * Netscape Communications Corporation.
  * Portions created by the Initial Developer are Copyright (C) 1998
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Darin Fisher <darin@netscape.com>
  *   Benjamin Smedberg <bsmedberg@covad.net>
+ *   Daniel Veditz <dveditz@cruzio.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either 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
@@ -171,16 +172,19 @@ nsResProtocolHandler::Init()
     // make resource://gre/ point to the GRE directory
     //
     rv = AddSpecialDir(NS_GRE_DIR, NS_LITERAL_CSTRING("gre"));
     NS_ENSURE_SUCCESS(rv, rv);
 
     //XXXbsmedberg Neil wants a resource://pchrome/ for the profile chrome dir...
     // but once I finish multiple chrome registration I'm not sure that it is needed
 
+    // XXX dveditz: resource://pchrome/ defeats profile directory salting
+    // if web content can load it. Tread carefully.
+
     return rv;
 }
 
 //----------------------------------------------------------------------------
 // nsResProtocolHandler::nsISupports
 //----------------------------------------------------------------------------
 
 NS_IMPL_THREADSAFE_ISUPPORTS3(nsResProtocolHandler,
@@ -224,17 +228,46 @@ nsResProtocolHandler::NewURI(const nsACS
     nsresult rv;
 
     nsResURL *resURL;
     NS_NEWXPCOM(resURL, nsResURL);
     if (!resURL)
         return NS_ERROR_OUT_OF_MEMORY;
     NS_ADDREF(resURL);
 
-    rv = resURL->Init(nsIStandardURL::URLTYPE_STANDARD, -1, aSpec, aCharset, aBaseURI);
+    // unescape any %2f and %2e to make sure nsStandardURL coalesces them.
+    // Later net_GetFileFromURLSpec() will do a full unescape and we want to
+    // treat them the same way the file system will. (bugs 380994, 394075)
+    nsCAutoString spec;
+    const char *src = aSpec.BeginReading();
+    const char *end = aSpec.EndReading();
+    const char *last = src;
+
+    spec.SetCapacity(aSpec.Length()+1);
+    for ( ; src < end; ++src) {
+        if (*src == '%' && (src < end-2) && *(src+1) == '2') {
+           char ch = '\0';
+           if (*(src+2) == 'f' || *(src+1) == 'F')
+             ch = '/';
+           else if (*(src+2) == 'e' || *(src+2) == 'E')
+             ch = '.';
+
+           if (ch) {
+             if (last < src)
+               spec.Append(last, src-last);
+             spec.Append(ch);
+             src += 2;
+             last = src+1; // src will be incremented by the loop
+           }
+        }
+    }
+    if (last < src)
+      spec.Append(last, src-last);
+
+    rv = resURL->Init(nsIStandardURL::URLTYPE_STANDARD, -1, spec, aCharset, aBaseURI);
     if (NS_SUCCEEDED(rv))
         rv = CallQueryInterface(resURL, result);
     NS_RELEASE(resURL);
     return rv;
 }
 
 NS_IMETHODIMP
 nsResProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_bug380994.js
@@ -0,0 +1,26 @@
+/* check resource: protocol for traversal problems */
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+
+const specs = [
+  "resource:///chrome/../plugins",
+  "resource:///chrome%2f../plugins",
+  "resource:///chrome/..%2fplugins",
+  "resource:///chrome%2f%2e%2e%2fplugins",
+  "resource:///../../../..",
+  "resource:///..%2f..%2f..%2f..",
+  "resource:///%2e%2e"
+];
+
+function run_test() {
+  var ios = Cc["@mozilla.org/network/io-service;1"].
+            getService(Ci.nsIIOService);
+
+  for each (spec in specs) {
+    uri = ios.newURI(spec,null,null);
+    if (uri.spec.indexOf("..") != -1)
+      do_throw("resource: traversal remains: '"+spec+"' ==> '"+uri.spec+"'");
+  }
+}