Bug 1461590: Lower-case hostnames when adding substitutions. r=smaug f=dveditz

Since URI hostnames are defined to be case-insensitive, we only ever see
lower-case hostnames when looking up substitutions. That means that
substitutions containing capital letters are inaccessible, which is a footgun
that has hit many people.

The handler should lower-case substitutions when they're added so that
look-ups are always case-insensitive.

MozReview-Commit-ID: C936hS2cSyY

--HG--
extra : rebase_source : a70e8ceb822879e51c3a40232b7dffdfb9c0a185
This commit is contained in:
Kris Maglione 2018-05-15 13:02:08 -07:00
parent 0d7a720986
commit 1ff74da18d
4 changed files with 64 additions and 5 deletions

View File

@ -14,6 +14,7 @@
#include "nsIFile.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
#include "nsReadableUtils.h"
#include "nsURLHelper.h"
#include "nsEscape.h"
@ -306,8 +307,11 @@ SubstitutingProtocolHandler::SetSubstitution(const nsACString& root, nsIURI *bas
}
nsresult
SubstitutingProtocolHandler::SetSubstitutionWithFlags(const nsACString& root, nsIURI *baseURI, uint32_t flags)
SubstitutingProtocolHandler::SetSubstitutionWithFlags(const nsACString& origRoot, nsIURI *baseURI, uint32_t flags)
{
nsAutoCString root;
ToLowerCase(origRoot, root);
if (!baseURI) {
mSubstitutions.Remove(root);
NotifyObservers(root, baseURI);
@ -349,10 +353,13 @@ SubstitutingProtocolHandler::SetSubstitutionWithFlags(const nsACString& root, ns
}
nsresult
SubstitutingProtocolHandler::GetSubstitution(const nsACString& root, nsIURI **result)
SubstitutingProtocolHandler::GetSubstitution(const nsACString& origRoot, nsIURI **result)
{
NS_ENSURE_ARG_POINTER(result);
nsAutoCString root;
ToLowerCase(origRoot, root);
SubstitutionEntry entry;
if (mSubstitutions.Get(root, &entry)) {
nsCOMPtr<nsIURI> baseURI = entry.baseURI;
@ -367,6 +374,12 @@ SubstitutingProtocolHandler::GetSubstitution(const nsACString& root, nsIURI **re
nsresult
SubstitutingProtocolHandler::GetSubstitutionFlags(const nsACString& root, uint32_t* flags)
{
#ifdef DEBUG
nsAutoCString lcRoot;
ToLowerCase(root, lcRoot);
MOZ_ASSERT(root.Equals(lcRoot), "GetSubstitutionFlags should never receive mixed-case root name");
#endif
*flags = 0;
SubstitutionEntry entry;
if (mSubstitutions.Get(root, &entry)) {
@ -379,9 +392,13 @@ SubstitutingProtocolHandler::GetSubstitutionFlags(const nsACString& root, uint32
}
nsresult
SubstitutingProtocolHandler::HasSubstitution(const nsACString& root, bool *result)
SubstitutingProtocolHandler::HasSubstitution(const nsACString& origRoot, bool *result)
{
NS_ENSURE_ARG_POINTER(result);
nsAutoCString root;
ToLowerCase(origRoot, root);
*result = HasSubstitution(root);
return NS_OK;
}

View File

@ -25,8 +25,8 @@ interface nsISubstitutingProtocolHandler : nsIProtocolHandler
*
* A null baseURI removes the specified substitution.
*
* A root key should always be lowercase; however, this may not be
* enforced.
* The root key will be converted to lower-case to conform to
* case-insensitive URI hostname matching behavior.
*/
[must_use] void setSubstitution(in ACString root, in nsIURI baseURI);

View File

@ -0,0 +1,41 @@
"use strict";
ChromeUtils.import("resource://gre/modules/Services.jsm");
add_task(async function test_case_insensitive_substitutions() {
let resProto = Services.io.getProtocolHandler("resource")
.QueryInterface(Ci.nsISubstitutingProtocolHandler);
let uri = Services.io.newFileURI(do_get_file("data"));
resProto.setSubstitution("FooBar", uri);
resProto.setSubstitutionWithFlags("BarBaz", uri, 0);
equal(resProto.resolveURI(Services.io.newURI("resource://foobar/")),
uri.spec, "Got correct resolved URI for setSubstitution");
equal(resProto.resolveURI(Services.io.newURI("resource://foobar/")),
uri.spec, "Got correct resolved URI for setSubstitutionWithFlags");
ok(resProto.hasSubstitution("foobar"), "hasSubstitution works with all-lower-case root");
ok(resProto.hasSubstitution("FooBar"), "hasSubstitution works with mixed-case root");
equal(resProto.getSubstitution("foobar").spec, uri.spec,
"getSubstitution works with all-lower-case root");
equal(resProto.getSubstitution("FooBar").spec, uri.spec,
"getSubstitution works with mixed-case root");
resProto.setSubstitution("foobar", null);
resProto.setSubstitution("barbaz", null);
Assert.throws(() => resProto.resolveURI(Services.io.newURI("resource://foobar/")),
e => e.result == Cr.NS_ERROR_NOT_AVAILABLE,
"Correctly unregistered case-insensitive substitution in setSubstitution");
Assert.throws(() => resProto.resolveURI(Services.io.newURI("resource://barbaz/")),
e => e.result == Cr.NS_ERROR_NOT_AVAILABLE,
"Correctly unregistered case-insensitive substitution in setSubstitutionWithFlags");
Assert.throws(() => resProto.getSubstitution("foobar"),
e => e.result == Cr.NS_ERROR_NOT_AVAILABLE,
"foobar substitution has been removed");
});

View File

@ -421,3 +421,4 @@ run-sequentially = node server exceptions dont replay well
# http2-using tests require node available
skip-if = os == "android"
[test_ioservice.js]
[test_substituting_protocol_handler.js]