diff --git a/browser/components/enterprisepolicies/Policies.sys.mjs b/browser/components/enterprisepolicies/Policies.sys.mjs index b997c59062de..7e29864e0fce 100644 --- a/browser/components/enterprisepolicies/Policies.sys.mjs +++ b/browser/components/enterprisepolicies/Policies.sys.mjs @@ -549,10 +549,25 @@ export var Policies = { param.ClientSignature ); } + if ("DefaultResult" in param) { + if ( + !Number.isInteger(param.DefaultResult) || + param.DefaultResult < 0 || + param.DefaultResult > 2 + ) { + lazy.log.error( + `Non-integer or out of range value for DefaultResult: ${param.DefaultResult}` + ); + } else { + setAndLockPref( + "browser.contentanalysis.default_result", + param.DefaultResult + ); + } + } let boolPrefs = [ ["IsPerUser", "is_per_user"], ["ShowBlockedResult", "show_blocked_result"], - ["DefaultAllow", "default_allow"], ["BypassForSameTabOperations", "bypass_for_same_tab_operations"], ]; for (let pref of boolPrefs) { diff --git a/browser/components/enterprisepolicies/schemas/policies-schema.json b/browser/components/enterprisepolicies/schemas/policies-schema.json index 61cde52b6d3c..1735df164e15 100644 --- a/browser/components/enterprisepolicies/schemas/policies-schema.json +++ b/browser/components/enterprisepolicies/schemas/policies-schema.json @@ -265,8 +265,8 @@ "ShowBlockedResult": { "type": "boolean" }, - "DefaultAllow": { - "type": "boolean" + "DefaultResult": { + "type": "number" }, "BypassForSameTabOperations": { "type": "boolean" diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index e911c3d37dfe..8b4e5917f283 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -1157,12 +1157,17 @@ value: false mirror: never -# Whether content analysis should allow content if there is a problem communicating -# with the agent. -- name: browser.contentanalysis.default_allow - type: bool - value: false - mirror: never +# What content analysis should return if there is a problem communicating +# with the agent. (see DefaultResponse enum in ContentAnalysis.h) +# Make sure these stay in sync with the out-of-range check in Policies.sys.mjs. +# +# 0: Block all requests +# 1: Warn on all requests (which lets the user decide) +# 2: Allow all requests +- name: browser.contentanalysis.default_result + type: uint32_t + value: 0 + mirror: always # Is the IPC pipe to the DLP tool specific to the user or to the system? - name: browser.contentanalysis.is_per_user diff --git a/toolkit/components/contentanalysis/ContentAnalysis.cpp b/toolkit/components/contentanalysis/ContentAnalysis.cpp index 477c8f7d4f1b..5725fc30b025 100644 --- a/toolkit/components/contentanalysis/ContentAnalysis.cpp +++ b/toolkit/components/contentanalysis/ContentAnalysis.cpp @@ -65,7 +65,6 @@ namespace { const char* kIsDLPEnabledPref = "browser.contentanalysis.enabled"; const char* kIsPerUserPref = "browser.contentanalysis.is_per_user"; const char* kPipePathNamePref = "browser.contentanalysis.pipe_path_name"; -const char* kDefaultAllowPref = "browser.contentanalysis.default_allow"; const char* kClientSignature = "browser.contentanalysis.client_signature"; const char* kAllowUrlPref = "browser.contentanalysis.allow_url_regex_list"; const char* kDenyUrlPref = "browser.contentanalysis.deny_url_regex_list"; @@ -797,6 +796,17 @@ static bool ShouldAllowAction( aResponseCode == nsIContentAnalysisResponse::Action::eReportOnly || aResponseCode == nsIContentAnalysisResponse::Action::eWarn; } + +static DefaultResult GetDefaultResultFromPref() { + uint32_t value = StaticPrefs::browser_contentanalysis_default_result(); + if (value > static_cast(DefaultResult::eLastValue)) { + LOGE( + "Invalid value for browser.contentanalysis.default_result pref " + "value"); + return DefaultResult::eBlock; + } + return static_cast(value); +} } // namespace NS_IMETHODIMP ContentAnalysisResponse::GetShouldAllowContent( @@ -809,7 +819,7 @@ NS_IMETHODIMP ContentAnalysisResult::GetShouldAllowContent( bool* aShouldAllowContent) { if (mValue.is()) { NoContentAnalysisResult result = mValue.as(); - if (Preferences::GetBool(kDefaultAllowPref)) { + if (GetDefaultResultFromPref() == DefaultResult::eAllow) { *aShouldAllowContent = result != NoContentAnalysisResult::DENY_DUE_TO_CANCELED; } else { @@ -1067,7 +1077,7 @@ nsresult ContentAnalysis::CancelWithError(nsCString aRequestToken, nsresult aResult) { return NS_DispatchToMainThread(NS_NewCancelableRunnableFunction( "ContentAnalysis::CancelWithError", - [aResult, aRequestToken = std::move(aRequestToken)] { + [aResult, aRequestToken = std::move(aRequestToken)]() mutable { RefPtr owner = GetContentAnalysisFromService(); if (!owner) { // May be shutting down @@ -1076,12 +1086,24 @@ nsresult ContentAnalysis::CancelWithError(nsCString aRequestToken, owner->SetLastResult(aResult); nsCOMPtr obsServ = mozilla::services::GetObserverService(); - bool allow = Preferences::GetBool(kDefaultAllowPref); + DefaultResult defaultResponse = GetDefaultResultFromPref(); + nsIContentAnalysisResponse::Action action; + switch (defaultResponse) { + case DefaultResult::eAllow: + action = nsIContentAnalysisResponse::Action::eAllow; + break; + case DefaultResult::eWarn: + action = nsIContentAnalysisResponse::Action::eWarn; + break; + case DefaultResult::eBlock: + action = nsIContentAnalysisResponse::Action::eCanceled; + break; + default: + MOZ_ASSERT(false); + action = nsIContentAnalysisResponse::Action::eCanceled; + } RefPtr response = - ContentAnalysisResponse::FromAction( - allow ? nsIContentAnalysisResponse::Action::eAllow - : nsIContentAnalysisResponse::Action::eCanceled, - aRequestToken); + ContentAnalysisResponse::FromAction(action, aRequestToken); response->SetOwner(owner); nsIContentAnalysisResponse::CancelError cancelError; switch (aResult) { @@ -1097,20 +1119,29 @@ nsresult ContentAnalysis::CancelWithError(nsCString aRequestToken, break; } response->SetCancelError(cancelError); - obsServ->NotifyObservers(response, "dlp-response", nullptr); - nsMainThreadPtrHandle callbackHolder; + Maybe maybeCallbackData; { auto lock = owner->mCallbackMap.Lock(); - auto callbackData = lock->Extract(aRequestToken); - if (callbackData.isSome()) { - callbackHolder = callbackData->TakeCallbackHolder(); + maybeCallbackData = lock->Extract(aRequestToken); + if (maybeCallbackData.isNothing()) { + LOGD("Content analysis did not find callback for token %s", + aRequestToken.get()); + return; } } + if (action == nsIContentAnalysisResponse::Action::eWarn) { + owner->SendWarnResponse(std::move(aRequestToken), + std::move(*maybeCallbackData), response); + return; + } + nsMainThreadPtrHandle callbackHolder = + maybeCallbackData->TakeCallbackHolder(); + obsServ->NotifyObservers(response, "dlp-response", nullptr); if (callbackHolder) { - if (allow) { - callbackHolder->ContentResult(response); - } else { + if (action == nsIContentAnalysisResponse::Action::eCanceled) { callbackHolder->Error(aResult); + } else { + callbackHolder->ContentResult(response); } } })); @@ -1294,6 +1325,20 @@ void ContentAnalysis::DoAnalyzeRequest( })); } +void ContentAnalysis::SendWarnResponse( + nsCString&& aResponseRequestToken, CallbackData aCallbackData, + RefPtr& aResponse) { + nsCOMPtr obsServ = + mozilla::services::GetObserverService(); + { + auto warnResponseDataMap = mWarnResponseDataMap.Lock(); + warnResponseDataMap->InsertOrUpdate( + aResponseRequestToken, + WarnResponseData(std::move(aCallbackData), aResponse)); + } + obsServ->NotifyObservers(aResponse, "dlp-response", nullptr); +} + void ContentAnalysis::IssueResponse(RefPtr& response) { MOZ_ASSERT(NS_IsMainThread()); nsCString responseRequestToken; @@ -1341,13 +1386,8 @@ void ContentAnalysis::IssueResponse(RefPtr& response) { nsCOMPtr obsServ = mozilla::services::GetObserverService(); if (action == nsIContentAnalysisResponse::Action::eWarn) { - { - auto warnResponseDataMap = mWarnResponseDataMap.Lock(); - warnResponseDataMap->InsertOrUpdate( - responseRequestToken, - WarnResponseData(std::move(*maybeCallbackData), response)); - } - obsServ->NotifyObservers(response, "dlp-response", nullptr); + SendWarnResponse(std::move(responseRequestToken), + std::move(*maybeCallbackData), response); return; } diff --git a/toolkit/components/contentanalysis/ContentAnalysis.h b/toolkit/components/contentanalysis/ContentAnalysis.h index 951b97d19124..2d8a1891b70f 100644 --- a/toolkit/components/contentanalysis/ContentAnalysis.h +++ b/toolkit/components/contentanalysis/ContentAnalysis.h @@ -44,6 +44,13 @@ class ContentAnalysisResponse; namespace mozilla::contentanalysis { +enum class DefaultResult : uint8_t { + eBlock = 0, + eWarn = 1, + eAllow = 2, + eLastValue = 2 +}; + class ContentAnalysisDiagnosticInfo final : public nsIContentAnalysisDiagnosticInfo { public: @@ -290,6 +297,9 @@ class ContentAnalysis final : public nsIContentAnalysis { RefPtr mResponse; }; DataMutex> mWarnResponseDataMap; + void SendWarnResponse(nsCString&& aResponseRequestToken, + CallbackData aCallbackData, + RefPtr& aResponse); std::vector mAllowUrlList; std::vector mDenyUrlList; diff --git a/toolkit/components/contentanalysis/tests/browser/browser_content_analysis_policies.js b/toolkit/components/contentanalysis/tests/browser/browser_content_analysis_policies.js index 18cb685d1126..e7122508e182 100644 --- a/toolkit/components/contentanalysis/tests/browser/browser_content_analysis_policies.js +++ b/toolkit/components/contentanalysis/tests/browser/browser_content_analysis_policies.js @@ -25,7 +25,7 @@ const kAgentNamePref = "agent_name"; const kClientSignaturePref = "client_signature"; const kPerUserPref = "is_per_user"; const kShowBlockedPref = "show_blocked_result"; -const kDefaultAllowPref = "default_allow"; +const kDefaultResultPref = "default_result"; const kBypassForSameTabOperationsPref = "bypass_for_same_tab_operations"; const ca = Cc["@mozilla.org/contentanalysis;1"].getService( @@ -88,7 +88,7 @@ add_task(async function test_ca_enterprise_config() { ClientSignature: string4, IsPerUser: true, ShowBlockedResult: false, - DefaultAllow: true, + DefaultResult: 1, BypassForSameTabOperations: true, }, }, @@ -137,9 +137,9 @@ add_task(async function test_ca_enterprise_config() { "show blocked match" ); is( - Services.prefs.getBoolPref("browser.contentanalysis." + kDefaultAllowPref), - true, - "default allow match" + Services.prefs.getIntPref("browser.contentanalysis." + kDefaultResultPref), + 1, + "default result match" ); is( Services.prefs.getBoolPref(