Bug 1880314 - add a default warn setting to Content Analysis r=dlp-reviewers,handyman

Differential Revision: https://phabricator.services.mozilla.com/D208039
This commit is contained in:
Greg Stoll 2024-04-26 22:51:55 +00:00
parent 578f9ba7b8
commit 07af27b0ac
6 changed files with 107 additions and 37 deletions

View File

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

View File

@ -265,8 +265,8 @@
"ShowBlockedResult": {
"type": "boolean"
},
"DefaultAllow": {
"type": "boolean"
"DefaultResult": {
"type": "number"
},
"BypassForSameTabOperations": {
"type": "boolean"

View File

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

View File

@ -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<uint32_t>(DefaultResult::eLastValue)) {
LOGE(
"Invalid value for browser.contentanalysis.default_result pref "
"value");
return DefaultResult::eBlock;
}
return static_cast<DefaultResult>(value);
}
} // namespace
NS_IMETHODIMP ContentAnalysisResponse::GetShouldAllowContent(
@ -809,7 +819,7 @@ NS_IMETHODIMP ContentAnalysisResult::GetShouldAllowContent(
bool* aShouldAllowContent) {
if (mValue.is<NoContentAnalysisResult>()) {
NoContentAnalysisResult result = mValue.as<NoContentAnalysisResult>();
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<ContentAnalysis> owner = GetContentAnalysisFromService();
if (!owner) {
// May be shutting down
@ -1076,12 +1086,24 @@ nsresult ContentAnalysis::CancelWithError(nsCString aRequestToken,
owner->SetLastResult(aResult);
nsCOMPtr<nsIObserverService> 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<ContentAnalysisResponse> 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<nsIContentAnalysisCallback> callbackHolder;
Maybe<CallbackData> 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<nsIContentAnalysisCallback> 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<ContentAnalysisResponse>& aResponse) {
nsCOMPtr<nsIObserverService> 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<ContentAnalysisResponse>& response) {
MOZ_ASSERT(NS_IsMainThread());
nsCString responseRequestToken;
@ -1341,13 +1386,8 @@ void ContentAnalysis::IssueResponse(RefPtr<ContentAnalysisResponse>& response) {
nsCOMPtr<nsIObserverService> 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;
}

View File

@ -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<ContentAnalysisResponse> mResponse;
};
DataMutex<nsTHashMap<nsCString, WarnResponseData>> mWarnResponseDataMap;
void SendWarnResponse(nsCString&& aResponseRequestToken,
CallbackData aCallbackData,
RefPtr<ContentAnalysisResponse>& aResponse);
std::vector<std::regex> mAllowUrlList;
std::vector<std::regex> mDenyUrlList;

View File

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