Bug 1184967 P1 Set RequestMode based on LoadInfo securityMode and client request content policy. r=nsm

This commit is contained in:
Ben Kelly 2015-09-01 07:58:34 -07:00
parent 293122e98b
commit 6fa0c8de16
4 changed files with 101 additions and 38 deletions

View File

@ -206,8 +206,9 @@ InternalRequest::MapContentPolicyTypeToRequestContext(nsContentPolicyType aConte
return context; return context;
} }
// static
bool bool
InternalRequest::IsNavigationRequest() const InternalRequest::IsNavigationContentPolicy(nsContentPolicyType aContentPolicyType)
{ {
// https://fetch.spec.whatwg.org/#navigation-request-context // https://fetch.spec.whatwg.org/#navigation-request-context
// //
@ -215,17 +216,22 @@ InternalRequest::IsNavigationRequest() const
// "iframe", "internal" (as long as context frame type is not "none"), // "iframe", "internal" (as long as context frame type is not "none"),
// "location", "metarefresh", and "prerender". // "location", "metarefresh", and "prerender".
// //
// TODO: include equivalent check for "form" context // Note, all of these request types are effectively initiated by nsDocShell.
// TODO: include equivalent check for "prerender" context //
return mContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT || // The TYPE_REFRESH is used in some code paths for metarefresh, but will not
mContentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT || // be seen during the actual load. Instead the new load gets a normal
mContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FRAME || // nsDocShell policy type. We include it here in case this utility method
mContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IFRAME || // is called before the load starts.
mContentPolicyType == nsIContentPolicy::TYPE_REFRESH; return aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT ||
aContentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT ||
aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FRAME ||
aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
aContentPolicyType == nsIContentPolicy::TYPE_REFRESH;
} }
// static
bool bool
InternalRequest::IsWorkerRequest() const InternalRequest::IsWorkerContentPolicy(nsContentPolicyType aContentPolicyType)
{ {
// https://fetch.spec.whatwg.org/#worker-request-context // https://fetch.spec.whatwg.org/#worker-request-context
// //
@ -235,8 +241,20 @@ InternalRequest::IsWorkerRequest() const
// Note, service workers are not included here because currently there is // Note, service workers are not included here because currently there is
// no way to generate a Request with a "serviceworker" RequestContext. // no way to generate a Request with a "serviceworker" RequestContext.
// ServiceWorker scripts cannot be intercepted. // ServiceWorker scripts cannot be intercepted.
return mContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_WORKER || return aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_WORKER ||
mContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER; aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER;
}
bool
InternalRequest::IsNavigationRequest() const
{
return IsNavigationContentPolicy(mContentPolicyType);
}
bool
InternalRequest::IsWorkerRequest() const
{
return IsWorkerContentPolicy(mContentPolicyType);
} }
bool bool
@ -245,5 +263,63 @@ InternalRequest::IsClientRequest() const
return IsNavigationRequest() || IsWorkerRequest(); return IsNavigationRequest() || IsWorkerRequest();
} }
// static
RequestMode
InternalRequest::MapChannelToRequestMode(nsIChannel* aChannel)
{
MOZ_ASSERT(aChannel);
nsCOMPtr<nsILoadInfo> loadInfo;
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aChannel->GetLoadInfo(getter_AddRefs(loadInfo))));
// RequestMode deviates from our internal security mode for navigations.
// While navigations normally allow cross origin we must set a same-origin
// RequestMode to get the correct service worker interception restrictions
// in place.
// TODO: remove the worker override once securityMode is fully implemented (bug 1189945)
nsContentPolicyType contentPolicy = loadInfo->InternalContentPolicyType();
if (IsNavigationContentPolicy(contentPolicy) ||
IsWorkerContentPolicy(contentPolicy)) {
return RequestMode::Same_origin;
}
uint32_t securityMode;
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(loadInfo->GetSecurityMode(&securityMode)));
switch(securityMode) {
case nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS:
case nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED:
return RequestMode::Same_origin;
case nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS:
case nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL:
return RequestMode::No_cors;
case nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS:
// TODO: Check additional flag force-preflight after bug 1199693 (bug 1189945)
return RequestMode::Cors;
default:
// TODO: assert never reached after CorsMode flag removed (bug 1189945)
MOZ_ASSERT(securityMode == nsILoadInfo::SEC_NORMAL);
break;
}
// TODO: remove following code once securityMode is fully implemented (bug 1189945)
// We only support app:// protocol interception in non-release builds.
#ifndef RELEASE_BUILD
nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(aChannel);
if (jarChannel) {
return RequestMode::No_cors;
}
#endif
nsCOMPtr<nsIHttpChannelInternal> httpChannel = do_QueryInterface(aChannel);
uint32_t corsMode;
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(httpChannel->GetCorsMode(&corsMode)));
// This cast is valid due to static asserts in ServiceWorkerManager.cpp.
return static_cast<RequestMode>(corsMode);
}
} // namespace dom } // namespace dom
} // namespace mozilla } // namespace mozilla

View File

