diff --git a/netwerk/protocol/res/src/nsResProtocolHandler.cpp b/netwerk/protocol/res/src/nsResProtocolHandler.cpp index c9a426f77f5..27469187e17 100644 --- a/netwerk/protocol/res/src/nsResProtocolHandler.cpp +++ b/netwerk/protocol/res/src/nsResProtocolHandler.cpp @@ -22,6 +22,7 @@ * Contributor(s): * Darin Fisher * Benjamin Smedberg + * Daniel Veditz * * 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 @@ -176,6 +177,9 @@ nsResProtocolHandler::Init() //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; } @@ -229,7 +233,36 @@ nsResProtocolHandler::NewURI(const nsACString &aSpec, 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); diff --git a/netwerk/test/unit/test_bug380994.js b/netwerk/test/unit/test_bug380994.js new file mode 100644 index 00000000000..bf718c5c598 --- /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+"'"); + } +}