зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1669355
- Refactor MIME type warnings into base class. r=necko-reviewers,valentin
Differential Revision: https://phabricator.services.mozilla.com/D93906
This commit is contained in:
Родитель
664e8f7256
Коммит
6ee5f4b5c1
|
@ -25,10 +25,12 @@
|
|||
#include "mozilla/NullPrincipal.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/StaticPrefs_browser.h"
|
||||
#include "mozilla/StaticPrefs_security.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/Tokenizer.h"
|
||||
#include "mozilla/dom/BrowsingContext.h"
|
||||
#include "mozilla/dom/CanonicalBrowsingContext.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "mozilla/dom/Performance.h"
|
||||
#include "mozilla/dom/PerformanceStorage.h"
|
||||
#include "mozilla/dom/WindowGlobalParent.h"
|
||||
|
@ -2424,6 +2426,342 @@ nsresult HttpBaseChannel::ComputeCrossOriginOpenerPolicyMismatch() {
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
enum class Report { Error, Warning };
|
||||
|
||||
// Helper Function to report messages to the console when the loaded
|
||||
// script had a wrong MIME type.
|
||||
void ReportMimeTypeMismatch(HttpBaseChannel* aChannel, const char* aMessageName,
|
||||
nsIURI* aURI, const nsACString& aContentType,
|
||||
Report report) {
|
||||
NS_ConvertUTF8toUTF16 spec(aURI->GetSpecOrDefault());
|
||||
NS_ConvertUTF8toUTF16 contentType(aContentType);
|
||||
|
||||
aChannel->LogMimeTypeMismatch(nsCString(aMessageName),
|
||||
report == Report::Warning, spec, contentType);
|
||||
}
|
||||
|
||||
// Check and potentially enforce X-Content-Type-Options: nosniff
|
||||
nsresult ProcessXCTO(HttpBaseChannel* aChannel, nsIURI* aURI,
|
||||
nsHttpResponseHead* aResponseHead,
|
||||
nsILoadInfo* aLoadInfo) {
|
||||
if (!aURI || !aResponseHead || !aLoadInfo) {
|
||||
// if there is no uri, no response head or no loadInfo, then there is
|
||||
// nothing to do
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// 1) Query the XCTO header and check if 'nosniff' is the first value.
|
||||
nsAutoCString contentTypeOptionsHeader;
|
||||
if (!aResponseHead->GetContentTypeOptionsHeader(contentTypeOptionsHeader)) {
|
||||
// if failed to get XCTO header, then there is nothing to do.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// let's compare the header (ignoring case)
|
||||
// e.g. "NoSniFF" -> "nosniff"
|
||||
// if it's not 'nosniff' then there is nothing to do here
|
||||
if (!contentTypeOptionsHeader.EqualsIgnoreCase("nosniff")) {
|
||||
// since we are getting here, the XCTO header was sent;
|
||||
// a non matching value most likely means a mistake happenend;
|
||||
// e.g. sending 'nosnif' instead of 'nosniff', let's log a warning.
|
||||
AutoTArray<nsString, 1> params;
|
||||
CopyUTF8toUTF16(contentTypeOptionsHeader, *params.AppendElement());
|
||||
RefPtr<dom::Document> doc;
|
||||
aLoadInfo->GetLoadingDocument(getter_AddRefs(doc));
|
||||
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "XCTO"_ns, doc,
|
||||
nsContentUtils::eSECURITY_PROPERTIES,
|
||||
"XCTOHeaderValueMissing", params);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// 2) Query the content type from the channel
|
||||
nsAutoCString contentType;
|
||||
aResponseHead->ContentType(contentType);
|
||||
|
||||
// 3) Compare the expected MIME type with the actual type
|
||||
if (aLoadInfo->GetExternalContentPolicyType() ==
|
||||
nsIContentPolicy::TYPE_STYLESHEET) {
|
||||
if (contentType.EqualsLiteral(TEXT_CSS)) {
|
||||
return NS_OK;
|
||||
}
|
||||
ReportMimeTypeMismatch(aChannel, "MimeTypeMismatch2", aURI, contentType,
|
||||
Report::Error);
|
||||
return NS_ERROR_CORRUPTED_CONTENT;
|
||||
}
|
||||
|
||||
if (aLoadInfo->GetExternalContentPolicyType() ==
|
||||
nsIContentPolicy::TYPE_SCRIPT) {
|
||||
if (nsContentUtils::IsJavascriptMIMEType(
|
||||
NS_ConvertUTF8toUTF16(contentType))) {
|
||||
return NS_OK;
|
||||
}
|
||||
ReportMimeTypeMismatch(aChannel, "MimeTypeMismatch2", aURI, contentType,
|
||||
Report::Error);
|
||||
return NS_ERROR_CORRUPTED_CONTENT;
|
||||
}
|
||||
|
||||
auto policyType = aLoadInfo->GetExternalContentPolicyType();
|
||||
if (policyType == nsIContentPolicy::TYPE_DOCUMENT ||
|
||||
policyType == nsIContentPolicy::TYPE_SUBDOCUMENT) {
|
||||
// If the header XCTO nosniff is set for any browsing context, then
|
||||
// we set the skipContentSniffing flag on the Loadinfo. Within
|
||||
// GetMIMETypeFromContent we then bail early and do not do any sniffing.
|
||||
aLoadInfo->SetSkipContentSniffing(true);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Ensure that a load of type script has correct MIME type
|
||||
nsresult EnsureMIMEOfScript(HttpBaseChannel* aChannel, nsIURI* aURI,
|
||||
nsHttpResponseHead* aResponseHead,
|
||||
nsILoadInfo* aLoadInfo) {
|
||||
if (!aURI || !aResponseHead || !aLoadInfo) {
|
||||
// if there is no uri, no response head or no loadInfo, then there is
|
||||
// nothing to do
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (aLoadInfo->GetExternalContentPolicyType() !=
|
||||
nsIContentPolicy::TYPE_SCRIPT) {
|
||||
// if this is not a script load, then there is nothing to do
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoCString contentType;
|
||||
aResponseHead->ContentType(contentType);
|
||||
NS_ConvertUTF8toUTF16 typeString(contentType);
|
||||
|
||||
if (nsContentUtils::IsJavascriptMIMEType(typeString)) {
|
||||
// script load has type script
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::javaScript);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
switch (aLoadInfo->InternalContentPolicyType()) {
|
||||
case nsIContentPolicy::TYPE_SCRIPT:
|
||||
case nsIContentPolicy::TYPE_INTERNAL_SCRIPT:
|
||||
case nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD:
|
||||
case nsIContentPolicy::TYPE_INTERNAL_MODULE:
|
||||
case nsIContentPolicy::TYPE_INTERNAL_MODULE_PRELOAD:
|
||||
case nsIContentPolicy::TYPE_INTERNAL_CHROMEUTILS_COMPILED_SCRIPT:
|
||||
case nsIContentPolicy::TYPE_INTERNAL_FRAME_MESSAGEMANAGER_SCRIPT:
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::script_load);
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_INTERNAL_WORKER:
|
||||
case nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER:
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::worker_load);
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER:
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::serviceworker_load);
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS:
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::importScript_load);
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_INTERNAL_AUDIOWORKLET:
|
||||
case nsIContentPolicy::TYPE_INTERNAL_PAINTWORKLET:
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::worklet_load);
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("unexpected script type");
|
||||
break;
|
||||
}
|
||||
|
||||
bool isPrivateWin = aLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
|
||||
bool isSameOrigin = false;
|
||||
aLoadInfo->GetLoadingPrincipal()->IsSameOrigin(aURI, isPrivateWin,
|
||||
&isSameOrigin);
|
||||
if (isSameOrigin) {
|
||||
// same origin
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::same_origin);
|
||||
} else {
|
||||
bool cors = false;
|
||||
nsAutoCString corsOrigin;
|
||||
nsresult rv = aResponseHead->GetHeader(
|
||||
nsHttp::ResolveAtom("Access-Control-Allow-Origin"), corsOrigin);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
if (corsOrigin.Equals("*")) {
|
||||
cors = true;
|
||||
} else {
|
||||
nsCOMPtr<nsIURI> corsOriginURI;
|
||||
rv = NS_NewURI(getter_AddRefs(corsOriginURI), corsOrigin);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
bool isPrivateWin =
|
||||
aLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
|
||||
bool isSameOrigin = false;
|
||||
aLoadInfo->GetLoadingPrincipal()->IsSameOrigin(
|
||||
corsOriginURI, isPrivateWin, &isSameOrigin);
|
||||
if (isSameOrigin) {
|
||||
cors = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cors) {
|
||||
// cors origin
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::CORS_origin);
|
||||
} else {
|
||||
// cross origin
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::cross_origin);
|
||||
}
|
||||
}
|
||||
|
||||
bool block = false;
|
||||
if (StringBeginsWith(contentType, "image/"_ns)) {
|
||||
// script load has type image
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::image);
|
||||
block = true;
|
||||
} else if (StringBeginsWith(contentType, "audio/"_ns)) {
|
||||
// script load has type audio
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::audio);
|
||||
block = true;
|
||||
} else if (StringBeginsWith(contentType, "video/"_ns)) {
|
||||
// script load has type video
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::video);
|
||||
block = true;
|
||||
} else if (StringBeginsWith(contentType, "text/csv"_ns)) {
|
||||
// script load has type text/csv
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::text_csv);
|
||||
block = true;
|
||||
}
|
||||
|
||||
if (block) {
|
||||
ReportMimeTypeMismatch(aChannel, "BlockScriptWithWrongMimeType2", aURI,
|
||||
contentType, Report::Error);
|
||||
return NS_ERROR_CORRUPTED_CONTENT;
|
||||
}
|
||||
|
||||
if (StringBeginsWith(contentType, "text/plain"_ns)) {
|
||||
// script load has type text/plain
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::text_plain);
|
||||
} else if (StringBeginsWith(contentType, "text/xml"_ns)) {
|
||||
// script load has type text/xml
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::text_xml);
|
||||
} else if (StringBeginsWith(contentType, "application/octet-stream"_ns)) {
|
||||
// script load has type application/octet-stream
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::app_octet_stream);
|
||||
} else if (StringBeginsWith(contentType, "application/xml"_ns)) {
|
||||
// script load has type application/xml
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::app_xml);
|
||||
} else if (StringBeginsWith(contentType, "application/json"_ns)) {
|
||||
// script load has type application/json
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::app_json);
|
||||
} else if (StringBeginsWith(contentType, "text/json"_ns)) {
|
||||
// script load has type text/json
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::text_json);
|
||||
} else if (StringBeginsWith(contentType, "text/html"_ns)) {
|
||||
// script load has type text/html
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::text_html);
|
||||
} else if (contentType.IsEmpty()) {
|
||||
// script load has no type
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::empty);
|
||||
} else {
|
||||
// script load has unknown type
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::unknown);
|
||||
}
|
||||
|
||||
// We restrict importScripts() in worker code to JavaScript MIME types.
|
||||
nsContentPolicyType internalType = aLoadInfo->InternalContentPolicyType();
|
||||
if (internalType == nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS) {
|
||||
ReportMimeTypeMismatch(aChannel, "BlockImportScriptsWithWrongMimeType",
|
||||
aURI, contentType, Report::Error);
|
||||
return NS_ERROR_CORRUPTED_CONTENT;
|
||||
}
|
||||
|
||||
if (internalType == nsIContentPolicy::TYPE_INTERNAL_WORKER ||
|
||||
internalType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER) {
|
||||
// Do not block the load if the feature is not enabled.
|
||||
if (!StaticPrefs::security_block_Worker_with_wrong_mime()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
ReportMimeTypeMismatch(aChannel, "BlockWorkerWithWrongMimeType", aURI,
|
||||
contentType, Report::Error);
|
||||
return NS_ERROR_CORRUPTED_CONTENT;
|
||||
}
|
||||
|
||||
// ES6 modules require a strict MIME type check.
|
||||
if (internalType == nsIContentPolicy::TYPE_INTERNAL_MODULE ||
|
||||
internalType == nsIContentPolicy::TYPE_INTERNAL_MODULE_PRELOAD) {
|
||||
ReportMimeTypeMismatch(aChannel, "BlockModuleWithWrongMimeType", aURI,
|
||||
contentType, Report::Error);
|
||||
return NS_ERROR_CORRUPTED_CONTENT;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Warn when a load of type script uses a wrong MIME type and
|
||||
// wasn't blocked by EnsureMIMEOfScript or ProcessXCTO.
|
||||
void WarnWrongMIMEOfScript(HttpBaseChannel* aChannel, nsIURI* aURI,
|
||||
nsHttpResponseHead* aResponseHead,
|
||||
nsILoadInfo* aLoadInfo) {
|
||||
if (!aURI || !aResponseHead || !aLoadInfo) {
|
||||
// If there is no uri, no response head or no loadInfo, then there is
|
||||
// nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
if (aLoadInfo->GetExternalContentPolicyType() !=
|
||||
nsIContentPolicy::TYPE_SCRIPT) {
|
||||
// If this is not a script load, then there is nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
bool succeeded;
|
||||
MOZ_ALWAYS_SUCCEEDS(aChannel->GetRequestSucceeded(&succeeded));
|
||||
if (!succeeded) {
|
||||
// Do not warn for failed loads: HTTP error pages are usually in HTML.
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoCString contentType;
|
||||
aResponseHead->ContentType(contentType);
|
||||
NS_ConvertUTF8toUTF16 typeString(contentType);
|
||||
if (!nsContentUtils::IsJavascriptMIMEType(typeString)) {
|
||||
ReportMimeTypeMismatch(aChannel, "WarnScriptWithWrongMimeType", aURI,
|
||||
contentType, Report::Warning);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult HttpBaseChannel::ValidateMIMEType() {
|
||||
nsresult rv = EnsureMIMEOfScript(this, mURI, mResponseHead.get(), mLoadInfo);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = ProcessXCTO(this, mURI, mResponseHead.get(), mLoadInfo);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
WarnWrongMIMEOfScript(this, mURI, mResponseHead.get(), mLoadInfo);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpBaseChannel::SetCookie(const nsACString& aCookieHeader) {
|
||||
if (mLoadFlags & LOAD_ANONYMOUS) return NS_OK;
|
||||
|
|
|
@ -615,6 +615,8 @@ class HttpBaseChannel : public nsHashPropertyBag,
|
|||
|
||||
nsresult ComputeCrossOriginOpenerPolicyMismatch();
|
||||
|
||||
nsresult ValidateMIMEType();
|
||||
|
||||
friend class PrivateBrowsingChannel<HttpBaseChannel>;
|
||||
friend class InterceptFailedOnStop;
|
||||
|
||||
|
|
|
@ -1031,6 +1031,12 @@ InterceptedHttpChannel::OnStartRequest(nsIRequest* aRequest) {
|
|||
Cancel(mStatus);
|
||||
}
|
||||
|
||||
rv = ValidateMIMEType();
|
||||
if (NS_FAILED(rv)) {
|
||||
mStatus = rv;
|
||||
Cancel(mStatus);
|
||||
}
|
||||
|
||||
mOnStartRequestCalled = true;
|
||||
if (mListener) {
|
||||
return mListener->OnStartRequest(this);
|
||||
|
|
|
@ -1525,327 +1525,6 @@ HttpTrafficCategory nsHttpChannel::CreateTrafficCategory() {
|
|||
NS_UsePrivateBrowsing(this), isSystemPrincipal, isThirdParty, cos, tc);
|
||||
}
|
||||
|
||||
enum class Report { Error, Warning };
|
||||
|
||||
// Helper Function to report messages to the console when the loaded
|
||||
// script had a wrong MIME type.
|
||||
void ReportMimeTypeMismatch(nsHttpChannel* aChannel, const char* aMessageName,
|
||||
nsIURI* aURI, const nsACString& aContentType,
|
||||
Report report) {
|
||||
NS_ConvertUTF8toUTF16 spec(aURI->GetSpecOrDefault());
|
||||
NS_ConvertUTF8toUTF16 contentType(aContentType);
|
||||
|
||||
aChannel->LogMimeTypeMismatch(nsCString(aMessageName),
|
||||
report == Report::Warning, spec, contentType);
|
||||
}
|
||||
|
||||
// Check and potentially enforce X-Content-Type-Options: nosniff
|
||||
nsresult ProcessXCTO(nsHttpChannel* aChannel, nsIURI* aURI,
|
||||
nsHttpResponseHead* aResponseHead,
|
||||
nsILoadInfo* aLoadInfo) {
|
||||
if (!aURI || !aResponseHead || !aLoadInfo) {
|
||||
// if there is no uri, no response head or no loadInfo, then there is
|
||||
// nothing to do
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// 1) Query the XCTO header and check if 'nosniff' is the first value.
|
||||
nsAutoCString contentTypeOptionsHeader;
|
||||
if (!aResponseHead->GetContentTypeOptionsHeader(contentTypeOptionsHeader)) {
|
||||
// if failed to get XCTO header, then there is nothing to do.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// let's compare the header (ignoring case)
|
||||
// e.g. "NoSniFF" -> "nosniff"
|
||||
// if it's not 'nosniff' then there is nothing to do here
|
||||
if (!contentTypeOptionsHeader.EqualsIgnoreCase("nosniff")) {
|
||||
// since we are getting here, the XCTO header was sent;
|
||||
// a non matching value most likely means a mistake happenend;
|
||||
// e.g. sending 'nosnif' instead of 'nosniff', let's log a warning.
|
||||
AutoTArray<nsString, 1> params;
|
||||
CopyUTF8toUTF16(contentTypeOptionsHeader, *params.AppendElement());
|
||||
RefPtr<Document> doc;
|
||||
aLoadInfo->GetLoadingDocument(getter_AddRefs(doc));
|
||||
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "XCTO"_ns, doc,
|
||||
nsContentUtils::eSECURITY_PROPERTIES,
|
||||
"XCTOHeaderValueMissing", params);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// 2) Query the content type from the channel
|
||||
nsAutoCString contentType;
|
||||
aResponseHead->ContentType(contentType);
|
||||
|
||||
// 3) Compare the expected MIME type with the actual type
|
||||
if (aLoadInfo->GetExternalContentPolicyType() ==
|
||||
nsIContentPolicy::TYPE_STYLESHEET) {
|
||||
if (contentType.EqualsLiteral(TEXT_CSS)) {
|
||||
return NS_OK;
|
||||
}
|
||||
ReportMimeTypeMismatch(aChannel, "MimeTypeMismatch2", aURI, contentType,
|
||||
Report::Error);
|
||||
return NS_ERROR_CORRUPTED_CONTENT;
|
||||
}
|
||||
|
||||
if (aLoadInfo->GetExternalContentPolicyType() ==
|
||||
nsIContentPolicy::TYPE_SCRIPT) {
|
||||
if (nsContentUtils::IsJavascriptMIMEType(
|
||||
NS_ConvertUTF8toUTF16(contentType))) {
|
||||
return NS_OK;
|
||||
}
|
||||
ReportMimeTypeMismatch(aChannel, "MimeTypeMismatch2", aURI, contentType,
|
||||
Report::Error);
|
||||
return NS_ERROR_CORRUPTED_CONTENT;
|
||||
}
|
||||
|
||||
auto policyType = aLoadInfo->GetExternalContentPolicyType();
|
||||
if (policyType == nsIContentPolicy::TYPE_DOCUMENT ||
|
||||
policyType == nsIContentPolicy::TYPE_SUBDOCUMENT) {
|
||||
// If the header XCTO nosniff is set for any browsing context, then
|
||||
// we set the skipContentSniffing flag on the Loadinfo. Within
|
||||
// GetMIMETypeFromContent we then bail early and do not do any sniffing.
|
||||
aLoadInfo->SetSkipContentSniffing(true);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Ensure that a load of type script has correct MIME type
|
||||
nsresult EnsureMIMEOfScript(nsHttpChannel* aChannel, nsIURI* aURI,
|
||||
nsHttpResponseHead* aResponseHead,
|
||||
nsILoadInfo* aLoadInfo) {
|
||||
if (!aURI || !aResponseHead || !aLoadInfo) {
|
||||
// if there is no uri, no response head or no loadInfo, then there is
|
||||
// nothing to do
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (aLoadInfo->GetExternalContentPolicyType() !=
|
||||
nsIContentPolicy::TYPE_SCRIPT) {
|
||||
// if this is not a script load, then there is nothing to do
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoCString contentType;
|
||||
aResponseHead->ContentType(contentType);
|
||||
NS_ConvertUTF8toUTF16 typeString(contentType);
|
||||
|
||||
if (nsContentUtils::IsJavascriptMIMEType(typeString)) {
|
||||
// script load has type script
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::javaScript);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
switch (aLoadInfo->InternalContentPolicyType()) {
|
||||
case nsIContentPolicy::TYPE_SCRIPT:
|
||||
case nsIContentPolicy::TYPE_INTERNAL_SCRIPT:
|
||||
case nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD:
|
||||
case nsIContentPolicy::TYPE_INTERNAL_MODULE:
|
||||
case nsIContentPolicy::TYPE_INTERNAL_MODULE_PRELOAD:
|
||||
case nsIContentPolicy::TYPE_INTERNAL_CHROMEUTILS_COMPILED_SCRIPT:
|
||||
case nsIContentPolicy::TYPE_INTERNAL_FRAME_MESSAGEMANAGER_SCRIPT:
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::script_load);
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_INTERNAL_WORKER:
|
||||
case nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER:
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::worker_load);
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER:
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::serviceworker_load);
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS:
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::importScript_load);
|
||||
break;
|
||||
case nsIContentPolicy::TYPE_INTERNAL_AUDIOWORKLET:
|
||||
case nsIContentPolicy::TYPE_INTERNAL_PAINTWORKLET:
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::worklet_load);
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("unexpected script type");
|
||||
break;
|
||||
}
|
||||
|
||||
bool isPrivateWin = aLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
|
||||
bool isSameOrigin = false;
|
||||
aLoadInfo->GetLoadingPrincipal()->IsSameOrigin(aURI, isPrivateWin,
|
||||
&isSameOrigin);
|
||||
if (isSameOrigin) {
|
||||
// same origin
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::same_origin);
|
||||
} else {
|
||||
bool cors = false;
|
||||
nsAutoCString corsOrigin;
|
||||
nsresult rv = aResponseHead->GetHeader(
|
||||
nsHttp::ResolveAtom("Access-Control-Allow-Origin"), corsOrigin);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
if (corsOrigin.Equals("*")) {
|
||||
cors = true;
|
||||
} else {
|
||||
nsCOMPtr<nsIURI> corsOriginURI;
|
||||
rv = NS_NewURI(getter_AddRefs(corsOriginURI), corsOrigin);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
bool isPrivateWin =
|
||||
aLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
|
||||
bool isSameOrigin = false;
|
||||
aLoadInfo->GetLoadingPrincipal()->IsSameOrigin(
|
||||
corsOriginURI, isPrivateWin, &isSameOrigin);
|
||||
if (isSameOrigin) {
|
||||
cors = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cors) {
|
||||
// cors origin
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::CORS_origin);
|
||||
} else {
|
||||
// cross origin
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::cross_origin);
|
||||
}
|
||||
}
|
||||
|
||||
bool block = false;
|
||||
if (StringBeginsWith(contentType, "image/"_ns)) {
|
||||
// script load has type image
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::image);
|
||||
block = true;
|
||||
} else if (StringBeginsWith(contentType, "audio/"_ns)) {
|
||||
// script load has type audio
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::audio);
|
||||
block = true;
|
||||
} else if (StringBeginsWith(contentType, "video/"_ns)) {
|
||||
// script load has type video
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::video);
|
||||
block = true;
|
||||
} else if (StringBeginsWith(contentType, "text/csv"_ns)) {
|
||||
// script load has type text/csv
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::text_csv);
|
||||
block = true;
|
||||
}
|
||||
|
||||
if (block) {
|
||||
ReportMimeTypeMismatch(aChannel, "BlockScriptWithWrongMimeType2", aURI,
|
||||
contentType, Report::Error);
|
||||
return NS_ERROR_CORRUPTED_CONTENT;
|
||||
}
|
||||
|
||||
if (StringBeginsWith(contentType, "text/plain"_ns)) {
|
||||
// script load has type text/plain
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::text_plain);
|
||||
} else if (StringBeginsWith(contentType, "text/xml"_ns)) {
|
||||
// script load has type text/xml
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::text_xml);
|
||||
} else if (StringBeginsWith(contentType, "application/octet-stream"_ns)) {
|
||||
// script load has type application/octet-stream
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::app_octet_stream);
|
||||
} else if (StringBeginsWith(contentType, "application/xml"_ns)) {
|
||||
// script load has type application/xml
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::app_xml);
|
||||
} else if (StringBeginsWith(contentType, "application/json"_ns)) {
|
||||
// script load has type application/json
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::app_json);
|
||||
} else if (StringBeginsWith(contentType, "text/json"_ns)) {
|
||||
// script load has type text/json
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::text_json);
|
||||
} else if (StringBeginsWith(contentType, "text/html"_ns)) {
|
||||
// script load has type text/html
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::text_html);
|
||||
} else if (contentType.IsEmpty()) {
|
||||
// script load has no type
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::empty);
|
||||
} else {
|
||||
// script load has unknown type
|
||||
AccumulateCategorical(
|
||||
Telemetry::LABELS_SCRIPT_BLOCK_INCORRECT_MIME_3::unknown);
|
||||
}
|
||||
|
||||
// We restrict importScripts() in worker code to JavaScript MIME types.
|
||||
nsContentPolicyType internalType = aLoadInfo->InternalContentPolicyType();
|
||||
if (internalType == nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS) {
|
||||
ReportMimeTypeMismatch(aChannel, "BlockImportScriptsWithWrongMimeType",
|
||||
aURI, contentType, Report::Error);
|
||||
return NS_ERROR_CORRUPTED_CONTENT;
|
||||
}
|
||||
|
||||
if (internalType == nsIContentPolicy::TYPE_INTERNAL_WORKER ||
|
||||
internalType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER) {
|
||||
// Do not block the load if the feature is not enabled.
|
||||
if (!StaticPrefs::security_block_Worker_with_wrong_mime()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
ReportMimeTypeMismatch(aChannel, "BlockWorkerWithWrongMimeType", aURI,
|
||||
contentType, Report::Error);
|
||||
return NS_ERROR_CORRUPTED_CONTENT;
|
||||
}
|
||||
|
||||
// ES6 modules require a strict MIME type check.
|
||||
if (internalType == nsIContentPolicy::TYPE_INTERNAL_MODULE ||
|
||||
internalType == nsIContentPolicy::TYPE_INTERNAL_MODULE_PRELOAD) {
|
||||
ReportMimeTypeMismatch(aChannel, "BlockModuleWithWrongMimeType", aURI,
|
||||
contentType, Report::Error);
|
||||
return NS_ERROR_CORRUPTED_CONTENT;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Warn when a load of type script uses a wrong MIME type and
|
||||
// wasn't blocked by EnsureMIMEOfScript or ProcessXCTO.
|
||||
void WarnWrongMIMEOfScript(nsHttpChannel* aChannel, nsIURI* aURI,
|
||||
nsHttpResponseHead* aResponseHead,
|
||||
nsILoadInfo* aLoadInfo) {
|
||||
if (!aURI || !aResponseHead || !aLoadInfo) {
|
||||
// If there is no uri, no response head or no loadInfo, then there is
|
||||
// nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
if (aLoadInfo->GetExternalContentPolicyType() !=
|
||||
nsIContentPolicy::TYPE_SCRIPT) {
|
||||
// If this is not a script load, then there is nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
bool succeeded;
|
||||
MOZ_ALWAYS_SUCCEEDS(aChannel->GetRequestSucceeded(&succeeded));
|
||||
if (!succeeded) {
|
||||
// Do not warn for failed loads: HTTP error pages are usually in HTML.
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoCString contentType;
|
||||
aResponseHead->ContentType(contentType);
|
||||
NS_ConvertUTF8toUTF16 typeString(contentType);
|
||||
if (!nsContentUtils::IsJavascriptMIMEType(typeString)) {
|
||||
ReportMimeTypeMismatch(aChannel, "WarnScriptWithWrongMimeType", aURI,
|
||||
contentType, Report::Warning);
|
||||
}
|
||||
}
|
||||
|
||||
void nsHttpChannel::SetCachedContentType() {
|
||||
if (!mResponseHead) {
|
||||
return;
|
||||
|
@ -1913,7 +1592,7 @@ nsresult nsHttpChannel::CallOnStartRequest() {
|
|||
mOnStartRequestCalled = true;
|
||||
});
|
||||
|
||||
nsresult rv = EnsureMIMEOfScript(this, mURI, mResponseHead.get(), mLoadInfo);
|
||||
nsresult rv = ValidateMIMEType();
|
||||
// Since ODA and OnStopRequest could be sent from socket process directly, we
|
||||
// need to update the channel status before calling mListener->OnStartRequest.
|
||||
// This is the only way to let child process discard the already received ODA
|
||||
|
@ -1923,14 +1602,6 @@ nsresult nsHttpChannel::CallOnStartRequest() {
|
|||
return mStatus;
|
||||
}
|
||||
|
||||
rv = ProcessXCTO(this, mURI, mResponseHead.get(), mLoadInfo);
|
||||
if (NS_FAILED(rv)) {
|
||||
mStatus = rv;
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
WarnWrongMIMEOfScript(this, mURI, mResponseHead.get(), mLoadInfo);
|
||||
|
||||
// Allow consumers to override our content type
|
||||
if (mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS) {
|
||||
// NOTE: We can have both a txn pump and a cache pump when the cache
|
||||
|
|
Загрузка…
Ссылка в новой задаче