Skip to content

Commit

Permalink
Add support for tracking websocket connections in extension telemetry…
Browse files Browse the repository at this point in the history
… service.

Enhance RemoteHostContacted signal and signal processor classes to support websocket connections. These changes are non-functional until the actual signal interception logic in web_request_api is landed.

Bug: 1362637
Change-Id: Ia8e6c09a03d57b446f3b7024eec2980bd373cf83
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3921947
Reviewed-by: Xinghui Lu <xinghuilu@chromium.org>
Commit-Queue: Adam Psarouthakis <psarouthakis@google.com>
Cr-Commit-Position: refs/heads/main@{#1054295}
  • Loading branch information
Adam Psarouthakis authored and Chromium LUCI CQ committed Oct 3, 2022
1 parent ae2c4d4 commit b8b3405
Show file tree
Hide file tree
Showing 9 changed files with 87 additions and 39 deletions.
Expand Up @@ -155,6 +155,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionTelemetryServiceBrowserTest,
remote_host_contacted_info.remote_host(0);
EXPECT_EQ(remote_host_info.contact_count(), static_cast<uint32_t>(1));
EXPECT_EQ(remote_host_info.url(), kExtensionContactedHost);
EXPECT_EQ(remote_host_info.connection_protocol(),
RemoteHostInfo::HTTP_HTTPS);
}
}

