Files

266 lines
9.0 KiB
C++

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#include "mozilla/ContentClassifierEngine.h"
#include "ContentClassifierFeatureUtils.h"
#include "ContentClassifierService.h"
#include "mozilla/extensions/WebExtensionPolicy.h"
#include "mozilla/net/UrlClassifierCommon.h"
#include "nsIEffectiveTLDService.h"
#include "nsNetUtil.h"
#include "mozilla/Components.h"
namespace mozilla {
namespace {
bool IsNonRecommendedAddonFromLoadInfo(nsILoadInfo* aLoadInfo) {
MOZ_ASSERT(aLoadInfo);
extensions::WebExtensionPolicy* policy =
ContentClassifierFeatureUtils::GetAddonPolicyFromLoadInfo(aLoadInfo);
return policy && !policy->HasRecommendedState();
}
} // namespace
ContentClassifierEngineResult ContentClassifierEngine::CheckNetworkRequest(
const ContentClassifierRequest& aRequest, bool aPreviouslyMatched) {
if (!mEngine || !sInitializedETLDService) {
return ContentClassifierEngineResult(NS_ERROR_NOT_INITIALIZED, mFeature);
}
if (!aRequest.mValid) {
return ContentClassifierEngineResult(NS_ERROR_INVALID_ARG, mFeature);
}
bool matched = false;
bool important = false;
nsCString exception;
const nsCString& sourceSite = mFeature.mUseTopWindowAsSource
? aRequest.mTopWindowSchemelessSite
: aRequest.mSourceSchemelessSite;
const bool thirdParty = mFeature.mUseTopWindowAsSource
? aRequest.mThirdParty
: aRequest.mThirdPartyToSource;
nsresult rv = content_classifier_engine_check_network_request_preparsed(
mEngine, &aRequest.mUrl, &aRequest.mSchemelessSite, &sourceSite,
&aRequest.mRequestType, thirdParty, aPreviouslyMatched, &matched,
&important, &exception);
return ContentClassifierEngineResult(matched, !exception.IsEmpty(), important,
rv, mFeature);
}
ContentClassifierRequest::ContentClassifierRequest(nsIChannel* aChannel)
: mThirdParty(true), mThirdPartyToSource(true), mValid(false) {
nsCOMPtr<nsIURI> uri;
nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
if (NS_FAILED(rv) || !uri) return;
rv = uri->GetSpec(mUrl);
if (NS_FAILED(rv)) return;
nsCOMPtr<nsILoadInfo> loadInfo;
rv = aChannel->GetLoadInfo(getter_AddRefs(loadInfo));
if (NS_FAILED(rv)) return;
ExtContentPolicyType contentPolicyType =
loadInfo->GetExternalContentPolicyType();
switch (contentPolicyType) {
case ExtContentPolicyType::TYPE_CSP_REPORT:
mRequestType.AssignLiteral("csp_report");
break;
case ExtContentPolicyType::TYPE_DOCUMENT:
mRequestType.AssignLiteral("document");
break;
case ExtContentPolicyType::TYPE_FONT:
mRequestType.AssignLiteral("font");
break;
case ExtContentPolicyType::TYPE_IMAGE:
case ExtContentPolicyType::TYPE_IMAGESET:
mRequestType.AssignLiteral("image");
break;
case ExtContentPolicyType::TYPE_MEDIA:
mRequestType.AssignLiteral("media");
break;
case ExtContentPolicyType::TYPE_OBJECT:
mRequestType.AssignLiteral("object");
break;
case ExtContentPolicyType::TYPE_BEACON:
case ExtContentPolicyType::TYPE_PING:
mRequestType.AssignLiteral("ping");
break;
case ExtContentPolicyType::TYPE_SCRIPT:
mRequestType.AssignLiteral("script");
break;
case ExtContentPolicyType::TYPE_STYLESHEET:
mRequestType.AssignLiteral("stylesheet");
break;
case ExtContentPolicyType::TYPE_SUBDOCUMENT:
mRequestType.AssignLiteral("subdocument");
break;
case ExtContentPolicyType::TYPE_WEBSOCKET:
mRequestType.AssignLiteral("websocket");
break;
case ExtContentPolicyType::TYPE_XMLHTTPREQUEST:
mRequestType.AssignLiteral("xmlhttprequest");
break;
default:
mRequestType.AssignLiteral("other");
break;
}
mPrivateBrowsing = NS_UsePrivateBrowsing(aChannel);
mIsNonRecommendedAddon = IsNonRecommendedAddonFromLoadInfo(loadInfo);
mValid = true;
// Unwrap nested URI schemes (jar:, view-source:, ...) before looking
// at the host, mirroring AsyncUrlChannelClassifier::FeatureData::
// InitializeList which calls NS_GetInnermostURI on the channel URI
// and then GetHost on that.
nsCOMPtr<nsIURI> innermostURI = NS_GetInnermostURI(uri);
if (!innermostURI) return;
nsCString host;
rv = innermostURI->GetHost(host);
if (NS_FAILED(rv)) return;
nsCOMPtr<nsIEffectiveTLDService> eTLDService =
components::EffectiveTLD::Service();
if (!eTLDService) return;
rv = eTLDService->GetSchemelessSiteFromHost(host, mSchemelessSite);
if (NS_FAILED(rv)) return;
// Top-window schemeless site is the outermost page's site, used by
// features whose mUseTopWindowAsSource flag is set.
nsCOMPtr<nsIURI> topWindowURI;
if (NS_SUCCEEDED(net::UrlClassifierCommon::GetTopWindowURI(
aChannel, getter_AddRefs(topWindowURI))) &&
topWindowURI) {
nsCOMPtr<nsIURI> innermostTopURI = NS_GetInnermostURI(topWindowURI);
if (innermostTopURI) {
nsCString topHost;
if (NS_SUCCEEDED(innermostTopURI->GetHost(topHost))) {
rv = eTLDService->GetSchemelessSiteFromHost(topHost,
mTopWindowSchemelessSite);
if (NS_FAILED(rv)) {
mTopWindowSchemelessSite.Truncate();
}
}
}
}
// Source schemeless site is the loading frame's site (the immediate
// initiator of the request), not the outermost top-window. Top-level
// document loads are filtered out further below by the third-party
// check in CheckNetworkRequest.
nsCOMPtr<nsIPrincipal> loadingPrincipal = loadInfo->GetLoadingPrincipal();
if (loadingPrincipal) {
rv = loadingPrincipal->GetBaseDomain(mSourceSchemelessSite);
if (NS_FAILED(rv)) return;
}
// Third-party-ness is the precomputed BrowsingContext flag the
// production AsyncUrl features consult
// (UrlClassifierFeatureTrackingProtection.cpp uses
// GetIsThirdPartyContextToTopWindow), not the cookie/storage-aware
// mozIThirdPartyUtil check.
mThirdParty = loadInfo->GetIsThirdPartyContextToTopWindow();
mThirdPartyToSource = !mSchemelessSite.Equals(mSourceSchemelessSite);
}
static bool IsValidRequestType(const nsACString& aRequestType) {
return aRequestType.EqualsLiteral("csp_report") ||
aRequestType.EqualsLiteral("document") ||
aRequestType.EqualsLiteral("font") ||
aRequestType.EqualsLiteral("image") ||
aRequestType.EqualsLiteral("media") ||
aRequestType.EqualsLiteral("object") ||
aRequestType.EqualsLiteral("ping") ||
aRequestType.EqualsLiteral("script") ||
aRequestType.EqualsLiteral("stylesheet") ||
aRequestType.EqualsLiteral("subdocument") ||
aRequestType.EqualsLiteral("websocket") ||
aRequestType.EqualsLiteral("xmlhttprequest") ||
aRequestType.EqualsLiteral("other");
}
ContentClassifierRequest::ContentClassifierRequest(
const nsACString& aUrl, const nsACString& aSourceUrl,
const nsACString& aTopWindowUrl, const nsACString& aRequestType,
bool aPrivateBrowsing, bool aForceThirdPartyToTop,
bool aIsNonRecommendedAddon)
: mRequestType(aRequestType),
mThirdParty(false),
mThirdPartyToSource(true),
mPrivateBrowsing(aPrivateBrowsing),
mValid(false),
mIsNonRecommendedAddon(aIsNonRecommendedAddon) {
if (!IsValidRequestType(aRequestType)) return;
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), aUrl);
if (NS_FAILED(rv)) return;
rv = uri->GetSpec(mUrl);
if (NS_FAILED(rv)) return;
nsCString host;
rv = uri->GetHost(host);
if (NS_FAILED(rv)) return;
nsCOMPtr<nsIEffectiveTLDService> eTLDService =
components::EffectiveTLD::Service();
if (!eTLDService) return;
rv = eTLDService->GetSchemelessSiteFromHost(host, mSchemelessSite);
if (NS_FAILED(rv)) return;
if (!aSourceUrl.IsEmpty()) {
nsCOMPtr<nsIURI> sourceUri;
rv = NS_NewURI(getter_AddRefs(sourceUri), aSourceUrl);
if (NS_FAILED(rv)) return;
nsCString sourceHost;
rv = sourceUri->GetHost(sourceHost);
if (NS_FAILED(rv)) return;
rv = eTLDService->GetSchemelessSiteFromHost(sourceHost,
mSourceSchemelessSite);
if (NS_FAILED(rv)) return;
mThirdPartyToSource = !mSchemelessSite.Equals(mSourceSchemelessSite);
}
if (!aTopWindowUrl.IsEmpty()) {
nsCOMPtr<nsIURI> topUri;
rv = NS_NewURI(getter_AddRefs(topUri), aTopWindowUrl);
if (NS_FAILED(rv)) return;
nsCString topHost;
rv = topUri->GetHost(topHost);
if (NS_FAILED(rv)) return;
rv = eTLDService->GetSchemelessSiteFromHost(topHost,
mTopWindowSchemelessSite);
if (NS_FAILED(rv)) {
mTopWindowSchemelessSite.Truncate();
return;
}
mThirdParty = !mSchemelessSite.Equals(mTopWindowSchemelessSite);
}
if (aForceThirdPartyToTop) {
mThirdParty = true;
}
mValid = true;
}
} // namespace mozilla