/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- *//* vim: set ts=8 sts=2 et sw=2 tw=80: *//* 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#include"nsIThreadRetargetableStreamListener.h"#include"mozilla/Assertions.h"#include"mozilla/Components.h"#include"mozilla/LinkedList.h"#include"mozilla/StaticPrefs_content.h"#include"mozilla/StoragePrincipalHelper.h"#include"nsCORSListenerProxy.h"#include"nsIHttpChannel.h"#include"HttpChannelChild.h"#include"nsIHttpChannelInternal.h"#include"nsContentUtils.h"#include"nsComponentManagerUtils.h"#include"nsIInterfaceRequestorUtils.h"#include"nsServiceManagerUtils.h"#include"nsStringStream.h"#include"nsWhitespaceTokenizer.h"#include"nsIChannelEventSink.h"#include"nsIDNSService.h"#include"nsIAsyncVerifyRedirectCallback.h"#include"nsCharSeparatedTokenizer.h"#include"nsAsyncRedirectVerifyHelper.h"#include"nsClassHashtable.h"#include"nsStreamUtils.h"#include"mozilla/Preferences.h"#include"nsIScriptError.h"#include"nsILoadContext.h"#include"nsIConsoleService.h"#include"nsICORSPreflightCache.h"#include"nsINetworkInterceptController.h"#include"nsICorsPreflightCallback.h"#include"nsISupportsImpl.h"#include"nsHttpChannel.h"#include"mozilla/BasePrincipal.h"#include"mozilla/ExpandedPrincipal.h"#include"mozilla/LoadInfo.h"#include"mozilla/NullPrincipal.h"#include"nsIHttpHeaderVisitor.h"#include"nsQueryObject.h"#include"mozilla/StaticPrefs_network.h"#include"mozilla/StaticPrefs_dom.h"#include"mozilla/dom/nsHTTPSOnlyUtils.h"#include"mozilla/dom/ReferrerInfo.h"#include"mozilla/dom/RequestBinding.h"#include"mozilla/glean/NetwerkProtocolHttpMetrics.h"usingnamespace mozilla::net;#definePREFLIGHT_CACHE_SIZE 100// 5 seconds is chosen to be compatible with Chromium.#definePREFLIGHT_DEFAULT_EXPIRY_SECONDS 5staticinlinensAutoStringGetStatusCodeAsString(nsIHttpChannel*aHttp) {if (NS_SUCCEEDED(aHttp->GetResponseStatus(&code))) {staticvoidLogBlockedRequest(nsIRequest*aRequest,constchar*aProperty,constchar16_t*aParam,uint32_taBlockingReason,nsIHttpChannel*aCreatingChannel,nsCOMPtr<nsIChannel>channel =do_QueryInterface(aRequest);NS_SetRequestBlockingReason(channel,aBlockingReason);channel->GetURI(getter_AddRefs(aUri));spec =aUri->GetSpecOrDefault();// Generate the error messagensAutoStringblockedMessage;AutoTArray<nsString, 2>params;CopyUTF8toUTF16(spec, *params.AppendElement());params.AppendElement(aParam);NS_ConvertUTF8toUTF16specUTF16(spec);rv =nsContentUtils::FormatLocalizedString(nsContentUtils::eSECURITY_PROPERTIES,aProperty,params,blockedMessage);NS_WARNING("Failed to log blocked cross-site request (no formalizedStr");nsAutoStringmsg(blockedMessage.get());nsDependentCStringcategory(aProperty);if (XRE_IsParentProcess()) {rv =aCreatingChannel->LogBlockedCORSRequest(msg,category,aIsWarning);"Failed to log blocked cross-site request to web console from ""parent->child, falling back to browser console");boolprivateBrowsing =false;nsCOMPtr<nsILoadGroup>loadGroup;rv =aRequest->GetLoadGroup(getter_AddRefs(loadGroup));NS_ENSURE_SUCCESS_VOID(rv);privateBrowsing =nsContentUtils::IsInPrivateBrowsing(loadGroup);boolfromChromeContext =false;nsCOMPtr<nsILoadInfo>loadInfo =channel->LoadInfo();fromChromeContext =loadInfo->TriggeringPrincipal()->IsSystemPrincipal();// we are passing aProperty as the category so we can link to the// appropriate MDN docs depending on the specific error.uint64_tinnerWindowID =nsContentUtils::GetInnerWindowID(aRequest);// The |innerWindowID| could be 0 if this request is created from script.// We can always try top level content window id in this case,// since the window id can lead to current top level window's web console.if (nsCOMPtr<nsIHttpChannel>httpChannel =do_QueryInterface(aRequest)) { (void)httpChannel->GetTopLevelContentWindowId(&innerWindowID);nsCORSListenerProxy::LogBlockedCORSRequest(innerWindowID,privateBrowsing,fromChromeContext,msg,category,//////////////////////////////////////////////////////////////////////////classnsPreflightCache :publicnsICORSPreflightCache {NS_DECL_NSICORSPREFLIGHTCACHEalready_AddRefed<CORSCacheEntry>GetEntry(nsIURI*aURI,nsIPrincipal*aPrincipal,boolaWithCredentials,const OriginAttributes&aOriginAttributes,boolaCreate);voidRemoveEntries(nsIURI*aURI,nsIPrincipal*aPrincipal,const OriginAttributes&aOriginAttributes);voidPurgePrivateBrowsingEntries();virtual ~nsPreflightCache() {Clear(); }nsRefPtrHashtable<nsCStringHashKey,CORSCacheEntry>mTable;LinkedList<CORSCacheEntry>mList;structCORSCacheEntry :publicLinkedListElement<CORSCacheEntry>,publicnsICORSPreflightCacheEntry {NS_DECL_NSICORSPREFLIGHTCACHEENTRYexplicitCORSCacheEntry(nsIURI*aUri,const OriginAttributes&aOriginAttributes,nsIPrincipal*aPrincipal,boolwithCredentials,mWithCredentials(withCredentials),voidPurgeExpired(TimeStampnow);boolCheckRequest(constnsCString&aMethod,constnsTArray<nsCString>&aHeaders);const OriginAttributesmOA;nsCOMPtr<nsIPrincipal>mPrincipal;nsCStringmKey;// serialized keyconstTimeStampmCreationTime{TimeStamp::NowLoRes()};nsTArray<nsPreflightCache::TokenTime>mMethods;nsTArray<nsPreflightCache::TokenTime>mHeaders;virtual ~CORSCacheEntry() =default;NS_IMPL_ISUPPORTS(nsPreflightCache,nsICORSPreflightCache)nsPreflightCache::GetEntries(nsTArray<RefPtr<nsICORSPreflightCacheEntry>>&aEntries) {for (autoiter =mTable.Iter(); !iter.Done();iter.Next()) {RefPtr<nsIPrincipal>iterPrincipal;iter.Data()->GetPrincipal(getter_AddRefs(iterPrincipal));if (iterPrincipal->Equals(aPrincipal)) {auto*entry =iter.UserData();aEntries.AppendElement(entry);nsPreflightCache::ClearEntry(nsICORSPreflightCacheEntry*entry) {nsresultrv =entry->GetURI(getter_AddRefs(uri));NS_ENSURE_SUCCESS(rv,rv);nsCOMPtr<nsIPrincipal>principal;rv =entry->GetPrincipal(getter_AddRefs(principal));NS_ENSURE_SUCCESS(rv,rv);const OriginAttributesoa =entry->OriginAttributesRef();RemoveEntries(uri,principal,oa);// Will be initialized in EnsurePreflightCache.staticStaticRefPtr<nsPreflightCache>sPreflightCache;staticboolEnsurePreflightCache() {if (sPreflightCache)returntrue;RefPtr<nsPreflightCache>newCache(newnsPreflightCache());sPreflightCache =newCache;voidnsPreflightCache::PurgePrivateBrowsingEntries() {for (autoiter =mTable.Iter(); !iter.Done();iter.Next()) {auto*entry =iter.UserData();if (entry->mOA.IsPrivateBrowsing()) {// last private browsing window closed, remove preflight cache entriesentry->removeFrom(sPreflightCache->mList);NS_IMPL_ISUPPORTS(CORSCacheEntry,nsICORSPreflightCacheEntry)CORSCacheEntry::GetKey(nsACString&aKey) {CORSCacheEntry::GetURI(nsIURI**aURI) { *aURI =do_AddRef(mURI).take();CORSCacheEntry::GetOriginAttributes(JSContext*aCx, JS::MutableHandle<JS::Value>aVal) {if (NS_WARN_IF(!ToJSValue(aCx,mOA,aVal))) {const OriginAttributes&CORSCacheEntry::OriginAttributesRef() {returnmOA; }CORSCacheEntry::GetPrincipal(nsIPrincipal**aPrincipal) { *aPrincipal =do_AddRef(mPrincipal).take();CORSCacheEntry::GetPrivateBrowsing(bool*aPrivateBrowsing) { *aPrivateBrowsing =mOA.IsPrivateBrowsing();CORSCacheEntry::GetWithCredentials(bool*aWithCredentials) { *aWithCredentials =mWithCredentials;voidCORSCacheEntry::PurgeExpired(TimeStampnow) {for (uint32_ti = 0,len =mMethods.Length();i <len; ++i) {if (now >=mMethods[i].expirationTime) {mMethods.UnorderedRemoveElementAt(i); --i;// Examine the element again, if necessary.for (uint32_ti = 0,len =mHeaders.Length();i <len; ++i) {if (now >=mHeaders[i].expirationTime) {mHeaders.UnorderedRemoveElementAt(i); --i;// Examine the element again, if necessary.boolCORSCacheEntry::CheckDNSCache() {// When proxy is used, the DNS lookup is done by proxy, so we skip this check.nsCOMPtr<nsIDNSService>dns;dns = mozilla::components::DNS::Service();if (NS_FAILED(mURI->GetAsciiHost(host))) {nsCOMPtr<nsIDNSRecord>record;nsresultrv =dns->ResolveNative(host,nsIDNSService::RESOLVE_OFFLINE,mOA,if (NS_FAILED(rv) || !record) {nsCOMPtr<nsIDNSAddrRecord>addrRec =do_QueryInterface(record); (void)addrRec->GetLastUpdate(&lastUpdate);if (lastUpdate >mCreationTime) {boolCORSCacheEntry::CheckRequest(constnsCString&aMethod,constnsTArray<nsCString>&aHeaders) {PurgeExpired(TimeStamp::NowLoRes());if (!aMethod.EqualsLiteral("GET") && !aMethod.EqualsLiteral("POST")) {boolEquals(constnsPreflightCache::TokenTime&e,constnsCString&method)const {returne.token.Equals(method);if (!mMethods.Contains(aMethod,CheckToken())) {boolEquals(constnsPreflightCache::TokenTime&e,constnsCString&header)const {returne.token.Equals(header,nsCaseInsensitiveCStringComparator);for (uint32_ti = 0;i <aHeaders.Length(); ++i) {if (!mHeaders.Contains(aHeaders[i],checker)) {already_AddRefed<CORSCacheEntry>nsPreflightCache::GetEntry(nsIURI*aURI,nsIPrincipal*aPrincipal,boolaWithCredentials,const OriginAttributes&aOriginAttributes,boolaCreate) {if (NS_FAILED(aPrincipal->GetPrefLightCacheKey(aURI,aWithCredentials,aOriginAttributes,key))) {NS_WARNING("Invalid cache key!");RefPtr<CORSCacheEntry>existingEntry =nullptr;if ((existingEntry =mTable.Get(key))) {if (existingEntry->mDoomed) {existingEntry->removeFrom(mList);// Entry already existed so just return it. Also update the LRU list.// Move to the head of the list.existingEntry->removeFrom(mList);mList.insertFront(existingEntry);returnexistingEntry.forget();// This is a new entry, allocate and insert into the table now so that any// failures don't cause items to be removed from a full cache.RefPtr<CORSCacheEntry>newEntry =newCORSCacheEntry(aURI,aOriginAttributes,aPrincipal,aWithCredentials,key);NS_ASSERTION(mTable.Count() <=PREFLIGHT_CACHE_SIZE,"Something is borked, too many entries in the cache!");// Now enforce the max count.if (mTable.Count() ==PREFLIGHT_CACHE_SIZE) {// Try to kick out all the expired entries.TimeStampnow =TimeStamp::NowLoRes();for (autoiter =mTable.Iter(); !iter.Done();iter.Next()) {auto*entry =iter.UserData();entry->PurgeExpired(now);if (entry->mHeaders.IsEmpty() &&entry->mMethods.IsEmpty()) {// Expired, remove from the list as well as the hash table.entry->removeFrom(sPreflightCache->mList);// If that didn't remove anything then kick out the least recently usedif (mTable.Count() ==PREFLIGHT_CACHE_SIZE) {CORSCacheEntry*lruEntry =static_cast<CORSCacheEntry*>(mList.popLast());// This will delete 'lruEntry'.lruEntry->GetKey(lruKey);NS_ASSERTION(mTable.Count() ==PREFLIGHT_CACHE_SIZE - 1,"Somehow tried to remove an entry that was never added!");CORSCacheEntry*newEntryWeak =newEntry.get();mTable.InsertOrUpdate(key,newEntry);mList.insertFront(newEntryWeak);voidnsPreflightCache::RemoveEntries(nsIURI*aURI,nsIPrincipal*aPrincipal,const OriginAttributes&aOriginAttributes) {RefPtr<CORSCacheEntry>entry;if (NS_SUCCEEDED(aPrincipal->GetPrefLightCacheKey(aURI,true,aOriginAttributes,key))) {if ((entry =mTable.Get(key))) {entry->removeFrom(mList);if (NS_SUCCEEDED(aPrincipal->GetPrefLightCacheKey(aURI,false,aOriginAttributes,key))) {if ((entry =mTable.Get(key))) {entry->removeFrom(mList);voidnsPreflightCache::Clear() {//////////////////////////////////////////////////////////////////////////NS_IMPL_ISUPPORTS(nsCORSListenerProxy,nsIStreamListener,nsIRequestObserver,nsIChannelEventSink,nsIInterfaceRequestor,nsIThreadRetargetableStreamListener)already_AddRefed<nsICORSPreflightCache>nsCORSListenerProxy::GetCORSPreflightSingleton() {NS_ASSERTION(!IsNeckoChild(),"not a parent process");if (!EnsurePreflightCache()) {NS_ASSERTION(false,"Failed to get the preflightCache");returndo_AddRef(sPreflightCache);voidnsCORSListenerProxy::Shutdown() {sPreflightCache =nullptr; }voidnsCORSListenerProxy::ClearCache() {sPreflightCache->Clear();voidnsCORSListenerProxy::ClearPrivateBrowsingCache() {sPreflightCache->PurgePrivateBrowsingEntries();// Usually, when using an expanded principal, there's no particularly good// origin to do the request with. However if the expanded principal only wraps// one principal, we can use that one instead.// This is needed so that DevTools can still do CORS-enabled requests (since// DevTools uses a triggering principal expanding the node principal to bypass// CSP checks, see Element::CreateDevToolsPrincipal(),bug 1604562, and bugstaticnsIPrincipal*GetOriginHeaderPrincipal(nsIPrincipal*aPrincipal) {while (aPrincipal &&aPrincipal->GetIsExpandedPrincipal()) {auto*ep =BasePrincipal::Cast(aPrincipal)->As<ExpandedPrincipal>();if (ep->AllowList().Length() != 1) {aPrincipal =ep->AllowList()[0];nsCORSListenerProxy::nsCORSListenerProxy(nsIStreamListener*aOuter,nsIPrincipal*aRequestingPrincipal,mRequestingPrincipal(aRequestingPrincipal),mOriginHeaderPrincipal(GetOriginHeaderPrincipal(aRequestingPrincipal)),mWithCredentials(aWithCredentials),mHasBeenCrossSite(false),mMutex("nsCORSListenerProxy") {nsresultnsCORSListenerProxy::Init(nsIChannel*aChannel,DataURIHandlingaAllowDataURI) {aChannel->GetNotificationCallbacks(getter_AddRefs(mOuterNotificationCallbacks));aChannel->SetNotificationCallbacks(this);UpdateChannel(aChannel,aAllowDataURI,UpdateType::Default,false);MutexAutoLocklock(mMutex);mRequestingPrincipal =nullptr;mOriginHeaderPrincipal =nullptr;mOuterNotificationCallbacks =nullptr;nsCORSListenerProxy::OnStartRequest(nsIRequest*aRequest) {MOZ_ASSERT(mInited,"nsCORSListenerProxy has not been initialized properly");nsresultrv =CheckRequestApproved(aRequest);mRequestApproved =NS_SUCCEEDED(rv);nsCOMPtr<nsIChannel>channel =do_QueryInterface(aRequest);NS_GetFinalChannelURI(channel,getter_AddRefs(uri));StoragePrincipalHelper::GetOriginAttributesForNetworkState(channel,// OK to use mRequestingPrincipal since preflights never getsPreflightCache->RemoveEntries(uri,mRequestingPrincipal,attrs);nsCOMPtr<nsIHttpChannelChild>httpChannelChild =do_QueryInterface(channel);rv =httpChannelChild->RemoveCorsPreflightCacheEntry(uri,mRequestingPrincipal,attrs);// Only warn here to ensure we fall through the request Cancel()// and outer listener OnStartRequest() calls.NS_WARNING("Failed to remove CORS preflight cache entry!");aRequest->Cancel(NS_ERROR_DOM_BAD_URI);nsCOMPtr<nsIStreamListener>listener;MutexAutoLocklock(mMutex);listener =mOuterListener;listener->OnStartRequest(aRequest);// Reason for NS_ERROR_DOM_BAD_URI already logged in CheckRequestApproved()returnNS_ERROR_DOM_BAD_URI;nsCOMPtr<nsIStreamListener>listener;MutexAutoLocklock(mMutex);listener =mOuterListener;returnlistener->OnStartRequest(aRequest);classCheckOriginHeader final :publicnsIHttpHeaderVisitor {CheckOriginHeader() =default;VisitHeader(constnsACString&aHeader,constnsACString&aValue) override {if (aHeader.EqualsLiteral("Access-Control-Allow-Origin")) {returnNS_ERROR_DOM_BAD_URI; ~CheckOriginHeader() =default;NS_IMPL_ISUPPORTS(CheckOriginHeader,nsIHttpHeaderVisitor)nsresultnsCORSListenerProxy::CheckRequestApproved(nsIRequest*aRequest) {// Check if this was actually a cross domain requestif (!mHasBeenCrossSite) {nsCOMPtr<nsIHttpChannel>topChannel;topChannel.swap(mHttpChannel);if (StaticPrefs::content_cors_disable()) {LogBlockedRequest(aRequest,"CORSDisabled",nullptr,nsILoadInfo::BLOCKING_REASON_CORSDISABLED,topChannel);returnNS_ERROR_DOM_BAD_URI;// Check if the request failednsresultrv =aRequest->GetStatus(&status);LogBlockedRequest(aRequest,"CORSDidNotSucceed2",nullptr,nsILoadInfo::BLOCKING_REASON_CORSDIDNOTSUCCEED,if (NS_BINDING_ABORTED !=status) {// Don't want to log mere cancellation as an error.LogBlockedRequest(aRequest,"CORSDidNotSucceed2",nullptr,nsILoadInfo::BLOCKING_REASON_CORSDIDNOTSUCCEED,// Test that things worked on a HTTP levelnsCOMPtr<nsIHttpChannel>http =do_QueryInterface(aRequest);nsCOMPtr<nsIChannel>channel =do_QueryInterface(aRequest);NS_GetFinalChannelURI(channel,getter_AddRefs(uri));if (uri &&uri->SchemeIs("moz-extension")) {// moz-extension:-URLs do not support CORS, but can universally be read// if an extension lists the resource in web_accessible_resources.// Access will be checked in UpdateChannel.LogBlockedRequest(aRequest,"CORSRequestNotHttp",nullptr,nsILoadInfo::BLOCKING_REASON_CORSREQUESTNOTHTTP,returnNS_ERROR_DOM_BAD_URI;nsCOMPtr<nsILoadInfo>loadInfo =http->LoadInfo();if (loadInfo->GetServiceWorkerTaintingSynthesized()) {// For synthesized responses, we don't need to perform any checks.// Note: This would be unsafe if we ever changed our behavior to allow// service workers to intercept CORS preflights.// Check the Access-Control-Allow-Origin headerRefPtr<CheckOriginHeader>visitor =newCheckOriginHeader();nsAutoCStringallowedOriginHeader;// check for duplicate headersrv =http->VisitOriginalResponseHeaders(visitor);aRequest,"CORSMultipleAllowOriginNotAllowed",nullptr,nsILoadInfo::BLOCKING_REASON_CORSMULTIPLEALLOWORIGINNOTALLOWED,rv =http->GetResponseHeader("Access-Control-Allow-Origin"_ns,autostatusCode =GetStatusCodeAsString(http);LogBlockedRequest(aRequest,"CORSMissingAllowOrigin2",statusCode.get(),nsILoadInfo::BLOCKING_REASON_CORSMISSINGALLOWORIGIN,//Bug 1210985 - Explicitly point out the error that the credential is// not supported if the allowing origin is '*'. Note that this check// has to be done before the condition// >> if (mWithCredentials || !allowedOriginHeader.EqualsLiteral("*"))// below since "if (A && B)" is included in "if (A || !B)".if (mWithCredentials &&allowedOriginHeader.EqualsLiteral("*")) {LogBlockedRequest(aRequest,"CORSNotSupportingCredentials",nullptr,nsILoadInfo::BLOCKING_REASON_CORSNOTSUPPORTINGCREDENTIALS,returnNS_ERROR_DOM_BAD_URI;if (mWithCredentials || !allowedOriginHeader.EqualsLiteral("*")) {MOZ_ASSERT(!mOriginHeaderPrincipal->GetIsExpandedPrincipal());mOriginHeaderPrincipal->GetWebExposedOriginSerialization(origin);if (!allowedOriginHeader.Equals(origin)) {aRequest,"CORSAllowOriginNotMatchingOrigin",NS_ConvertUTF8toUTF16(allowedOriginHeader).get(),nsILoadInfo::BLOCKING_REASON_CORSALLOWORIGINNOTMATCHINGORIGIN,returnNS_ERROR_DOM_BAD_URI;// Check Access-Control-Allow-Credentials headernsAutoCStringallowCredentialsHeader;rv =http->GetResponseHeader("Access-Control-Allow-Credentials"_ns,if (!allowCredentialsHeader.EqualsLiteral("true")) {aRequest,"CORSMissingAllowCredentials",nullptr,nsILoadInfo::BLOCKING_REASON_CORSMISSINGALLOWCREDENTIALS,topChannel);returnNS_ERROR_DOM_BAD_URI;nsCORSListenerProxy::OnStopRequest(nsIRequest*aRequest,nsresultaStatusCode) {MOZ_ASSERT(mInited,"nsCORSListenerProxy has not been initialized properly");nsCOMPtr<nsIStreamListener>listener;MutexAutoLocklock(mMutex);listener = std::move(mOuterListener);nsresultrv =listener->OnStopRequest(aRequest,aStatusCode);mOuterNotificationCallbacks =nullptr;nsCORSListenerProxy::OnDataAvailable(nsIRequest*aRequest,nsIInputStream*aInputStream,uint64_taOffset,uint32_taCount) {// NB: This can be called on any thread! But we're guaranteed that it is// called between OnStartRequest and OnStopRequest, so we don't need to worryMOZ_ASSERT(mInited,"nsCORSListenerProxy has not been initialized properly");// Reason for NS_ERROR_DOM_BAD_URI already logged in CheckRequestApproved()returnNS_ERROR_DOM_BAD_URI;nsCOMPtr<nsIStreamListener>listener;MutexAutoLocklock(mMutex);listener =mOuterListener;returnlistener->OnDataAvailable(aRequest,aInputStream,aOffset,aCount);nsCORSListenerProxy::OnDataFinished(nsresultaStatus) {nsCOMPtr<nsIStreamListener>listener;MutexAutoLocklock(mMutex);listener =mOuterListener;nsCOMPtr<nsIThreadRetargetableStreamListener>retargetableListener =do_QueryInterface(listener);if (retargetableListener) {returnretargetableListener->OnDataFinished(aStatus);voidnsCORSListenerProxy::SetInterceptController(nsINetworkInterceptController*aInterceptController) {mInterceptController =aInterceptController;nsCORSListenerProxy::GetInterface(constnsIID&aIID,void**aResult) {if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) { *aResult =static_cast<nsIChannelEventSink*>(this);if (aIID.Equals(NS_GET_IID(nsINetworkInterceptController)) &&nsCOMPtr<nsINetworkInterceptController>copy(mInterceptController); *aResult =copy.forget().take();returnmOuterNotificationCallbacks ?mOuterNotificationCallbacks->GetInterface(aIID,aResult)nsCORSListenerProxy::AsyncOnChannelRedirect(nsIChannel*aOldChannel,nsIChannel*aNewChannel,uint32_taFlags,nsIAsyncVerifyRedirectCallback*aCb) {if (NS_IsInternalSameURIRedirect(aOldChannel,aNewChannel,aFlags) ||NS_IsHSTSUpgradeRedirect(aOldChannel,aNewChannel,aFlags)) {// Internal redirects still need to be updated in order to maintain// the correct headers. We use DataURIHandling::Allow, since unallowed// data URIs should have been blocked before we got to the internalrv =UpdateChannel(aNewChannel,DataURIHandling::Allow,UpdateType::InternalOrHSTSRedirect,false);"nsCORSListenerProxy::AsyncOnChannelRedirect: ""internal redirect UpdateChannel() returned failure");// A real, external redirect. Perform CORS checking on new URL.rv =CheckRequestApproved(aOldChannel);NS_GetFinalChannelURI(aOldChannel,getter_AddRefs(oldURI));StoragePrincipalHelper::GetOriginAttributesForNetworkState(aOldChannel,// OK to use mRequestingPrincipal since preflights never getsPreflightCache->RemoveEntries(oldURI,mRequestingPrincipal,attrs);nsCOMPtr<nsIHttpChannelChild>httpChannelChild =do_QueryInterface(aOldChannel);rv =httpChannelChild->RemoveCorsPreflightCacheEntry(oldURI,mRequestingPrincipal,attrs);// Only warn here to ensure we call the channel Cancel() belowNS_WARNING("Failed to remove CORS preflight cache entry!");aOldChannel->Cancel(NS_ERROR_DOM_BAD_URI);// Reason for NS_ERROR_DOM_BAD_URI already logged in// CheckRequestApproved()returnNS_ERROR_DOM_BAD_URI;// Once we've been cross-site, cross-origin redirects reset our source// origin. Note that we need to call GetChannelURIPrincipal() because// we are looking for the principal that is actually being loaded and not// the principal that initiated the load.nsCOMPtr<nsIPrincipal>oldChannelPrincipal;nsContentUtils::GetSecurityManager()->GetChannelURIPrincipal(aOldChannel,getter_AddRefs(oldChannelPrincipal));nsCOMPtr<nsIPrincipal>newChannelPrincipal;nsContentUtils::GetSecurityManager()->GetChannelURIPrincipal(aNewChannel,getter_AddRefs(newChannelPrincipal));if (!oldChannelPrincipal || !newChannelPrincipal) {rv =NS_ERROR_OUT_OF_MEMORY;if (!oldChannelPrincipal->Equals(newChannelPrincipal)) {// Spec says to set our source origin to a unique origin.NullPrincipal::CreateWithInheritedAttributes(oldChannelPrincipal);// We need to strip auth header from preflight request for// cross-origin redirects.NS_ShouldRemoveAuthHeaderOnRedirect(aOldChannel,aNewChannel,aFlags);nsCOMPtr<nsIHttpChannel>oldHttpChannel =do_QueryInterface(aOldChannel); (void)oldHttpChannel->GetRequestMethod(method); (void)oldHttpChannel->ShouldStripRequestBodyHeader(method, &rewriteToGET);aNewChannel,DataURIHandling::Disallow,rewriteToGET ?UpdateType::StripRequestBodyHeader :UpdateType::Default,"nsCORSListenerProxy::AsyncOnChannelRedirect: ""UpdateChannel() returned failure");nsCOMPtr<nsIChannelEventSink>outer =do_GetInterface(mOuterNotificationCallbacks);returnouter->AsyncOnChannelRedirect(aOldChannel,aNewChannel,aFlags,aCb);aCb->OnRedirectVerifyCallback(NS_OK);nsCORSListenerProxy::CheckListenerChain() {MOZ_ASSERT(NS_IsMainThread());nsCOMPtr<nsIThreadRetargetableStreamListener>retargetableListener;MutexAutoLocklock(mMutex);retargetableListener =do_QueryInterface(mOuterListener);if (!retargetableListener) {returnNS_ERROR_NO_INTERFACE;returnretargetableListener->CheckListenerChain();// Please note that the CSP directive 'upgrade-insecure-requests' and the// HTTPS-Only Mode are relying on the promise that channels get updated from// http: to https: before the channel fetches any data from the netwerk. Such// channels should not be blocked by CORS and marked as cross origin requests.// In such a case we should bail out of CORS and rely on the promise that// nsHttpChannel::Connect() upgrades the request from http to https.boolCheckInsecureUpgradePreventsCORS(nsIPrincipal*aRequestingPrincipal,nsCOMPtr<nsIURI>channelURI;nsresultrv =NS_GetFinalChannelURI(aChannel,getter_AddRefs(channelURI));NS_ENSURE_SUCCESS(rv,false);// upgrade insecure requests is only applicable to http requestsif (!channelURI->SchemeIs("http")) {nsCOMPtr<nsIURI>originalURI;rv =aChannel->GetOriginalURI(getter_AddRefs(originalURI));NS_ENSURE_SUCCESS(rv,false);nsAutoCStringprincipalHost,channelHost,origChannelHost;// if we can not query a host from the uri, there is nothing to doif (NS_FAILED(aRequestingPrincipal->GetAsciiHost(principalHost)) ||NS_FAILED(channelURI->GetAsciiHost(channelHost)) ||NS_FAILED(originalURI->GetAsciiHost(origChannelHost))) {// if the hosts do not match, there is nothing to doif (!principalHost.EqualsIgnoreCase(channelHost.get())) {// also check that uri matches the one of the originalURIif (!channelHost.EqualsIgnoreCase(origChannelHost.get())) {nsresultnsCORSListenerProxy::UpdateChannel(nsIChannel*aChannel,DataURIHandlingaAllowDataURI,MOZ_ASSERT_IF(aUpdateType ==UpdateType::InternalOrHSTSRedirect,nsCOMPtr<nsIURI>uri,originalURI;nsresultrv =NS_GetFinalChannelURI(aChannel,getter_AddRefs(uri));NS_ENSURE_SUCCESS(rv,rv);rv =aChannel->GetOriginalURI(getter_AddRefs(originalURI));NS_ENSURE_SUCCESS(rv,rv);nsCOMPtr<nsILoadInfo>loadInfo =aChannel->LoadInfo();// Introduced for DevTools in order to allow overriding some requests// with the content of data: URIs.if (loadInfo->GetAllowInsecureRedirectToDataURI() &&uri->SchemeIs("data")) {// exempt data URIs from the same origin check.if (aAllowDataURI ==DataURIHandling::Allow &&originalURI ==uri) {if (uri->SchemeIs("data")) {if (loadInfo->GetAboutBlankInherits() &&NS_IsAboutBlank(uri)) {// Set CORS attributes on channel so that intercepted requests get correct// values. We have to do this here because the CheckMayLoad checks may lead// to early return. We can't be sure this is an http channel though, so we// can't return early on failure.nsCOMPtr<nsIHttpChannelInternal>internal =do_QueryInterface(aChannel);rv =internal->SetRequestMode(dom::RequestMode::Cors);NS_ENSURE_SUCCESS(rv,rv);rv =internal->SetCorsIncludeCredentials(mWithCredentials);NS_ENSURE_SUCCESS(rv,rv);// consider calling SetBlockedRequest in nsCORSListenerProxy::UpdateChannel// Check that the uri is ok to loaduint32_tflags =loadInfo->CheckLoadURIFlags();rv =nsContentUtils::GetSecurityManager()->CheckLoadURIWithPrincipal(mRequestingPrincipal,uri,flags,loadInfo->GetInnerWindowID());NS_ENSURE_SUCCESS(rv,rv);rv =nsContentUtils::GetSecurityManager()->CheckLoadURIWithPrincipal(mRequestingPrincipal,originalURI,flags,loadInfo->GetInnerWindowID());NS_ENSURE_SUCCESS(rv,rv);if (uri->SchemeIs("moz-extension")) {// moz-extension:-URLs do not support CORS, but can universally be read// if an extension lists the resource in web_accessible_resources.// This is enforced via the CheckLoadURIWithPrincipal call above:// moz-extension resources have the URI_DANGEROUS_TO_LOAD flag, unless// listed in web_accessible_resources.if (!mHasBeenCrossSite &&NS_SUCCEEDED(mRequestingPrincipal->CheckMayLoad(uri,false)) &&NS_SUCCEEDED(mRequestingPrincipal->CheckMayLoad(originalURI,false)))) {// If the CSP directive 'upgrade-insecure-requests' is used or the HTTPS-Only// Mode is enabled then we should not incorrectly require CORS if the only// difference of a subresource request and the main page is the scheme. e.g.// then the xhr request will be upgraded to https before it fetches any data// from the netwerk, hence we shouldn't require CORS in that specific case.if (CheckInsecureUpgradePreventsCORS(mRequestingPrincipal,aChannel)) {// Check if https-only mode upgrades this later anywaynsCOMPtr<nsILoadInfo>loadinfo =aChannel->LoadInfo();if (nsHTTPSOnlyUtils::IsSafeToAcceptCORSOrMixedContent(loadinfo)) {// Check if 'upgrade-insecure-requests' is usedif (loadInfo->GetUpgradeInsecureRequests() ||loadInfo->GetBrowserUpgradeInsecureRequests()) {// Check if we need to do a preflight, and if so set one up. This must be// called once we know that the request is going, or has gone, cross-origin.rv =CheckPreflightNeeded(aChannel,aUpdateType,aStripAuthHeader);NS_ENSURE_SUCCESS(rv,rv);// It's a cross site load// Step 9. If request’s mode is "cors", locationURL includes credentials,// and request’s origin is not same origin with locationURL’s origin,// then return a network error.uri->GetUserPass(userpass);NS_ENSURE_TRUE(userpass.IsEmpty(),NS_ERROR_DOM_BAD_URI);// If we have an expanded principal here, we'll reject the CORS request,// because we can't send a useful Origin header which is required for CORS.if (nsContentUtils::IsExpandedPrincipal(mOriginHeaderPrincipal)) {nsCOMPtr<nsIHttpChannel>httpChannel =do_QueryInterface(aChannel);LogBlockedRequest(aChannel,"CORSOriginHeaderNotAdded",nullptr,nsILoadInfo::BLOCKING_REASON_CORSORIGINHEADERNOTADDED,returnNS_ERROR_DOM_BAD_URI;rv =mOriginHeaderPrincipal->GetWebExposedOriginSerialization(origin);NS_ENSURE_SUCCESS(rv,rv);nsCOMPtr<nsIHttpChannel>http =do_QueryInterface(aChannel);NS_ENSURE_TRUE(http,NS_ERROR_FAILURE);// hide the Origin header when requesting from .onion and requesting CORSif (StaticPrefs::network_http_referer_hideOnionSource()) {if (mOriginHeaderPrincipal->GetIsOnion()) {origin.AssignLiteral("null");rv =http->SetRequestHeader(net::nsHttp::Origin.val(),origin,false);NS_ENSURE_SUCCESS(rv,rv);// Make cookie-less if needed. We don't need to do anything here if the// channel was opened with AsyncOpen, since then AsyncOpen will take// care of the cookie policy for us.rv =http->GetLoadFlags(&flags);NS_ENSURE_SUCCESS(rv,rv);flags |=nsIRequest::LOAD_ANONYMOUS;if (StaticPrefs::network_cors_preflight_allow_client_cert()) {flags |=nsIRequest::LOAD_ANONYMOUS_ALLOW_CLIENT_CERT;rv =http->SetLoadFlags(flags);NS_ENSURE_SUCCESS(rv,rv);nsresultnsCORSListenerProxy::CheckPreflightNeeded(nsIChannel*aChannel,// If this caller isn't using AsyncOpen, or if this *is* a preflight channel,// then we shouldn't initiate preflight for this channel.nsCOMPtr<nsILoadInfo>loadInfo =aChannel->LoadInfo();if (loadInfo->GetSecurityMode() !=nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT ||loadInfo->GetIsPreflight()) {booldoPreflight =loadInfo->GetForcePreflight();nsCOMPtr<nsIHttpChannel>http =do_QueryInterface(aChannel);// Note: A preflight is not needed for moz-extension:-requests either, but// there is already a check for that in the caller of CheckPreflightNeeded,LogBlockedRequest(aChannel,"CORSRequestNotHttp",nullptr,nsILoadInfo::BLOCKING_REASON_CORSREQUESTNOTHTTP,returnNS_ERROR_DOM_BAD_URI; (void)http->GetRequestMethod(method);if (!method.LowerCaseEqualsLiteral("get") && !method.LowerCaseEqualsLiteral("post") && !method.LowerCaseEqualsLiteral("head")) {// Avoid copying the array hereconstnsTArray<nsCString>&loadInfoHeaders =loadInfo->CorsUnsafeHeaders();if (!loadInfoHeaders.IsEmpty()) {// Add Content-Type header if needednsTArray<nsCString>headers;nsAutoCStringcontentTypeHeader;nsresultrv =http->GetRequestHeader("Content-Type"_ns,contentTypeHeader);// GetRequestHeader return an error if the header is not set. Don't add// "content-type" to the list if that's the case. !nsContentUtils::IsAllowedNonCorsContentType(contentTypeHeader) && !loadInfoHeaders.Contains("content-type"_ns,nsCaseInsensitiveCStringArrayComparator())) {headers.AppendElements(loadInfoHeaders);headers.AppendElement("content-type"_ns);nsCOMPtr<nsIHttpChannelInternal>internal =do_QueryInterface(http);autostatusCode =GetStatusCodeAsString(http);LogBlockedRequest(aChannel,"CORSDidNotSucceed2",statusCode.get(),nsILoadInfo::BLOCKING_REASON_CORSDIDNOTSUCCEED,returnNS_ERROR_DOM_BAD_URI;internal->SetCorsPreflightParameters(headers.IsEmpty() ?loadInfoHeaders :headers,aUpdateType ==UpdateType::StripRequestBodyHeader,aStripAuthHeader);//////////////////////////////////////////////////////////////////////////// Class used as streamlistener and notification callback when// doing the initial OPTIONS request for a CORS checkclassnsCORSPreflightListener final :publicnsIStreamListener,publicnsIInterfaceRequestor,publicnsIChannelEventSink {nsCORSPreflightListener(nsIPrincipal*aReferrerPrincipal,nsICorsPreflightCallback*aCallback,nsILoadContext*aLoadContext,boolaWithCredentials,constnsCString&aPreflightMethod,constnsTArray<nsCString>&aPreflightHeaders) :mPreflightMethod(aPreflightMethod),mPreflightHeaders(aPreflightHeaders.Clone()),mReferrerPrincipal(aReferrerPrincipal),mLoadContext(aLoadContext),mWithCredentials(aWithCredentials) {}NS_DECL_NSISTREAMLISTENERNS_DECL_NSIREQUESTOBSERVERNS_DECL_NSIINTERFACEREQUESTORNS_DECL_NSICHANNELEVENTSINKnsresultCheckPreflightRequestApproved(nsIRequest*aRequest); ~nsCORSPreflightListener() =default;voidAddResultToCache(nsIRequest*aRequest);nsCStringmPreflightMethod;nsTArray<nsCString>mPreflightHeaders;nsCOMPtr<nsIPrincipal>mReferrerPrincipal;nsCOMPtr<nsICorsPreflightCallback>mCallback;nsCOMPtr<nsILoadContext>mLoadContext;NS_IMPL_ISUPPORTS(nsCORSPreflightListener,nsIStreamListener,nsIRequestObserver,nsIInterfaceRequestor,voidnsCORSPreflightListener::AddResultToCache(nsIRequest*aRequest) {nsCOMPtr<nsIHttpChannel>http =do_QueryInterface(aRequest);NS_ASSERTION(http,"Request was not http");// The "Access-Control-Max-Age" header should return an age in seconds. (void)http->GetResponseHeader("Access-Control-Max-Age"_ns,headerVal);if (headerVal.IsEmpty()) {age =PREFLIGHT_DEFAULT_EXPIRY_SECONDS;// Sanitize the string. We only allow 'delta-seconds' as specified by// trailing non-whitespace characters).nsACString::const_char_iteratoriter,end;headerVal.BeginReading(iter);headerVal.EndReading(end);if (*iter <'0' || *iter >'9') {age =age * 10 + (*iter -'0');// Cap at 24 hours. This also avoids overflowage = std::min(age, 86400U);if (!age || !EnsurePreflightCache()) {// String seems fine, go ahead and cache.// Note that we have already checked that these headers follow the correctNS_GetFinalChannelURI(http,getter_AddRefs(uri));TimeStampexpirationTime =TimeStamp::NowLoRes() + TimeDuration::FromSeconds(age);StoragePrincipalHelper::GetOriginAttributesForNetworkState(http,attrs);RefPtr<CORSCacheEntry>entry =sPreflightCache->GetEntry(uri,mReferrerPrincipal,mWithCredentials,attrs,true);nsCOMPtr<nsIHttpChannelInternal>httpChannelInternal(do_QueryInterface(aRequest));if (httpChannelInternal) { (void)httpChannelInternal->GetIsProxyUsed(&entry->mIsProxyUsed);// The "Access-Control-Allow-Methods" header contains a comma separated (void)http->GetResponseHeader("Access-Control-Allow-Methods"_ns,headerVal);for (constnsACString&method :nsCCharSeparatedTokenizer(headerVal,',').ToRange()) {for (i = 0;i <entry->mMethods.Length(); ++i) {if (entry->mMethods[i].token.Equals(method)) {entry->mMethods[i].expirationTime =expirationTime;if (i ==entry->mMethods.Length()) {nsPreflightCache::TokenTime*newMethod =entry->mMethods.AppendElement();newMethod->token =method;newMethod->expirationTime =expirationTime;// The "Access-Control-Allow-Headers" header contains a comma separated (void)http->GetResponseHeader("Access-Control-Allow-Headers"_ns,headerVal);for (constnsACString&header :nsCCharSeparatedTokenizer(headerVal,',').ToRange()) {for (i = 0;i <entry->mHeaders.Length(); ++i) {if (entry->mHeaders[i].token.Equals(header)) {entry->mHeaders[i].expirationTime =expirationTime;if (i ==entry->mHeaders.Length()) {nsPreflightCache::TokenTime*newHeader =entry->mHeaders.AppendElement();newHeader->token =header;newHeader->expirationTime =expirationTime;nsCORSPreflightListener::OnStartRequest(nsIRequest*aRequest) {nsCOMPtr<nsIChannel>channel =do_QueryInterface(aRequest);nsCOMPtr<nsILoadInfo>loadInfo =channel ?channel->LoadInfo() :nullptr;MOZ_ASSERT(!loadInfo || !loadInfo->GetServiceWorkerTaintingSynthesized());nsresultrv =CheckPreflightRequestApproved(aRequest);// Everything worked, try to cache and then fire off the actual request.AddResultToCache(aRequest);mCallback->OnPreflightSucceeded();mCallback->OnPreflightFailed(rv);nsCORSPreflightListener::OnStopRequest(nsIRequest*aRequest,nsresultaStatus) {/** nsIStreamListener methods **/nsCORSPreflightListener::OnDataAvailable(nsIRequest*aRequest,returninStr->ReadSegments(NS_DiscardSegment,nullptr,count, &totalRead);nsCORSPreflightListener::AsyncOnChannelRedirect(nsIChannel*aOldChannel,nsIChannel*aNewChannel,uint32_taFlags,nsIAsyncVerifyRedirectCallback*callback) {// Only internal redirects allowed for now.if (!NS_IsInternalSameURIRedirect(aOldChannel,aNewChannel,aFlags) && !NS_IsHSTSUpgradeRedirect(aOldChannel,aNewChannel,aFlags)) {nsCOMPtr<nsIHttpChannel>httpChannel =do_QueryInterface(aOldChannel);aOldChannel,"CORSExternalRedirectNotAllowed",nullptr,nsILoadInfo::BLOCKING_REASON_CORSEXTERNALREDIRECTNOTALLOWED,returnNS_ERROR_DOM_BAD_URI;callback->OnRedirectVerifyCallback(NS_OK);nsresultnsCORSPreflightListener::CheckPreflightRequestApproved(nsresultrv =aRequest->GetStatus(&status);NS_ENSURE_SUCCESS(rv,rv);NS_ENSURE_SUCCESS(status,status);// Test that things worked on a HTTP levelnsCOMPtr<nsIHttpChannel>http =do_QueryInterface(aRequest);nsCOMPtr<nsIHttpChannelInternal>internal =do_QueryInterface(aRequest);NS_ENSURE_STATE(internal);nsCOMPtr<nsIHttpChannel>parentHttpChannel =do_QueryInterface(mCallback);rv =http->GetRequestSucceeded(&succeedded);if (NS_FAILED(rv) || !succeedded) {autostatusCode =GetStatusCodeAsString(http);LogBlockedRequest(aRequest,"CORSPreflightDidNotSucceed3",statusCode.get(),nsILoadInfo::BLOCKING_REASON_CORSPREFLIGHTDIDNOTSUCCEED,returnNS_ERROR_DOM_BAD_URI;// The "Access-Control-Allow-Methods" header contains a comma separated (void)http->GetResponseHeader("Access-Control-Allow-Methods"_ns,headerVal);boolfoundMethod =mPreflightMethod.EqualsLiteral("GET") ||mPreflightMethod.EqualsLiteral("HEAD") ||mPreflightMethod.EqualsLiteral("POST");for (constnsACString&method :nsCCharSeparatedTokenizer(headerVal,',').ToRange()) {if (!NS_IsValidHTTPToken(method)) {LogBlockedRequest(aRequest,"CORSInvalidAllowMethod",NS_ConvertUTF8toUTF16(method).get(),nsILoadInfo::BLOCKING_REASON_CORSINVALIDALLOWMETHOD,returnNS_ERROR_DOM_BAD_URI;if (method.EqualsLiteral("*") && !mWithCredentials) {foundMethod |=mPreflightMethod.Equals(method);LogBlockedRequest(aRequest,"CORSMethodNotFound",nullptr,nsILoadInfo::BLOCKING_REASON_CORSMETHODNOTFOUND,returnNS_ERROR_DOM_BAD_URI;// The "Access-Control-Allow-Headers" header contains a comma separated (void)http->GetResponseHeader("Access-Control-Allow-Headers"_ns,headerVal);nsTArray<nsCString>headers;boolhasAuthorizationHeader =false;for (constnsACString&header :nsCCharSeparatedTokenizer(headerVal,',').ToRange()) {if (!NS_IsValidHTTPToken(header)) {LogBlockedRequest(aRequest,"CORSInvalidAllowHeader",NS_ConvertUTF8toUTF16(header).get(),nsILoadInfo::BLOCKING_REASON_CORSINVALIDALLOWHEADER,returnNS_ERROR_DOM_BAD_URI;if (header.EqualsLiteral("*") && !mWithCredentials) {headers.AppendElement(header);if (header.LowerCaseEqualsASCII("authorization")) {hasAuthorizationHeader =true;boolauthorizationInPreflightHeaders =false;boolauthorizationCoveredByWildcard =false;for (uint32_ti = 0;i <mPreflightHeaders.Length(); ++i) {// Cache the result of the authorization header.mPreflightHeaders[i].LowerCaseEqualsASCII("authorization");authorizationInPreflightHeaders =true;network_cors_preflight_authorization_covered_by_wildcard() && !hasAuthorizationHeader) {// When `Access-Control-Allow-Headers` is `*` and there is no// `Authorization` header listed, we send a deprecation warning to theLogBlockedRequest(aRequest,"CORSAllowHeaderFromPreflightDeprecation",nullptr, 0,parentHttpChannel,true); glean::network::cors_authorization_header .Get("covered_by_wildcard"_ns)authorizationCoveredByWildcard =true;constauto&comparator =nsCaseInsensitiveCStringArrayComparator();if (!headers.Contains(mPreflightHeaders[i],comparator)) {aRequest,"CORSMissingAllowHeaderFromPreflight2",NS_ConvertUTF8toUTF16(mPreflightHeaders[i]).get(),nsILoadInfo::BLOCKING_REASON_CORSMISSINGALLOWHEADERFROMPREFLIGHT, glean::network::cors_authorization_header.Get("disallowed"_ns).Add(1);returnNS_ERROR_DOM_BAD_URI;if (authorizationInPreflightHeaders && !authorizationCoveredByWildcard) { glean::network::cors_authorization_header.Get("allowed"_ns).Add(1);nsCORSPreflightListener::GetInterface(constnsIID&aIID,void**aResult) {if (aIID.Equals(NS_GET_IID(nsILoadContext)) &&mLoadContext) {nsCOMPtr<nsILoadContext>copy =mLoadContext;returnQueryInterface(aIID,aResult);voidnsCORSListenerProxy::RemoveFromCorsPreflightCache(nsIURI*aURI,nsIPrincipal*aRequestingPrincipal,const OriginAttributes&aOriginAttributes) {MOZ_ASSERT(XRE_IsParentProcess());sPreflightCache->RemoveEntries(aURI,aRequestingPrincipal,nsresultnsCORSListenerProxy::StartCORSPreflight(nsIChannel*aRequestChannel,nsICorsPreflightCallback*aCallback,nsTArray<nsCString>&aUnsafeHeaders,nsIChannel**aPreflightChannel) { *aPreflightChannel =nullptr;if (StaticPrefs::content_cors_disable()) {nsCOMPtr<nsIHttpChannel>http =do_QueryInterface(aRequestChannel);LogBlockedRequest(aRequestChannel,"CORSDisabled",nullptr,nsILoadInfo::BLOCKING_REASON_CORSDISABLED,http);returnNS_ERROR_DOM_BAD_URI;nsCOMPtr<nsIHttpChannel>httpChannel(do_QueryInterface(aRequestChannel));NS_ENSURE_TRUE(httpChannel,NS_ERROR_UNEXPECTED); (void)httpChannel->GetRequestMethod(method);nsresultrv =NS_GetFinalChannelURI(aRequestChannel,getter_AddRefs(uri));NS_ENSURE_SUCCESS(rv,rv);nsCOMPtr<nsILoadInfo>originalLoadInfo =aRequestChannel->LoadInfo();MOZ_ASSERT(originalLoadInfo->GetSecurityMode() ==nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT,"how did we end up here?");nsCOMPtr<nsIPrincipal>principal =originalLoadInfo->GetLoadingPrincipal();MOZ_ASSERT(principal &&originalLoadInfo->GetExternalContentPolicyType() !=ExtContentPolicy::TYPE_DOCUMENT,"Should not do CORS loads for top-level loads, so a ""loadingPrincipal should always exist.");originalLoadInfo->GetCookiePolicy() ==nsILoadInfo::SEC_COOKIES_INCLUDE;RefPtr<CORSCacheEntry>entry;rv =aRequestChannel->GetLoadFlags(&loadFlags);NS_ENSURE_SUCCESS(rv,rv);// Disable preflight cache if devtools says so or on other force reloadsbooldisableCache = (loadFlags &nsIRequest::LOAD_BYPASS_CACHE);if (sPreflightCache && !disableCache) {StoragePrincipalHelper::GetOriginAttributesForNetworkState(aRequestChannel,entry =sPreflightCache->GetEntry(uri,principal,withCredentials,attrs,if (entry &&entry->CheckRequest(method,aUnsafeHeaders)) {aCallback->OnPreflightSucceeded();// Either it wasn't cached or the cached result has expired. Build a// channel for the OPTIONS request.nsCOMPtr<nsILoadInfo>loadInfo =static_cast<mozilla::net::LoadInfo*>(originalLoadInfo.get())static_cast<mozilla::net::LoadInfo*>(loadInfo.get())->SetIsPreflight();nsCOMPtr<nsILoadGroup>loadGroup;rv =aRequestChannel->GetLoadGroup(getter_AddRefs(loadGroup));NS_ENSURE_SUCCESS(rv,rv);// We want to give the preflight channel's notification callbacks the same// load context as the original channel's notification callbacks had. We// don't worry about a load context provided via the loadgroup here, since// they have the same loadgroup.nsCOMPtr<nsIInterfaceRequestor>callbacks;rv =aRequestChannel->GetNotificationCallbacks(getter_AddRefs(callbacks));NS_ENSURE_SUCCESS(rv,rv);nsCOMPtr<nsILoadContext>loadContext =do_GetInterface(callbacks);// Preflight requests should never be intercepted by service workers and// NOTE: We ignore CORS checks on synthesized responses (see the CORS// preflights, then we need to extend the GetResponseSynthesized() check in// nsCORSListenerProxy::CheckRequestApproved()). If we change our behavior// here and allow service workers to intercept CORS preflights, then that// check won't be safe any more.nsIChannel::LOAD_BYPASS_SERVICE_WORKER |nsIRequest::LOAD_ANONYMOUS;if (StaticPrefs::network_cors_preflight_allow_client_cert()) {loadFlags |=nsIRequest::LOAD_ANONYMOUS_ALLOW_CLIENT_CERT;nsCOMPtr<nsIChannel>preflightChannel;rv =NS_NewChannelInternal(getter_AddRefs(preflightChannel),uri,loadInfo,nullptr,// PerformanceStorageNS_ENSURE_SUCCESS(rv,rv);// Set method and headersnsCOMPtr<nsIHttpChannel>preHttp =do_QueryInterface(preflightChannel);NS_ASSERTION(preHttp,"Failed to QI to nsIHttpChannel!");rv =preHttp->SetRequestMethod("OPTIONS"_ns);NS_ENSURE_SUCCESS(rv,rv);rv =preHttp->SetRequestHeader("Access-Control-Request-Method"_ns,method,NS_ENSURE_SUCCESS(rv,rv);// Set the CORS preflight channel's warning reporter to be the same as the// requesting channel so that all log messages are able to be reported throughRefPtr<nsHttpChannel>reqCh =do_QueryObject(aRequestChannel);RefPtr<nsHttpChannel>preCh =do_QueryObject(preHttp);if (preCh &&reqCh) {// there are other implementers of nsIHttpChannelpreCh->SetWarningReporter(reqCh->GetWarningReporter());nsTArray<nsCString>preflightHeaders;if (!aUnsafeHeaders.IsEmpty()) {for (uint32_ti = 0;i <aUnsafeHeaders.Length(); ++i) {preflightHeaders.AppendElement();ToLowerCase(aUnsafeHeaders[i],preflightHeaders[i]);for (uint32_ti = 0;i <preflightHeaders.Length(); ++i) {headers +=preflightHeaders[i];rv =preHttp->SetRequestHeader("Access-Control-Request-Headers"_ns,headers,NS_ENSURE_SUCCESS(rv,rv);// Set up listener which will start the original channelRefPtr<nsCORSPreflightListener>preflightListener =newnsCORSPreflightListener(principal,aCallback,loadContext,withCredentials,method,preflightHeaders);rv =preflightChannel->SetNotificationCallbacks(preflightListener);NS_ENSURE_SUCCESS(rv,rv);// request's referrer and referrer policy should match the original request.nsCOMPtr<nsIReferrerInfo>referrerInfo;rv =reqCh->GetReferrerInfo(getter_AddRefs(referrerInfo));NS_ENSURE_SUCCESS(rv,rv);nsCOMPtr<nsIReferrerInfo>newReferrerInfo =static_cast<dom::ReferrerInfo*>(referrerInfo.get())->Clone();rv =preCh->SetReferrerInfo(newReferrerInfo);NS_ENSURE_SUCCESS(rv,rv);rv =preflightChannel->AsyncOpen(preflightListener);NS_ENSURE_SUCCESS(rv,rv);// Return newly created preflight channelpreflightChannel.forget(aPreflightChannel);voidnsCORSListenerProxy::LogBlockedCORSRequest(uint64_taInnerWindowID,boolaPrivateBrowsing,boolaFromChromeContext,constnsAString&aMessage,constnsACString&aCategory,boolaIsWarning) {// Build the error object and log it to the consolensCOMPtr<nsIConsoleService>console( mozilla::components::Console::Service(&rv));NS_WARNING("Failed to log blocked cross-site request (no console)");nsCOMPtr<nsIScriptError>scriptError =do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);NS_WARNING("Failed to log blocked cross-site request (no scriptError)");aIsWarning ?nsIScriptError::warningFlag :nsIScriptError::errorFlag;// query innerWindowID and log to web console, otherwise log to// the error to the browser console.if (aInnerWindowID > 0) {rv =scriptError->InitWithSanitizedSource(aMessage,rv =scriptError->Init(aMessage,errorFlag,aCategory,aPrivateBrowsing,aFromChromeContext);// From chrome context"Failed to log blocked cross-site request (scriptError init failed)");console->LogMessage(scriptError); This page was generated by Searchfox.