Bug 1199796 - Refactor Request and XHR request method validation. r=nsm
authorJon Droniak <droniakj1@owls.southernct.edu>
Fri, 11 Sep 2015 11:26:33 -0700
changeset 262105 4d3bfb6ffeb10bd68f8bdeed08121899d007e7cb
parent 262104 4796f2a0cf56dea53edec64794ec5c0f724d4ac0
child 262106 757c4bd763261bc63a57ce9a4775406e67cbc85b
push id29359
push userphilringnalda@gmail.com
push dateSat, 12 Sep 2015 15:57:02 +0000
treeherdermozilla-central@cffdd225055e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnsm
bugs1199796
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1199796 - Refactor Request and XHR request method validation. r=nsm
dom/base/nsXMLHttpRequest.cpp
dom/fetch/FetchUtil.cpp
dom/fetch/FetchUtil.h
dom/fetch/Request.cpp
dom/fetch/moz.build
--- a/dom/base/nsXMLHttpRequest.cpp
+++ b/dom/base/nsXMLHttpRequest.cpp
@@ -8,16 +8,17 @@
 
 #ifndef XP_WIN
 #include <unistd.h>
 #endif
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/dom/BlobSet.h"
 #include "mozilla/dom/File.h"
+#include "mozilla/dom/FetchUtil.h"
 #include "mozilla/dom/XMLHttpRequestUploadBinding.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/MemoryReporting.h"
 #include "nsIDOMDocument.h"
 #include "mozilla/dom/ProgressEvent.h"
 #include "nsIJARChannel.h"
@@ -1612,41 +1613,20 @@ nsXMLHttpRequest::Open(const nsACString&
     GetOwner()->GetExtantDoc()->WarnOnceAbout(nsIDocument::eSyncXMLHttpRequest);
   }
 
   Telemetry::Accumulate(Telemetry::XMLHTTPREQUEST_ASYNC_OR_SYNC,
                         async ? 0 : 1);
 
   NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
 
-  // Disallow HTTP/1.1 TRACE method (see bug 302489)
-  // and MS IIS equivalent TRACK (see bug 381264)
-  // and CONNECT
-  if (inMethod.LowerCaseEqualsLiteral("trace") ||
-      inMethod.LowerCaseEqualsLiteral("connect") ||
-      inMethod.LowerCaseEqualsLiteral("track")) {
-    return NS_ERROR_DOM_SECURITY_ERR;
-  }
-
   nsAutoCString method;
-  // GET, POST, DELETE, HEAD, OPTIONS, PUT methods normalized to upper case
-  if (inMethod.LowerCaseEqualsLiteral("get")) {
-    method.AssignLiteral("GET");
-  } else if (inMethod.LowerCaseEqualsLiteral("post")) {
-    method.AssignLiteral("POST");
-  } else if (inMethod.LowerCaseEqualsLiteral("delete")) {
-    method.AssignLiteral("DELETE");
-  } else if (inMethod.LowerCaseEqualsLiteral("head")) {
-    method.AssignLiteral("HEAD");
-  } else if (inMethod.LowerCaseEqualsLiteral("options")) {
-    method.AssignLiteral("OPTIONS");
-  } else if (inMethod.LowerCaseEqualsLiteral("put")) {
-    method.AssignLiteral("PUT");
-  } else {
-    method = inMethod; // other methods are not normalized
+  nsresult rv = FetchUtil::GetValidRequestMethod(inMethod, method);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
   }
 
   // sync request is not allowed using withCredential or responseType
   // in window context
   if (!async && HasOrHasHadOwner() &&
       (mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS ||
        mTimeoutMilliseconds ||
        mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT)) {
@@ -1657,17 +1637,16 @@ nsXMLHttpRequest::Open(const nsACString&
       LogMessage("TimeoutSyncXHRWarning", GetOwner());
     }
     if (mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT) {
       LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
     }
     return NS_ERROR_DOM_INVALID_ACCESS_ERR;
   }
 
