359 lines
13 KiB
C++
359 lines
13 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* 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 "gtest/gtest.h"
|
|
#include "mozilla/Assertions.h"
|
|
#include "mozilla/glean/ContentanalysisMetrics.h"
|
|
#include "mozilla/Logging.h"
|
|
#include "mozilla/Preferences.h"
|
|
#include "mozilla/ScopeExit.h"
|
|
#include "nsIURI.h"
|
|
#include "nsIURIMutator.h"
|
|
#include "nsServiceManagerUtils.h"
|
|
#include "ContentAnalysis.h"
|
|
#include "TestContentAnalysisUtils.h"
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::contentanalysis;
|
|
|
|
class ContentAnalysisTelemetryTest : public testing::Test {
|
|
protected:
|
|
ContentAnalysisTelemetryTest() {
|
|
auto* logmodule = LogModule::Get("contentanalysis");
|
|
logmodule->SetLevel(LogLevel::Verbose);
|
|
|
|
nsCOMPtr<nsIContentAnalysis> caSvc =
|
|
do_GetService("@mozilla.org/contentanalysis;1");
|
|
MOZ_ASSERT(caSvc);
|
|
mContentAnalysis = static_cast<ContentAnalysis*>(caSvc.get());
|
|
MOZ_ALWAYS_SUCCEEDS(mContentAnalysis->TestOnlySetCACmdLineArg(true));
|
|
}
|
|
|
|
// Note that the constructor (and SetUp() method) get called once per test,
|
|
// not once for the whole fixture. To make running these tests faster,
|
|
// start the agent once here. A test or two may restart it, but this will
|
|
// be faster than starting it for every test that wants it.
|
|
static void SetUpTestSuite() {
|
|
if (sAgentInfo.processInfo.hProcess != nullptr) {
|
|
// Agent is already running, no need to start it again.
|
|
return;
|
|
}
|
|
GeneratePipeName(L"contentanalysissdk-gtest-", sPipeName);
|
|
MOZ_ALWAYS_SUCCEEDS(Preferences::SetBool(kIsDLPEnabledPref, true));
|
|
MOZ_ALWAYS_SUCCEEDS(
|
|
Preferences::SetString(kPipePathNamePref, sPipeName.get()));
|
|
EnsureAgentStarted();
|
|
}
|
|
|
|
static void EnsureAgentStarted() {
|
|
if (sAgentInfo.processInfo.hProcess != nullptr) {
|
|
// Agent is already running, no need to start it again.
|
|
return;
|
|
}
|
|
sAgentInfo = LaunchAgentNormal(L"block", L"warn", sPipeName);
|
|
}
|
|
static void EnsureAgentTerminated() {
|
|
sAgentInfo.TerminateProcess();
|
|
sAgentInfo = MozAgentInfo();
|
|
}
|
|
|
|
static void TearDownTestSuite() {
|
|
EnsureAgentTerminated();
|
|
|
|
MOZ_ALWAYS_SUCCEEDS(Preferences::ClearUser(kPipePathNamePref));
|
|
MOZ_ALWAYS_SUCCEEDS(Preferences::ClearUser(kIsDLPEnabledPref));
|
|
}
|
|
|
|
void TearDown() override {
|
|
MOZ_ALWAYS_SUCCEEDS(mContentAnalysis->TestOnlySetCACmdLineArg(false));
|
|
}
|
|
void AttemptToConnectAndMeasureTelemetry(const nsCString& expectedError);
|
|
|
|
void ResetUrlLists() {
|
|
mContentAnalysis->mParsedUrlLists = false;
|
|
mContentAnalysis->mAllowUrlList = {};
|
|
mContentAnalysis->mDenyUrlList = {};
|
|
}
|
|
|
|
// This is used to help tests clean up after terminating and restarting
|
|
// the agent.
|
|
RefPtr<ContentAnalysis> mContentAnalysis;
|
|
static nsString sPipeName;
|
|
static MozAgentInfo sAgentInfo;
|
|
};
|
|
|
|
MOZ_RUNINIT nsString ContentAnalysisTelemetryTest::sPipeName;
|
|
MOZ_RUNINIT MozAgentInfo ContentAnalysisTelemetryTest::sAgentInfo;
|
|
|
|
void ContentAnalysisTelemetryTest::AttemptToConnectAndMeasureTelemetry(
|
|
const nsCString& expectedError) {
|
|
ASSERT_TRUE(expectedError.IsEmpty() ||
|
|
expectedError.EqualsLiteral("NS_ERROR_CONNECTION_REFUSED") ||
|
|
expectedError.EqualsLiteral("NS_ERROR_INVALID_SIGNATURE"))
|
|
<< "Unexpected expectedError in test!";
|
|
auto originalConnectionAttempts =
|
|
glean::content_analysis::connection_attempt.TestGetValue()
|
|
.unwrap()
|
|
.valueOr(0);
|
|
auto originalConnectionRefusedFailures =
|
|
glean::content_analysis::connection_failure
|
|
.Get(nsCString("NS_ERROR_CONNECTION_REFUSED"))
|
|
.TestGetValue()
|
|
.unwrap()
|
|
.valueOr(0);
|
|
auto originalInvalidSignatureFailures =
|
|
glean::content_analysis::connection_failure
|
|
.Get(nsCString("NS_ERROR_INVALID_SIGNATURE"))
|
|
.TestGetValue()
|
|
.unwrap()
|
|
.valueOr(0);
|
|
|
|
mContentAnalysis->ForceRecreateClientForTest();
|
|
|
|
RefPtr timedOut = MakeRefPtr<media::Refcountable<BoolStruct>>();
|
|
RefPtr<CancelableRunnable> timer = QueueTimeoutToMainThread(timedOut);
|
|
mozilla::SpinEventLoopUntil("Waiting for attempt"_ns, [&, timedOut]() {
|
|
if (timedOut->mValue) {
|
|
return true;
|
|
}
|
|
return !mContentAnalysis->GetCreatingClientForTest();
|
|
});
|
|
|
|
timer->Cancel();
|
|
EXPECT_FALSE(timedOut->mValue);
|
|
|
|
EXPECT_EQ(glean::content_analysis::connection_attempt.TestGetValue()
|
|
.unwrap()
|
|
.valueOr(0),
|
|
originalConnectionAttempts + 1);
|
|
EXPECT_EQ(
|
|
glean::content_analysis::connection_failure
|
|
.Get(nsCString("NS_ERROR_CONNECTION_REFUSED"))
|
|
.TestGetValue()
|
|
.unwrap()
|
|
.valueOr(0),
|
|
originalConnectionRefusedFailures +
|
|
(expectedError.EqualsASCII("NS_ERROR_CONNECTION_REFUSED") ? 1 : 0));
|
|
EXPECT_EQ(
|
|
glean::content_analysis::connection_failure
|
|
.Get(nsCString("NS_ERROR_INVALID_SIGNATURE"))
|
|
.TestGetValue()
|
|
.unwrap()
|
|
.valueOr(0),
|
|
originalInvalidSignatureFailures +
|
|
(expectedError.EqualsASCII("NS_ERROR_INVALID_SIGNATURE") ? 1 : 0));
|
|
}
|
|
|
|
TEST_F(ContentAnalysisTelemetryTest, TestConnectionSuccess) {
|
|
EnsureAgentStarted();
|
|
|
|
AttemptToConnectAndMeasureTelemetry(""_ns);
|
|
}
|
|
|
|
TEST_F(ContentAnalysisTelemetryTest, TestConnectionFailureBecauseNoAgent) {
|
|
EnsureAgentTerminated();
|
|
AttemptToConnectAndMeasureTelemetry("NS_ERROR_CONNECTION_REFUSED"_ns);
|
|
}
|
|
|
|
TEST_F(ContentAnalysisTelemetryTest,
|
|
TestConnectionFailureBecauseSignatureVerification) {
|
|
EnsureAgentStarted();
|
|
MOZ_ALWAYS_SUCCEEDS(
|
|
Preferences::SetCString(kClientSignaturePref, "anInvalidSignature"));
|
|
auto restorePref =
|
|
MakeScopeExit([&] { Preferences::ClearUser(kClientSignaturePref); });
|
|
AttemptToConnectAndMeasureTelemetry("NS_ERROR_INVALID_SIGNATURE"_ns);
|
|
}
|
|
|
|
TEST_F(ContentAnalysisTelemetryTest, TestSimpleRequest) {
|
|
EnsureAgentStarted();
|
|
|
|
nsCOMPtr<nsIURI> uri = GetExampleDotComURI();
|
|
nsString allow(L"allowSimple");
|
|
nsCOMPtr<nsIContentAnalysisRequest> request = new ContentAnalysisRequest(
|
|
nsIContentAnalysisRequest::AnalysisType::eBulkDataEntry,
|
|
nsIContentAnalysisRequest::Reason::eClipboardPaste, std::move(allow),
|
|
false, EmptyCString(), uri,
|
|
nsIContentAnalysisRequest::OperationType::eClipboard, nullptr);
|
|
|
|
nsCString analysisTypeString = "BULK_DATA_ENTRY"_ns;
|
|
auto originalAnalysisTypeCount =
|
|
glean::content_analysis::request_sent_by_analysis_type
|
|
.Get(analysisTypeString)
|
|
.TestGetValue()
|
|
.unwrap()
|
|
.valueOr(0);
|
|
nsCString reasonString = "CLIPBOARD_PASTE"_ns;
|
|
auto originalReasonCount =
|
|
glean::content_analysis::request_sent_by_reason.Get(reasonString)
|
|
.TestGetValue()
|
|
.unwrap()
|
|
.valueOr(0);
|
|
|
|
SendRequestAndExpectResponse(mContentAnalysis, request, Some(true),
|
|
Some(nsIContentAnalysisResponse::eAllow),
|
|
Nothing());
|
|
|
|
EXPECT_EQ(glean::content_analysis::request_sent_by_analysis_type
|
|
.Get(analysisTypeString)
|
|
.TestGetValue()
|
|
.unwrap()
|
|
.valueOr(0),
|
|
originalAnalysisTypeCount + 1);
|
|
EXPECT_EQ(glean::content_analysis::request_sent_by_reason.Get(reasonString)
|
|
.TestGetValue()
|
|
.unwrap()
|
|
.valueOr(0),
|
|
originalReasonCount + 1);
|
|
}
|
|
|
|
TEST_F(ContentAnalysisTelemetryTest, TestAllowAndDenyLists) {
|
|
EnsureAgentStarted();
|
|
MOZ_ALWAYS_SUCCEEDS(
|
|
Preferences::SetCString(kAllowUrlPref, ".*example\\.com.*"));
|
|
|
|
ResetUrlLists();
|
|
nsCOMPtr<nsIURI> uri = GetExampleDotComURI();
|
|
nsString allow(L"allowSimple");
|
|
nsCOMPtr<nsIContentAnalysisRequest> request = new ContentAnalysisRequest(
|
|
nsIContentAnalysisRequest::AnalysisType::eBulkDataEntry,
|
|
nsIContentAnalysisRequest::Reason::eClipboardPaste, std::move(allow),
|
|
false, EmptyCString(), uri,
|
|
nsIContentAnalysisRequest::OperationType::eClipboard, nullptr);
|
|
|
|
auto originalAllowCount =
|
|
glean::content_analysis::request_allowed_by_allow_url.TestGetValue()
|
|
.unwrap()
|
|
.valueOr(0);
|
|
|
|
SendRequestAndWaitForEarlyResult(mContentAnalysis, request, Some(true));
|
|
|
|
EXPECT_EQ(glean::content_analysis::request_allowed_by_allow_url.TestGetValue()
|
|
.unwrap()
|
|
.valueOr(0),
|
|
originalAllowCount + 1);
|
|
|
|
MOZ_ALWAYS_SUCCEEDS(Preferences::ClearUser(kAllowUrlPref));
|
|
MOZ_ALWAYS_SUCCEEDS(
|
|
Preferences::SetCString(kDenyUrlPref, ".*example\\.com.*"));
|
|
ResetUrlLists();
|
|
|
|
auto originalDenyCount =
|
|
glean::content_analysis::request_blocked_by_deny_url.TestGetValue()
|
|
.unwrap()
|
|
.valueOr(0);
|
|
|
|
SendRequestAndWaitForEarlyResult(mContentAnalysis, request, Some(false));
|
|
|
|
EXPECT_EQ(glean::content_analysis::request_blocked_by_deny_url.TestGetValue()
|
|
.unwrap()
|
|
.valueOr(0),
|
|
originalDenyCount + 1);
|
|
|
|
MOZ_ALWAYS_SUCCEEDS(Preferences::ClearUser(kDenyUrlPref));
|
|
ResetUrlLists();
|
|
}
|
|
|
|
TEST_F(ContentAnalysisTelemetryTest, TestSimpleAllowResponse) {
|
|
EnsureAgentStarted();
|
|
|
|
nsCOMPtr<nsIURI> uri = GetExampleDotComURI();
|
|
nsString allow(L"allowSimple");
|
|
nsCOMPtr<nsIContentAnalysisRequest> request = new ContentAnalysisRequest(
|
|
nsIContentAnalysisRequest::AnalysisType::eBulkDataEntry,
|
|
nsIContentAnalysisRequest::Reason::eClipboardPaste, std::move(allow),
|
|
false, EmptyCString(), uri,
|
|
nsIContentAnalysisRequest::OperationType::eClipboard, nullptr);
|
|
|
|
nsCString allowString = "1000"_ns; // eAllow
|
|
auto originalAnalysisTypeCount =
|
|
glean::content_analysis::response_action.Get(allowString)
|
|
.TestGetValue()
|
|
.unwrap()
|
|
.valueOr(0);
|
|
|
|
SendRequestAndExpectResponse(mContentAnalysis, request, Some(true),
|
|
Some(nsIContentAnalysisResponse::eAllow),
|
|
Nothing());
|
|
|
|
EXPECT_EQ(glean::content_analysis::response_action.Get(allowString)
|
|
.TestGetValue()
|
|
.unwrap()
|
|
.valueOr(0),
|
|
originalAnalysisTypeCount + 1);
|
|
}
|
|
|
|
TEST_F(ContentAnalysisTelemetryTest, TestSimpleBlockResponse) {
|
|
EnsureAgentStarted();
|
|
|
|
nsCOMPtr<nsIURI> uri = GetExampleDotComURI();
|
|
nsString block(L"block");
|
|
nsCOMPtr<nsIContentAnalysisRequest> request = new ContentAnalysisRequest(
|
|
nsIContentAnalysisRequest::AnalysisType::eBulkDataEntry,
|
|
nsIContentAnalysisRequest::Reason::eClipboardPaste, std::move(block),
|
|
false, EmptyCString(), uri,
|
|
nsIContentAnalysisRequest::OperationType::eClipboard, nullptr);
|
|
|
|
nsCString blockString = "3"_ns; // eBlock
|
|
auto originalAnalysisTypeCount =
|
|
glean::content_analysis::response_action.Get(blockString)
|
|
.TestGetValue()
|
|
.unwrap()
|
|
.valueOr(0);
|
|
|
|
SendRequestAndExpectResponse(mContentAnalysis, request, Some(false),
|
|
Some(nsIContentAnalysisResponse::eBlock),
|
|
Nothing());
|
|
|
|
EXPECT_EQ(glean::content_analysis::response_action.Get(blockString)
|
|
.TestGetValue()
|
|
.unwrap()
|
|
.valueOr(0),
|
|
originalAnalysisTypeCount + 1);
|
|
}
|
|
|
|
TEST_F(ContentAnalysisTelemetryTest, TestConnectionRetry) {
|
|
// This is a little tricky to test because the usual way of
|
|
// establishing a connection is to call ForceRecreateClientForTest(),
|
|
// which counts as a retry. So make sure we have a good connection,
|
|
// then restart the agent, then do another request, which should
|
|
// trigger a retry.
|
|
EnsureAgentStarted();
|
|
nsCOMPtr<nsIURI> uri = GetExampleDotComURI();
|
|
nsString allow(L"allowSimple");
|
|
nsCOMPtr<nsIContentAnalysisRequest> request = new ContentAnalysisRequest(
|
|
nsIContentAnalysisRequest::AnalysisType::eBulkDataEntry,
|
|
nsIContentAnalysisRequest::Reason::eClipboardPaste, std::move(allow),
|
|
false, EmptyCString(), uri,
|
|
nsIContentAnalysisRequest::OperationType::eClipboard, nullptr);
|
|
SendRequestAndExpectResponse(mContentAnalysis, request, Some(true),
|
|
Some(nsIContentAnalysisResponse::eAllow),
|
|
Nothing());
|
|
|
|
EnsureAgentTerminated();
|
|
EnsureAgentStarted();
|
|
|
|
auto originalConnectionAttempts =
|
|
glean::content_analysis::connection_attempt.TestGetValue()
|
|
.unwrap()
|
|
.valueOr(0);
|
|
auto originalRetryAttempts =
|
|
glean::content_analysis::connection_attempt_retry.TestGetValue()
|
|
.unwrap()
|
|
.valueOr(0);
|
|
|
|
SendRequestAndExpectResponse(mContentAnalysis, request, Some(true),
|
|
Some(nsIContentAnalysisResponse::eAllow),
|
|
Nothing());
|
|
|
|
EXPECT_EQ(glean::content_analysis::connection_attempt.TestGetValue()
|
|
.unwrap()
|
|
.valueOr(0),
|
|
originalConnectionAttempts + 1);
|
|
EXPECT_EQ(glean::content_analysis::connection_attempt_retry.TestGetValue()
|
|
.unwrap()
|
|
.valueOr(0),
|
|
originalRetryAttempts + 1);
|
|
}
|