Expand Down
Expand Up @@ -33,8 +33,8 @@ void PotentialPasswordTheftSignalProcessor::ProcessSignal(
if (signal.GetType() == ExtensionSignalType::kRemoteHostContacted) {
const auto& rhc_signal =
static_cast<const RemoteHostContactedSignal&>(signal);
// Extract only the host portion of the contacted URLs.
std::string host_url = rhc_signal.contacted_host_url().host();
// Extract only the host portion of the remote host URLs.
std::string host_url = rhc_signal.remote_host_url().host();
extension_id = rhc_signal.extension_id();
(remote_host_url_queue_[extension_id])
.emplace_back(std::make_pair(host_url, signal_creation_time));
Expand Down
Expand Up @@ -3,13 +3,20 @@
// found in the LICENSE file.

#include "chrome/browser/safe_browsing/extension_telemetry/remote_host_contacted_signal.h"
#include "components/safe_browsing/core/common/proto/csd.pb.h"

namespace safe_browsing {

using RemoteHostInfo = ExtensionTelemetryReportRequest::SignalInfo::
RemoteHostContactedInfo::RemoteHostInfo;

RemoteHostContactedSignal::RemoteHostContactedSignal(
const extensions::ExtensionId& extension_id,
const GURL& host_url)
: ExtensionSignal(extension_id), contacted_host_url_(host_url) {}
const GURL& host_url,
RemoteHostInfo::ProtocolType protocol)
: ExtensionSignal(extension_id),
remote_host_url_(host_url),
protocol_(protocol) {}

RemoteHostContactedSignal::~RemoteHostContactedSignal() = default;

Expand Down
Expand Up @@ -6,25 +6,38 @@
#define CHROME_BROWSER_SAFE_BROWSING_EXTENSION_TELEMETRY_REMOTE_HOST_CONTACTED_SIGNAL_H_

#include "chrome/browser/safe_browsing/extension_telemetry/extension_signal.h"
#include "components/safe_browsing/core/common/proto/csd.pb.h"
#include "url/gurl.h"

namespace safe_browsing {

using RemoteHostInfo = ExtensionTelemetryReportRequest::SignalInfo::
RemoteHostContactedInfo::RemoteHostInfo;

// A signal that is created when an extension initiates a web request.
class RemoteHostContactedSignal : public ExtensionSignal {
public:
RemoteHostContactedSignal(const extensions::ExtensionId& extension_id,
const GURL& host_url);
RemoteHostContactedSignal(
const extensions::ExtensionId& extension_id,
const GURL& host_url,
RemoteHostInfo::ProtocolType protocol = RemoteHostInfo::HTTP_HTTPS);
~RemoteHostContactedSignal() override;

// ExtensionSignal:
ExtensionSignalType GetType() const override;

const GURL& contacted_host_url() const { return contacted_host_url_; }
const GURL& remote_host_url() const { return remote_host_url_; }

const RemoteHostInfo::ProtocolType& protocol_type() const {
return protocol_;
}

protected:
// Url of the remote contacted host.
GURL contacted_host_url_;
GURL remote_host_url_;

// The protocol used to contact the remote host.
RemoteHostInfo::ProtocolType protocol_;
};

} // namespace safe_browsing
Expand Down
Expand Up @@ -6,7 +6,6 @@

#include "base/check_op.h"
#include "chrome/browser/safe_browsing/extension_telemetry/remote_host_contacted_signal.h"
#include "components/safe_browsing/core/common/proto/csd.pb.h"

namespace safe_browsing {

Expand All @@ -21,15 +20,16 @@ void RemoteHostContactedSignalProcessor::ProcessSignal(
const auto& rhc_signal =
static_cast<const RemoteHostContactedSignal&>(signal);
// Extract only the host portion of the urls.
const std::string host_url = rhc_signal.contacted_host_url().host();
++((remote_host_url_store_[rhc_signal.extension_id()])[host_url]);
std::string host_url = rhc_signal.remote_host_url().host();
++(((remote_host_info_store_[rhc_signal.extension_id()])
[rhc_signal.protocol_type()])[host_url]);
}

std::unique_ptr<ExtensionTelemetryReportRequest_SignalInfo>
RemoteHostContactedSignalProcessor::GetSignalInfoForReport(
const extensions::ExtensionId& extension_id) {
auto remote_host_url_store_it = remote_host_url_store_.find(extension_id);
if (remote_host_url_store_it == remote_host_url_store_.end())
auto remote_host_info_store_it = remote_host_info_store_.find(extension_id);
if (remote_host_info_store_it == remote_host_info_store_.end())
return nullptr;

// Create the signal info protobuf.
Expand All @@ -40,21 +40,26 @@ RemoteHostContactedSignalProcessor::GetSignalInfoForReport(
remote_host_contacted_info =
signal_info->mutable_remote_host_contacted_info();

for (auto& remote_host_contacted_urls_it : remote_host_url_store_it->second) {
ExtensionTelemetryReportRequest_SignalInfo_RemoteHostContactedInfo_RemoteHostInfo*
remote_host_pb = remote_host_contacted_info->add_remote_host();
remote_host_pb->set_url(std::move(remote_host_contacted_urls_it.first));
remote_host_pb->set_contact_count(remote_host_contacted_urls_it.second);
for (auto& remote_host_urls_by_protocol_it :
remote_host_info_store_it->second) {
for (auto& remote_host_urls_it : remote_host_urls_by_protocol_it.second) {
ExtensionTelemetryReportRequest_SignalInfo_RemoteHostContactedInfo_RemoteHostInfo*
remote_host_pb = remote_host_contacted_info->add_remote_host();
remote_host_pb->set_url(std::move(remote_host_urls_it.first));
remote_host_pb->set_contact_count(remote_host_urls_it.second);
remote_host_pb->set_connection_protocol(
remote_host_urls_by_protocol_it.first);
}
}

// Clear the data in the remote host urls store.
remote_host_url_store_.erase(remote_host_url_store_it);
remote_host_info_store_.erase(remote_host_info_store_it);

return signal_info;
}

bool RemoteHostContactedSignalProcessor::HasDataToReportForTest() const {
return !remote_host_url_store_.empty();
return !remote_host_info_store_.empty();
}

} // namespace safe_browsing
Expand Up @@ -10,6 +10,7 @@

#include "base/containers/flat_map.h"
#include "chrome/browser/safe_browsing/extension_telemetry/extension_signal_processor.h"
#include "components/safe_browsing/core/common/proto/csd.pb.h"
#include "url/gurl.h"

namespace safe_browsing {
Expand All @@ -36,12 +37,18 @@ class RemoteHostContactedSignalProcessor : public ExtensionSignalProcessor {
bool HasDataToReportForTest() const override;

protected:
using RemoteHostInfo = ExtensionTelemetryReportRequest::SignalInfo::
RemoteHostContactedInfo::RemoteHostInfo;
// Maps remote hosts url to contact count.
using RemoteHostURLs = base::flat_map<std::string, uint32_t>;
// Maps extension id to (remote host url, contact count).
using RemoteHostURLsPerExtension =
base::flat_map<extensions::ExtensionId, RemoteHostURLs>;
RemoteHostURLsPerExtension remote_host_url_store_;
// Maps connection protocols to remote hosts contacted.
using RemoteHostURLsByConnectionProtocol =
base::flat_map<RemoteHostInfo::ProtocolType, RemoteHostURLs>;
// Maps extension id to remote hosts contacted.
using RemoteHostInfoPerExtension =
base::flat_map<extensions::ExtensionId,
RemoteHostURLsByConnectionProtocol>;
RemoteHostInfoPerExtension remote_host_info_store_;
};

} // namespace safe_browsing
Expand Down
Expand Up @@ -21,7 +21,8 @@ using RemoteHostInfo =

constexpr const char* kExtensionId[] = {"crx-0", "crx-1"};
const char* host_urls[] = {"http://www.google.com", "http://www.youtube.com"};

RemoteHostInfo::ProtocolType protocolType[] = {RemoteHostInfo::HTTP_HTTPS,
RemoteHostInfo::WEBSOCKET};
class RemoteHostContactedSignalProcessorTest : public ::testing::Test {
protected:
RemoteHostContactedSignalProcessorTest() = default;
Expand All @@ -36,7 +37,8 @@ TEST_F(RemoteHostContactedSignalProcessorTest,

TEST_F(RemoteHostContactedSignalProcessorTest,
StoresDataAfterProcessingSignal) {
auto signal = RemoteHostContactedSignal(kExtensionId[0], GURL(host_urls[0]));
auto signal = RemoteHostContactedSignal(kExtensionId[0], GURL(host_urls[0]),
protocolType[0]);
processor_.ProcessSignal(signal);

// Verify that processor now has some data to report.
Expand All @@ -47,28 +49,28 @@ TEST_F(RemoteHostContactedSignalProcessorTest,
EXPECT_FALSE(processor_.GetSignalInfoForReport(kExtensionId[1]));
}

// Test contacted_count and mapping functionalities.
// Test contacted_count, protocol type, and mapping functionalities.
TEST_F(RemoteHostContactedSignalProcessorTest, ReportsSignalInfoCorrectly) {
// Process 3 signals for the first extension, each corresponding to the
// web request sent to the first test url.
for (int i = 0; i < 3; i++) {
auto signal =
RemoteHostContactedSignal(kExtensionId[0], GURL(host_urls[0]));
processor_.ProcessSignal(signal);
auto signal = RemoteHostContactedSignal(kExtensionId[0], GURL(host_urls[0]),
protocolType[0]);
processor_.ProcessSignal(std::move(signal));
}

// Process 3 signals for the second extension. Two signal corresponds to the
// web request sent to the first url, the third to the web request
// sent to the second url.
for (int i = 0; i < 2; i++) {
auto signal =
RemoteHostContactedSignal(kExtensionId[1], GURL(host_urls[0]));
processor_.ProcessSignal(signal);
auto signal = RemoteHostContactedSignal(kExtensionId[1], GURL(host_urls[0]),
protocolType[0]);
processor_.ProcessSignal(std::move(signal));
}
{
auto signal =
RemoteHostContactedSignal(kExtensionId[1], GURL(host_urls[1]));
processor_.ProcessSignal(signal);
auto signal = RemoteHostContactedSignal(kExtensionId[1], GURL(host_urls[1]),
protocolType[1]);
processor_.ProcessSignal(std::move(signal));
}

// Retrieve signal info for first extension.
Expand All @@ -93,33 +95,37 @@ TEST_F(RemoteHostContactedSignalProcessorTest, ReportsSignalInfoCorrectly) {
const RemoteHostContactedInfo& remote_host_contacted_info =
extension_0_signal_info->remote_host_contacted_info();

// Verify data stored : only 1 url (contacted 3 times).
// Verify data stored : only 1 url (contacted 3 times) and
// contacted via HTTP_HTTPS protocol.
ASSERT_EQ(remote_host_contacted_info.remote_host_size(), 1);
const RemoteHostInfo& remote_host_info =
remote_host_contacted_info.remote_host(0);
EXPECT_EQ(remote_host_info.contact_count(), static_cast<uint32_t>(3));
EXPECT_EQ(remote_host_info.connection_protocol(), protocolType[0]);
}

// Verify signal info contents for second extension.
{
const RemoteHostContactedInfo& remote_host_contacted_info =
extension_1_signal_info->remote_host_contacted_info();

// Verify data stored : 2 URLs (2 contacted counts for 1st, 1 for the 2nd).
// Verify data stored : 2 URLs (2 contacted counts for 1st, 1 for the 2nd)
// and contacted via HTTP_HTTPS and WEBSOCKET protocols.
ASSERT_EQ(remote_host_contacted_info.remote_host_size(), 2);
{
const RemoteHostInfo& remote_host_info =
remote_host_contacted_info.remote_host(0);
EXPECT_EQ(remote_host_info.contact_count(), static_cast<uint32_t>(2));
EXPECT_EQ(remote_host_info.connection_protocol(), protocolType[0]);
}
{
const RemoteHostInfo& remote_host_info =
remote_host_contacted_info.remote_host(1);
EXPECT_EQ(remote_host_info.contact_count(), static_cast<uint32_t>(1));
EXPECT_EQ(remote_host_info.connection_protocol(), protocolType[1]);
}
}
}

} // namespace

} // namespace safe_browsing
5 changes: 5 additions & 0 deletions components/safe_browsing/core/common/features.cc
Expand Up @@ -118,6 +118,10 @@ BASE_FEATURE(kExtensionTelemetryReportContactedHosts,
"SafeBrowsingExtensionTelemetryReportContactedHosts",
base::FEATURE_DISABLED_BY_DEFAULT);

BASE_FEATURE(kExtensionTelemetryReportHostsContactedViaWebSocket,
"SafeBrowsingExtensionTelemetryReportHostsContactedViaWebsocket",
base::FEATURE_DISABLED_BY_DEFAULT);

BASE_FEATURE(kExtensionTelemetryPotentialPasswordTheft,
"SafeBrowsingExtensionTelemetryPotentialPasswordTheft",
base::FEATURE_DISABLED_BY_DEFAULT);
Expand Down Expand Up @@ -234,6 +238,7 @@ constexpr struct {
{&kEnhancedProtectionPhase2IOS, true},
{&kExtensionTelemetry, true},
{&kExtensionTelemetryReportContactedHosts, true},
{&kExtensionTelemetryReportHostsContactedViaWebSocket, true},
{&kExtensionTelemetryCookiesGetAllSignal, true},
{&kExtensionTelemetryPersistence, true},
{&kExtensionTelemetryPotentialPasswordTheft, true},
Expand Down
3 changes: 3 additions & 0 deletions components/safe_browsing/core/common/features.h
Expand Up @@ -109,6 +109,9 @@ BASE_DECLARE_FEATURE(kExtensionTelemetryTabsExecuteScriptSignal);
// Enables reporting of remote hosts contacted by extensions in telemetry.
BASE_DECLARE_FEATURE(kExtensionTelemetryReportContactedHosts);

// Enables reporting of remote hosts contacted by extensions via websockets;
BASE_DECLARE_FEATURE(kExtensionTelemetryReportHostsContactedViaWebSocket);

// Enables collection of potential password theft data and uploads
// telemetry reports to SB servers.
BASE_DECLARE_FEATURE(kExtensionTelemetryPotentialPasswordTheft);
Expand Down

0 comments on commit b8b3405

Please sign in to comment.