-  nsresult rv;
   nsCOMPtr<nsIURI> uri;
 
   if (mState & (XML_HTTP_REQUEST_OPENED |
                 XML_HTTP_REQUEST_HEADERS_RECEIVED |
                 XML_HTTP_REQUEST_LOADING |
                 XML_HTTP_REQUEST_SENT)) {
     // IE aborts as well
     Abort();
new file mode 100644
--- /dev/null
+++ b/dom/fetch/FetchUtil.cpp
@@ -0,0 +1,37 @@
+#include "FetchUtil.h"
+#include "nsError.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace dom {
+
+// static
+nsresult
+FetchUtil::GetValidRequestMethod(const nsACString& aMethod, nsCString& outMethod)
+{
+  nsAutoCString upperCaseMethod(aMethod);
+  ToUpperCase(upperCaseMethod);
+  if (upperCaseMethod.EqualsLiteral("CONNECT") ||
+      upperCaseMethod.EqualsLiteral("TRACE") ||
+      upperCaseMethod.EqualsLiteral("TRACK") ||
+      !NS_IsValidHTTPToken(aMethod)) {
+    outMethod.SetIsVoid(true);
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
+  if (upperCaseMethod.EqualsLiteral("DELETE") ||
+      upperCaseMethod.EqualsLiteral("GET") ||
+      upperCaseMethod.EqualsLiteral("HEAD") ||
+      upperCaseMethod.EqualsLiteral("OPTIONS") ||
+      upperCaseMethod.EqualsLiteral("POST") ||
+      upperCaseMethod.EqualsLiteral("PUT")) {
+    outMethod = upperCaseMethod;
+  }
+  else {
+    outMethod = aMethod; // Case unchanged for non-standard methods
+  }
+  return NS_OK;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/fetch/FetchUtil.h
@@ -0,0 +1,28 @@
+#ifndef mozilla_dom_FetchUtil_h
+#define mozilla_dom_FetchUtil_h
+
+#include "nsString.h"
+#include "nsError.h"
+
+namespace mozilla {
+namespace dom {
+
+class FetchUtil final
+{
+private:
+  FetchUtil() = delete;
+
+public:
+  /**
+  * Sets outMethod to a valid HTTP request method string based on an input method.
+  * Implements checks and normalization as specified by the Fetch specification.
+  * Returns NS_ERROR_DOM_SECURITY_ERR if the method is invalid.
+  * Otherwise returns NS_OK and the normalized method via outMethod.
+  */
+  static nsresult
+  GetValidRequestMethod(const nsACString& aMethod, nsCString& outMethod);
+};
+
+} // namespace dom
+} // namespace mozilla
+#endif
--- a/dom/fetch/Request.cpp
+++ b/dom/fetch/Request.cpp
@@ -7,16 +7,17 @@
 #include "Request.h"
 
 #include "nsIURI.h"
 #include "nsPIDOMWindow.h"
 
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/Headers.h"
 #include "mozilla/dom/Fetch.h"
+#include "mozilla/dom/FetchUtil.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/URL.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/workers/bindings/URL.h"
 #include "mozilla/unused.h"
 
 #include "WorkerPrivate.h"
 
@@ -293,44 +294,31 @@ Request::Constructor(const GlobalObject&
 
   if (aInit.mRedirect.WasPassed()) {
     request->SetRedirectMode(aInit.mRedirect.Value());
   }
 
   // Request constructor step 14.
   if (aInit.mMethod.WasPassed()) {
     nsAutoCString method(aInit.mMethod.Value());
-    nsAutoCString upperCaseMethod = method;
-    ToUpperCase(upperCaseMethod);
 
     // Step 14.1. Disallow forbidden methods, and anything that is not a HTTP
     // token, since HTTP states that Method may be any of the defined values or
     // a token (extension method).
-    if (upperCaseMethod.EqualsLiteral("CONNECT") ||
-        upperCaseMethod.EqualsLiteral("TRACE") ||
-        upperCaseMethod.EqualsLiteral("TRACK") ||
-        !NS_IsValidHTTPToken(method)) {
+    nsAutoCString outMethod;
+    nsresult rv = FetchUtil::GetValidRequestMethod(method, outMethod);
+    if (NS_FAILED(rv)) {
       NS_ConvertUTF8toUTF16 label(method);
       aRv.ThrowTypeError(MSG_INVALID_REQUEST_METHOD, &label);
       return nullptr;
     }
 
     // Step 14.2
-    if (upperCaseMethod.EqualsLiteral("DELETE") ||
-        upperCaseMethod.EqualsLiteral("GET") ||
-        upperCaseMethod.EqualsLiteral("HEAD") ||
-        upperCaseMethod.EqualsLiteral("POST") ||
-        upperCaseMethod.EqualsLiteral("PUT") ||
-        upperCaseMethod.EqualsLiteral("OPTIONS")) {
-      request->ClearCreatedByFetchEvent();
-      request->SetMethod(upperCaseMethod);
-    } else {
-      request->ClearCreatedByFetchEvent();
-      request->SetMethod(method);
-    }
+    request->ClearCreatedByFetchEvent();
+    request->SetMethod(outMethod);
   }
 
   nsRefPtr<InternalHeaders> requestHeaders = request->Headers();
 
   nsRefPtr<InternalHeaders> headers;
   if (aInit.mHeaders.WasPassed()) {
     nsRefPtr<Headers> h = Headers::Constructor(aGlobal, aInit.mHeaders.Value(), aRv);
     if (aRv.Failed()) {
--- a/dom/fetch/moz.build
+++ b/dom/fetch/moz.build
@@ -3,28 +3,30 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS.mozilla.dom += [
     'ChannelInfo.h',
     'Fetch.h',
     'FetchDriver.h',
+    'FetchUtil.h',
     'Headers.h',
     'InternalHeaders.h',
     'InternalRequest.h',
     'InternalResponse.h',
     'Request.h',
     'Response.h',
 ]
 
 UNIFIED_SOURCES += [
     'ChannelInfo.cpp',
     'Fetch.cpp',
     'FetchDriver.cpp',
+    'FetchUtil.cpp',
     'Headers.cpp',
     'InternalHeaders.cpp',
     'InternalRequest.cpp',
     'InternalResponse.cpp',
     'Request.cpp',
     'Response.cpp',
 ]