@ -377,6 +377,8 @@ public:
bool bool
IsClientRequest() const; IsClientRequest() const;
static RequestMode
MapChannelToRequestMode(nsIChannel* aChannel);
private: private:
// Does not copy mBodyStream. Use fallible Clone() for complete copy. // Does not copy mBodyStream. Use fallible Clone() for complete copy.
@ -387,6 +389,12 @@ private:
static RequestContext static RequestContext
MapContentPolicyTypeToRequestContext(nsContentPolicyType aContentPolicyType); MapContentPolicyTypeToRequestContext(nsContentPolicyType aContentPolicyType);
static bool
IsNavigationContentPolicy(nsContentPolicyType aContentPolicyType);
static bool
IsWorkerContentPolicy(nsContentPolicyType aContentPolicyType);
nsCString mMethod; nsCString mMethod;
// mURL always stores the url with the ref stripped // mURL always stores the url with the ref stripped
nsCString mURL; nsCString mURL;

View File

@ -150,7 +150,7 @@ class RespondWithHandler final : public PromiseNativeHandler
nsMainThreadPtrHandle<nsIInterceptedChannel> mInterceptedChannel; nsMainThreadPtrHandle<nsIInterceptedChannel> mInterceptedChannel;
nsMainThreadPtrHandle<ServiceWorker> mServiceWorker; nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
const RequestMode mRequestMode; const RequestMode mRequestMode;
const bool mIsClientRequest; const DebugOnly<bool> mIsClientRequest;
const bool mIsNavigationRequest; const bool mIsNavigationRequest;
public: public:
NS_DECL_ISUPPORTS NS_DECL_ISUPPORTS
@ -272,8 +272,6 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValu
// If one of the following conditions is true, return a network error: // If one of the following conditions is true, return a network error:
// * response's type is "error". // * response's type is "error".
// * request's mode is not "no-cors" and response's type is "opaque". // * request's mode is not "no-cors" and response's type is "opaque".
// * request is a client request and response's type is neither "basic"
// nor "default".
// * request is not a navigation request and response's type is // * request is not a navigation request and response's type is
// "opaqueredirect". // "opaqueredirect".
@ -282,19 +280,13 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValu
return; return;
} }
MOZ_ASSERT_IF(mIsClientRequest, mRequestMode == RequestMode::Same_origin);
if (response->Type() == ResponseType::Opaque && mRequestMode != RequestMode::No_cors) { if (response->Type() == ResponseType::Opaque && mRequestMode != RequestMode::No_cors) {
autoCancel.SetCancelStatus(NS_ERROR_BAD_OPAQUE_INTERCEPTION_REQUEST_MODE); autoCancel.SetCancelStatus(NS_ERROR_BAD_OPAQUE_INTERCEPTION_REQUEST_MODE);
return; return;
} }
// TODO: remove this case as its no longer in the spec (bug 1184967)
if (mIsClientRequest && response->Type() != ResponseType::Basic &&
response->Type() != ResponseType::Default &&
response->Type() != ResponseType::Opaqueredirect) {
autoCancel.SetCancelStatus(NS_ERROR_CLIENT_REQUEST_OPAQUE_INTERCEPTION);
return;
}
if (!mIsNavigationRequest && response->Type() == ResponseType::Opaqueredirect) { if (!mIsNavigationRequest && response->Type() == ResponseType::Opaqueredirect) {
autoCancel.SetCancelStatus(NS_ERROR_BAD_OPAQUE_REDIRECT_INTERCEPTION); autoCancel.SetCancelStatus(NS_ERROR_BAD_OPAQUE_REDIRECT_INTERCEPTION);
return; return;

View File

@ -3709,22 +3709,7 @@ public:
nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(httpChannel); nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(httpChannel);
NS_ENSURE_TRUE(internalChannel, NS_ERROR_NOT_AVAILABLE); NS_ENSURE_TRUE(internalChannel, NS_ERROR_NOT_AVAILABLE);
uint32_t corsMode; mRequestMode = InternalRequest::MapChannelToRequestMode(channel);
internalChannel->GetCorsMode(&corsMode);
switch (corsMode) {
case nsIHttpChannelInternal::CORS_MODE_SAME_ORIGIN:
mRequestMode = RequestMode::Same_origin;
break;
case nsIHttpChannelInternal::CORS_MODE_NO_CORS:
mRequestMode = RequestMode::No_cors;
break;
case nsIHttpChannelInternal::CORS_MODE_CORS:
case nsIHttpChannelInternal::CORS_MODE_CORS_WITH_FORCED_PREFLIGHT:
mRequestMode = RequestMode::Cors;
break;
default:
MOZ_CRASH("Unexpected CORS mode");
}
// This is safe due to static_asserts at top of file. // This is safe due to static_asserts at top of file.
uint32_t redirectMode; uint32_t redirectMode;
@ -3757,6 +3742,8 @@ public:
mMethod = "GET"; mMethod = "GET";
mRequestMode = InternalRequest::MapChannelToRequestMode(channel);
if (loadFlags & nsIRequest::LOAD_ANONYMOUS) { if (loadFlags & nsIRequest::LOAD_ANONYMOUS) {
mRequestCredentials = RequestCredentials::Omit; mRequestCredentials = RequestCredentials::Omit;
} }