forked from mirrors/gecko-dev
Bug 1888259 - Show a notification when a Content Analysis request is denied for lack of an agent connection r=dlp-reviewers,fluent-reviewers,handyman
Note that this also covers the case when the connection initially succeeds but then the agent goes away - we hope to handle this better in bug 1888293. This also shows a message if the signature verification fails. Differential Revision: https://phabricator.services.mozilla.com/D206024
This commit is contained in:
parent
e5eba44e91
commit
2bd7572f35
5 changed files with 126 additions and 16 deletions
|
|
@ -243,7 +243,7 @@ export const ContentAnalysis = {
|
||||||
},
|
},
|
||||||
|
|
||||||
// nsIObserver
|
// nsIObserver
|
||||||
async observe(aSubj, aTopic) {
|
async observe(aSubj, aTopic, _aData) {
|
||||||
switch (aTopic) {
|
switch (aTopic) {
|
||||||
case "quit-application-requested": {
|
case "quit-application-requested": {
|
||||||
let pendingRequests =
|
let pendingRequests =
|
||||||
|
|
@ -345,7 +345,7 @@ export const ContentAnalysis = {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "dlp-response":
|
case "dlp-response": {
|
||||||
const request = aSubj.QueryInterface(Ci.nsIContentAnalysisResponse);
|
const request = aSubj.QueryInterface(Ci.nsIContentAnalysisResponse);
|
||||||
// Cancels timer or slow message UI,
|
// Cancels timer or slow message UI,
|
||||||
// if present, and possibly presents the CA verdict.
|
// if present, and possibly presents the CA verdict.
|
||||||
|
|
@ -379,12 +379,14 @@ export const ContentAnalysis = {
|
||||||
windowAndResourceNameOrOperationType.resourceNameOrOperationType,
|
windowAndResourceNameOrOperationType.resourceNameOrOperationType,
|
||||||
windowAndResourceNameOrOperationType.browsingContext,
|
windowAndResourceNameOrOperationType.browsingContext,
|
||||||
request.requestToken,
|
request.requestToken,
|
||||||
responseResult
|
responseResult,
|
||||||
|
request.cancelError
|
||||||
);
|
);
|
||||||
this._showAnotherPendingDialog(
|
this._showAnotherPendingDialog(
|
||||||
windowAndResourceNameOrOperationType.browsingContext
|
windowAndResourceNameOrOperationType.browsingContext
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -662,7 +664,8 @@ export const ContentAnalysis = {
|
||||||
aResourceNameOrOperationType,
|
aResourceNameOrOperationType,
|
||||||
aBrowsingContext,
|
aBrowsingContext,
|
||||||
aRequestToken,
|
aRequestToken,
|
||||||
aCAResult
|
aCAResult,
|
||||||
|
aRequestCancelError
|
||||||
) {
|
) {
|
||||||
let message = null;
|
let message = null;
|
||||||
let timeoutMs = 0;
|
let timeoutMs = 0;
|
||||||
|
|
@ -683,7 +686,7 @@ export const ContentAnalysis = {
|
||||||
);
|
);
|
||||||
timeoutMs = this._RESULT_NOTIFICATION_FAST_TIMEOUT_MS;
|
timeoutMs = this._RESULT_NOTIFICATION_FAST_TIMEOUT_MS;
|
||||||
break;
|
break;
|
||||||
case Ci.nsIContentAnalysisResponse.eWarn:
|
case Ci.nsIContentAnalysisResponse.eWarn: {
|
||||||
const result = await Services.prompt.asyncConfirmEx(
|
const result = await Services.prompt.asyncConfirmEx(
|
||||||
aBrowsingContext,
|
aBrowsingContext,
|
||||||
Ci.nsIPromptService.MODAL_TYPE_TAB,
|
Ci.nsIPromptService.MODAL_TYPE_TAB,
|
||||||
|
|
@ -711,6 +714,7 @@ export const ContentAnalysis = {
|
||||||
const allow = result.get("buttonNumClicked") === 0;
|
const allow = result.get("buttonNumClicked") === 0;
|
||||||
lazy.gContentAnalysis.respondToWarnDialog(aRequestToken, allow);
|
lazy.gContentAnalysis.respondToWarnDialog(aRequestToken, allow);
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
case Ci.nsIContentAnalysisResponse.eBlock:
|
case Ci.nsIContentAnalysisResponse.eBlock:
|
||||||
if (!lazy.showBlockedResult) {
|
if (!lazy.showBlockedResult) {
|
||||||
// Don't show anything
|
// Don't show anything
|
||||||
|
|
@ -724,13 +728,51 @@ export const ContentAnalysis = {
|
||||||
timeoutMs = this._RESULT_NOTIFICATION_TIMEOUT_MS;
|
timeoutMs = this._RESULT_NOTIFICATION_TIMEOUT_MS;
|
||||||
break;
|
break;
|
||||||
case Ci.nsIContentAnalysisResponse.eUnspecified:
|
case Ci.nsIContentAnalysisResponse.eUnspecified:
|
||||||
message = await this.l10n.formatValue("contentanalysis-error-message", {
|
message = await this.l10n.formatValue(
|
||||||
content: this._getResourceNameFromNameOrOperationType(
|
"contentanalysis-unspecified-error-message",
|
||||||
aResourceNameOrOperationType
|
{
|
||||||
),
|
agent: lazy.agentName,
|
||||||
});
|
content: this._getResourceNameFromNameOrOperationType(
|
||||||
|
aResourceNameOrOperationType
|
||||||
|
),
|
||||||
|
}
|
||||||
|
);
|
||||||
timeoutMs = this._RESULT_NOTIFICATION_TIMEOUT_MS;
|
timeoutMs = this._RESULT_NOTIFICATION_TIMEOUT_MS;
|
||||||
break;
|
break;
|
||||||
|
case Ci.nsIContentAnalysisResponse.eCanceled:
|
||||||
|
{
|
||||||
|
let messageId;
|
||||||
|
switch (aRequestCancelError) {
|
||||||
|
case Ci.nsIContentAnalysisResponse.eUserInitiated:
|
||||||
|
console.error(
|
||||||
|
"Got unexpected cancel response with eUserInitiated"
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
case Ci.nsIContentAnalysisResponse.eNoAgent:
|
||||||
|
messageId = "contentanalysis-no-agent-connected-message";
|
||||||
|
break;
|
||||||
|
case Ci.nsIContentAnalysisResponse.eInvalidAgentSignature:
|
||||||
|
messageId = "contentanalysis-invalid-agent-signature-message";
|
||||||
|
break;
|
||||||
|
case Ci.nsIContentAnalysisResponse.eErrorOther:
|
||||||
|
messageId = "contentanalysis-unspecified-error-message";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.error(
|
||||||
|
"Unexpected CA cancelError value: " + aRequestCancelError
|
||||||
|
);
|
||||||
|
messageId = "contentanalysis-unspecified-error-message";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
message = await this.l10n.formatValue(messageId, {
|
||||||
|
agent: lazy.agentName,
|
||||||
|
content: this._getResourceNameFromNameOrOperationType(
|
||||||
|
aResourceNameOrOperationType
|
||||||
|
),
|
||||||
|
});
|
||||||
|
timeoutMs = this._RESULT_NOTIFICATION_TIMEOUT_MS;
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error("Unexpected CA result value: " + aCAResult);
|
throw new Error("Unexpected CA result value: " + aCAResult);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -612,6 +612,12 @@ ContentAnalysisResponse::GetAction(Action* aAction) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
ContentAnalysisResponse::GetCancelError(CancelError* aCancelError) {
|
||||||
|
*aCancelError = mCancelError;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
static void LogAcknowledgement(
|
static void LogAcknowledgement(
|
||||||
content_analysis::sdk::ContentAnalysisAcknowledgement* aPbAck) {
|
content_analysis::sdk::ContentAnalysisAcknowledgement* aPbAck) {
|
||||||
if (!static_cast<LogModule*>(gContentAnalysisLog)
|
if (!static_cast<LogModule*>(gContentAnalysisLog)
|
||||||
|
|
@ -643,6 +649,10 @@ void ContentAnalysisResponse::SetOwner(RefPtr<ContentAnalysis> aOwner) {
|
||||||
mOwner = std::move(aOwner);
|
mOwner = std::move(aOwner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ContentAnalysisResponse::SetCancelError(CancelError aCancelError) {
|
||||||
|
mCancelError = aCancelError;
|
||||||
|
}
|
||||||
|
|
||||||
void ContentAnalysisResponse::ResolveWarnAction(bool aAllowContent) {
|
void ContentAnalysisResponse::ResolveWarnAction(bool aAllowContent) {
|
||||||
MOZ_ASSERT(mAction == Action::eWarn);
|
MOZ_ASSERT(mAction == Action::eWarn);
|
||||||
mAction = aAllowContent ? Action::eAllow : Action::eBlock;
|
mAction = aAllowContent ? Action::eAllow : Action::eBlock;
|
||||||
|
|
@ -955,6 +965,20 @@ nsresult ContentAnalysis::CancelWithError(nsCString aRequestToken,
|
||||||
: nsIContentAnalysisResponse::Action::eCanceled,
|
: nsIContentAnalysisResponse::Action::eCanceled,
|
||||||
aRequestToken);
|
aRequestToken);
|
||||||
response->SetOwner(owner);
|
response->SetOwner(owner);
|
||||||
|
nsIContentAnalysisResponse::CancelError cancelError;
|
||||||
|
switch (aResult) {
|
||||||
|
case NS_ERROR_NOT_AVAILABLE:
|
||||||
|
cancelError = nsIContentAnalysisResponse::CancelError::eNoAgent;
|
||||||
|
break;
|
||||||
|
case NS_ERROR_INVALID_SIGNATURE:
|
||||||
|
cancelError =
|
||||||
|
nsIContentAnalysisResponse::CancelError::eInvalidAgentSignature;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
cancelError = nsIContentAnalysisResponse::CancelError::eErrorOther;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
response->SetCancelError(cancelError);
|
||||||
obsServ->NotifyObservers(response, "dlp-response", nullptr);
|
obsServ->NotifyObservers(response, "dlp-response", nullptr);
|
||||||
nsMainThreadPtrHandle<nsIContentAnalysisCallback> callbackHolder;
|
nsMainThreadPtrHandle<nsIContentAnalysisCallback> callbackHolder;
|
||||||
{
|
{
|
||||||
|
|
@ -1236,8 +1260,28 @@ NS_IMETHODIMP
|
||||||
ContentAnalysis::AnalyzeContentRequestCallback(
|
ContentAnalysis::AnalyzeContentRequestCallback(
|
||||||
nsIContentAnalysisRequest* aRequest, bool aAutoAcknowledge,
|
nsIContentAnalysisRequest* aRequest, bool aAutoAcknowledge,
|
||||||
nsIContentAnalysisCallback* aCallback) {
|
nsIContentAnalysisCallback* aCallback) {
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
NS_ENSURE_ARG(aRequest);
|
NS_ENSURE_ARG(aRequest);
|
||||||
NS_ENSURE_ARG(aCallback);
|
NS_ENSURE_ARG(aCallback);
|
||||||
|
nsresult rv = AnalyzeContentRequestCallbackPrivate(aRequest, aAutoAcknowledge,
|
||||||
|
aCallback);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
nsCString requestToken;
|
||||||
|
nsresult requestTokenRv = aRequest->GetRequestToken(requestToken);
|
||||||
|
NS_ENSURE_SUCCESS(requestTokenRv, requestTokenRv);
|
||||||
|
CancelWithError(requestToken, rv);
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult ContentAnalysis::AnalyzeContentRequestCallbackPrivate(
|
||||||
|
nsIContentAnalysisRequest* aRequest, bool aAutoAcknowledge,
|
||||||
|
nsIContentAnalysisCallback* aCallback) {
|
||||||
|
// Make sure we send the notification first, so if we later return
|
||||||
|
// an error the JS will handle it correctly.
|
||||||
|
nsCOMPtr<nsIObserverService> obsServ =
|
||||||
|
mozilla::services::GetObserverService();
|
||||||
|
obsServ->NotifyObservers(aRequest, "dlp-request-made", nullptr);
|
||||||
|
|
||||||
bool isActive;
|
bool isActive;
|
||||||
nsresult rv = GetIsActive(&isActive);
|
nsresult rv = GetIsActive(&isActive);
|
||||||
|
|
@ -1246,10 +1290,6 @@ ContentAnalysis::AnalyzeContentRequestCallback(
|
||||||
return NS_ERROR_NOT_AVAILABLE;
|
return NS_ERROR_NOT_AVAILABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIObserverService> obsServ =
|
|
||||||
mozilla::services::GetObserverService();
|
|
||||||
obsServ->NotifyObservers(aRequest, "dlp-request-made", nullptr);
|
|
||||||
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
// since we're on the main thread, don't need to synchronize this
|
// since we're on the main thread, don't need to synchronize this
|
||||||
int64_t requestCount = ++mRequestCount;
|
int64_t requestCount = ++mRequestCount;
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,10 @@ class ContentAnalysis final : public nsIContentAnalysis {
|
||||||
nsresult CreateContentAnalysisClient(nsCString&& aPipePathName,
|
nsresult CreateContentAnalysisClient(nsCString&& aPipePathName,
|
||||||
nsString&& aClientSignatureSetting,
|
nsString&& aClientSignatureSetting,
|
||||||
bool aIsPerUser);
|
bool aIsPerUser);
|
||||||
|
nsresult AnalyzeContentRequestCallbackPrivate(
|
||||||
|
nsIContentAnalysisRequest* aRequest, bool aAutoAcknowledge,
|
||||||
|
nsIContentAnalysisCallback* aCallback);
|
||||||
|
|
||||||
nsresult RunAnalyzeRequestTask(
|
nsresult RunAnalyzeRequestTask(
|
||||||
const RefPtr<nsIContentAnalysisRequest>& aRequest, bool aAutoAcknowledge,
|
const RefPtr<nsIContentAnalysisRequest>& aRequest, bool aAutoAcknowledge,
|
||||||
int64_t aRequestCount,
|
int64_t aRequestCount,
|
||||||
|
|
@ -219,6 +223,7 @@ class ContentAnalysisResponse final : public nsIContentAnalysisResponse {
|
||||||
|
|
||||||
void SetOwner(RefPtr<ContentAnalysis> aOwner);
|
void SetOwner(RefPtr<ContentAnalysis> aOwner);
|
||||||
void DoNotAcknowledge() { mDoNotAcknowledge = true; }
|
void DoNotAcknowledge() { mDoNotAcknowledge = true; }
|
||||||
|
void SetCancelError(CancelError aCancelError);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
~ContentAnalysisResponse() = default;
|
~ContentAnalysisResponse() = default;
|
||||||
|
|
@ -239,7 +244,11 @@ class ContentAnalysisResponse final : public nsIContentAnalysisResponse {
|
||||||
// Identifier for the corresponding nsIContentAnalysisRequest
|
// Identifier for the corresponding nsIContentAnalysisRequest
|
||||||
nsCString mRequestToken;
|
nsCString mRequestToken;
|
||||||
|
|
||||||
// ContentAnalysis (or, more precisely, it's Client object) must outlive
|
// If mAction is eCanceled, this is the error explaining why the request was
|
||||||
|
// canceled, or eUserInitiated if the user canceled it.
|
||||||
|
CancelError mCancelError = CancelError::eUserInitiated;
|
||||||
|
|
||||||
|
// ContentAnalysis (or, more precisely, its Client object) must outlive
|
||||||
// the transaction.
|
// the transaction.
|
||||||
RefPtr<ContentAnalysis> mOwner;
|
RefPtr<ContentAnalysis> mOwner;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,8 +52,18 @@ interface nsIContentAnalysisResponse : nsISupports
|
||||||
eCanceled = 1001,
|
eCanceled = 1001,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
cenum CancelError : 32 {
|
||||||
|
eUserInitiated = 0,
|
||||||
|
eNoAgent = 1,
|
||||||
|
eInvalidAgentSignature = 2,
|
||||||
|
eErrorOther = 3,
|
||||||
|
};
|
||||||
|
|
||||||
[infallible] readonly attribute nsIContentAnalysisResponse_Action action;
|
[infallible] readonly attribute nsIContentAnalysisResponse_Action action;
|
||||||
[infallible] readonly attribute boolean shouldAllowContent;
|
[infallible] readonly attribute boolean shouldAllowContent;
|
||||||
|
// If action is eCanceled, this is the error explaining why the request was canceled,
|
||||||
|
// or eUserInitiated if the user canceled it.
|
||||||
|
[infallible] readonly attribute nsIContentAnalysisResponse_CancelError cancelError;
|
||||||
|
|
||||||
// Identifier for the corresponding nsIContentAnalysisRequest
|
// Identifier for the corresponding nsIContentAnalysisRequest
|
||||||
readonly attribute ACString requestToken;
|
readonly attribute ACString requestToken;
|
||||||
|
|
|
||||||
|
|
@ -42,8 +42,17 @@ contentanalysis-genericresponse-message = Content Analysis responded with { $res
|
||||||
# $content - Description of the content being blocked, such as "clipboard" or "aFile.txt"
|
# $content - Description of the content being blocked, such as "clipboard" or "aFile.txt"
|
||||||
contentanalysis-block-message = Your organization uses data-loss prevention software that has blocked this content: { $content }.
|
contentanalysis-block-message = Your organization uses data-loss prevention software that has blocked this content: { $content }.
|
||||||
# Variables:
|
# Variables:
|
||||||
|
# $agent - The name of the DLP agent doing the analysis
|
||||||
# $content - Description of the content being blocked, such as "clipboard" or "aFile.txt"
|
# $content - Description of the content being blocked, such as "clipboard" or "aFile.txt"
|
||||||
contentanalysis-error-message = An error occurred in communicating with the data-loss prevention software. Transfer denied for resource: { $content }.
|
contentanalysis-unspecified-error-message = An error occurred in communicating with { $agent }. Transfer denied for resource: { $content }.
|
||||||
|
# Variables:
|
||||||
|
# $agent - The name of the DLP agent doing the analysis
|
||||||
|
# $content - Description of the content being blocked, such as "clipboard" or "aFile.txt"
|
||||||
|
contentanalysis-no-agent-connected-message = Unable to connect to { $agent }. Transfer denied for resource: { $content }.
|
||||||
|
# Variables:
|
||||||
|
# $agent - The name of the DLP agent doing the analysis
|
||||||
|
# $content - Description of the content being blocked, such as "clipboard" or "aFile.txt"
|
||||||
|
contentanalysis-invalid-agent-signature-message = Failed signature verification for { $agent }. Transfer denied for resource: { $content }.
|
||||||
|
|
||||||
contentanalysis-inprogress-quit-title = Quit { -brand-shorter-name }?
|
contentanalysis-inprogress-quit-title = Quit { -brand-shorter-name }?
|
||||||
contentanalysis-inprogress-quit-message = Several actions are in progress. If you quit { -brand-shorter-name }, these actions will not be completed.
|
contentanalysis-inprogress-quit-message = Several actions are in progress. If you quit { -brand-shorter-name }, these actions will not be completed.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue