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:
Andrew Sutherland 2020-10-19 07:00:41 +00:00
Родитель 664e8f7256
Коммит 6ee5f4b5c1
4 изменённых файлов: 347 добавлений и 330 удалений

Просмотреть файл

@ -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