Skip to content

Commit

Permalink
Add chrome.tabs API extension telemetry signal and processor
Browse files Browse the repository at this point in the history
Define a new extension telemetry signal type for intercepting
certain chrome.tabs API invocations. Also implement a signal processor
that processes this signal type and generates an associated report. The
actual invoking of this signal processor by the extension telemetry
service will be implemented in a follow-on CL.

Bug: 1465947
Change-Id: I085adee814e0a99eb58bd7b1d34d453fbe3fc035
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4793940
Reviewed-by: Richard Chen <richche@google.com>
Commit-Queue: Anunoy Ghosh <anunoy@chromium.org>
Reviewed-by: Xinghui Lu <xinghuilu@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1187933}
  • Loading branch information
Anunoy Ghosh authored and Chromium LUCI CQ committed Aug 24, 2023
1 parent 53d87d7 commit 9d99c19
Show file tree
Hide file tree
Showing 11 changed files with 517 additions and 3 deletions.
4 changes: 4 additions & 0 deletions chrome/browser/safe_browsing/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,10 @@ static_library("safe_browsing") {
"extension_telemetry/remote_host_contacted_signal.h",
"extension_telemetry/remote_host_contacted_signal_processor.cc",
"extension_telemetry/remote_host_contacted_signal_processor.h",
"extension_telemetry/tabs_api_signal.cc",
"extension_telemetry/tabs_api_signal.h",
"extension_telemetry/tabs_api_signal_processor.cc",
"extension_telemetry/tabs_api_signal_processor.h",
"extension_telemetry/tabs_execute_script_signal.cc",
"extension_telemetry/tabs_execute_script_signal.h",
"extension_telemetry/tabs_execute_script_signal_processor.cc",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ enum class ExtensionSignalType {
kPotentialPasswordTheft = 4,
kCookiesGet = 5,
kDeclarativeNetRequest = 6,
kMaxValue = kDeclarativeNetRequest,
kTabsApi = 7,
kMaxValue = kTabsApi,
};

// An abstract signal. Subclasses provide type-specific functionality to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace safe_browsing {

std::string SanitizeURLWithoutFilename(const std::string url) {
std::string SanitizeURLWithoutFilename(std::string url) {
return GURL(url).GetWithoutFilename().spec();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
namespace safe_browsing {

// Strips the filename from the |url|.
std::string SanitizeURLWithoutFilename(const std::string url);
std::string SanitizeURLWithoutFilename(std::string url);

} // namespace safe_browsing

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/safe_browsing/extension_telemetry/tabs_api_signal.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "chrome/browser/safe_browsing/extension_telemetry/extension_signal_util.h"

namespace safe_browsing {

TabsApiSignal::TabsApiSignal(const extensions::ExtensionId& extension_id,
TabsApiInfo::ApiMethod api_method,
const std::string& current_url,
const std::string& new_url)
: ExtensionSignal(extension_id), api_method_(api_method) {
if (!current_url.empty()) {
current_url_ = SanitizeURLWithoutFilename(current_url);
}
if (!new_url.empty()) {
new_url_ = SanitizeURLWithoutFilename(new_url);
}
}

TabsApiSignal::~TabsApiSignal() = default;

ExtensionSignalType TabsApiSignal::GetType() const {
return ExtensionSignalType::kTabsApi;
}

std::string TabsApiSignal::GetUniqueCallDetailsId() const {
return base::JoinString({base::NumberToString(static_cast<int>(api_method_)),
current_url_, new_url_},
",");
}

} // namespace safe_browsing
45 changes: 45 additions & 0 deletions chrome/browser/safe_browsing/extension_telemetry/tabs_api_signal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_SAFE_BROWSING_EXTENSION_TELEMETRY_TABS_API_SIGNAL_H_
#define CHROME_BROWSER_SAFE_BROWSING_EXTENSION_TELEMETRY_TABS_API_SIGNAL_H_

#include <string>

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

namespace safe_browsing {

using TabsApiInfo = ExtensionTelemetryReportRequest::SignalInfo::TabsApiInfo;

// A signal that is created when an extension invokes chrome.tabs API methods.
class TabsApiSignal : public ExtensionSignal {
public:
TabsApiSignal(const extensions::ExtensionId& extension_id,
TabsApiInfo::ApiMethod api_method,
const std::string& current_url,
const std::string& new_url);
~TabsApiSignal() override;

// ExtensionSignal:
ExtensionSignalType GetType() const override;

// Returns a unique id, which can be used to compare API arguments and as a
// key for storage (e.g., in a map).
std::string GetUniqueCallDetailsId() const;

TabsApiInfo::ApiMethod api_method() const { return api_method_; }
const std::string& current_url() const { return current_url_; }
const std::string& new_url() const { return new_url_; }

protected:
TabsApiInfo::ApiMethod api_method_;
std::string current_url_;
std::string new_url_;
};

} // namespace safe_browsing

#endif // CHROME_BROWSER_SAFE_BROWSING_EXTENSION_TELEMETRY_TABS_EXECUTE_SCRIPT_SIGNAL_H_
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/safe_browsing/extension_telemetry/tabs_api_signal_processor.h"
#include <string>

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

namespace safe_browsing {

namespace {

// Used to limit the number of unique API call details stored for each
// extension.
constexpr size_t kMaxUniqueCallDetails = 100;

} // namespace

TabsApiSignalProcessor::TabsApiInfoStoreEntry::TabsApiInfoStoreEntry() =
default;
TabsApiSignalProcessor::TabsApiInfoStoreEntry::~TabsApiInfoStoreEntry() =
default;
TabsApiSignalProcessor::TabsApiInfoStoreEntry::TabsApiInfoStoreEntry(
const TabsApiInfoStoreEntry& src) = default;
TabsApiSignalProcessor::TabsApiSignalProcessor()
: max_unique_call_details_(kMaxUniqueCallDetails) {}
TabsApiSignalProcessor::~TabsApiSignalProcessor() = default;

void TabsApiSignalProcessor::ProcessSignal(const ExtensionSignal& signal) {
// Validate TabsApi signal.
DCHECK_EQ(ExtensionSignalType::kTabsApi, signal.GetType());
const auto& tabs_api_signal = static_cast<const TabsApiSignal&>(signal);
// Ignore the signal if both URL (current and new) are empty (sanity check).
if (tabs_api_signal.current_url().empty() &&
tabs_api_signal.new_url().empty()) {
return;
}

// Retrieve the signal info store entry for this extension from the store. If
// this is the first signal for an extension, a new entry is created in the
// store.
TabsApiInfoStoreEntry& info_store_entry =
tabs_api_info_store_[tabs_api_signal.extension_id()];
TabsApiCallDetailsMap& call_details_map =
info_store_entry.tabs_api_call_details_map;

const std::string call_details_id = tabs_api_signal.GetUniqueCallDetailsId();
auto call_details_it = call_details_map.find(call_details_id);
if (call_details_it != call_details_map.end()) {
// If a tabs API call with the same arguments has been invoked
// before, simply increment the count for the corresponding record.
auto count = call_details_it->second.count();
call_details_it->second.set_count(count + 1);
} else if (call_details_map.size() < max_unique_call_details_) {
// For new call details, process the signal only if under max limit, i.e,
// add a new TabsApiCallDetails object to the call details map.
TabsApiCallDetails call_details;
call_details.set_method(tabs_api_signal.api_method());
call_details.set_current_url(tabs_api_signal.current_url());
call_details.set_new_url(tabs_api_signal.new_url());
call_details.set_count(1);
call_details_map.emplace(call_details_id, call_details);
}
}

std::unique_ptr<ExtensionTelemetryReportRequest_SignalInfo>
TabsApiSignalProcessor::GetSignalInfoForReport(
const extensions::ExtensionId& extension_id) {
auto tabs_api_info_store_entry = tabs_api_info_store_.find(extension_id);
if (tabs_api_info_store_entry == tabs_api_info_store_.end()) {
return nullptr;
}

// Create the signal info protobuf.
auto signal_info =
std::make_unique<ExtensionTelemetryReportRequest_SignalInfo>();
TabsApiInfo* tabs_api_info = signal_info->mutable_tabs_api_info();

for (auto& call_details :
tabs_api_info_store_entry->second.tabs_api_call_details_map) {
*tabs_api_info->add_call_details() = std::move(call_details.second);
}

// Finally, clear the data in the info store.
tabs_api_info_store_.erase(tabs_api_info_store_entry);

return signal_info;
}

bool TabsApiSignalProcessor::HasDataToReportForTest() const {
return !tabs_api_info_store_.empty();
}

void TabsApiSignalProcessor::SetMaxUniqueCallDetailsForTest(
size_t max_unique_call_details) {
max_unique_call_details_ = max_unique_call_details;
}

} // namespace safe_browsing
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_SAFE_BROWSING_EXTENSION_TELEMETRY_TABS_API_SIGNAL_PROCESSOR_H_
#define CHROME_BROWSER_SAFE_BROWSING_EXTENSION_TELEMETRY_TABS_API_SIGNAL_PROCESSOR_H_

#include <memory>
#include <string>

#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"

namespace safe_browsing {

class ExtensionSignal;
class ExtensionTelemetryReportRequest_SignalInfo;

using TabsApiCallDetails =
ExtensionTelemetryReportRequest::SignalInfo::TabsApiInfo::CallDetails;

// A class that processes chrome.tabs API signal data to generate telemetry
// reports.
class TabsApiSignalProcessor : public ExtensionSignalProcessor {
public:
TabsApiSignalProcessor();
~TabsApiSignalProcessor() override;

TabsApiSignalProcessor(TabsApiSignalProcessor&) = delete;
TabsApiSignalProcessor& operator=(const TabsApiSignalProcessor&) = delete;

// ExtensionSignalProcessor:
void ProcessSignal(const ExtensionSignal& signal) override;
std::unique_ptr<ExtensionTelemetryReportRequest_SignalInfo>
GetSignalInfoForReport(const extensions::ExtensionId& extension_id) override;
bool HasDataToReportForTest() const override;

void SetMaxUniqueCallDetailsForTest(size_t max_unique_call_details);

protected:
// Max number of unique API call details stored per extension.
// The signal processor only stores up to this number of unique API call
// details.
size_t max_unique_call_details_;

// Stores a map of unique API call details. The key used is a string
// concatenation of the call arguments stored.
using TabsApiCallDetailsMap = base::flat_map<std::string, TabsApiCallDetails>;

// Stores chrome.tabs API signal info for a single extension. If
// `max_unique_call_details_` is exceeded, no more call details will be
// recorded.
struct TabsApiInfoStoreEntry {
TabsApiInfoStoreEntry();
~TabsApiInfoStoreEntry();
TabsApiInfoStoreEntry(const TabsApiInfoStoreEntry&);

TabsApiCallDetailsMap tabs_api_call_details_map;
};

// Stores chrome.tabs API signal for multiple extensions, keyed by extension
// id.
using TabsApiInfoStore =
base::flat_map<extensions::ExtensionId, TabsApiInfoStoreEntry>;
TabsApiInfoStore tabs_api_info_store_;
};

} // namespace safe_browsing

#endif // CHROME_BROWSER_SAFE_BROWSING_EXTENSION_TELEMETRY_TABS_API_SIGNAL_PROCESSOR_H_

0 comments on commit 9d99c19

Please sign in to comment.