Skip to content

Commit

Permalink
Add image data request to content analysis delegate.
Browse files Browse the repository at this point in the history
Bug: b/261589323
Change-Id: I69bccfb90fed4f2eda3da8e148c85b1f6a734aec
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4295198
Reviewed-by: Roger Tawa <rogerta@chromium.org>
Commit-Queue: Nancy Xiao <nancylanxiao@google.com>
Reviewed-by: Dominique Fauteux-Chapleau <domfc@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1118134}
  • Loading branch information
nancy-lan-xiao authored and Chromium LUCI CQ committed Mar 16, 2023
1 parent b7878a7 commit ace6e91
Show file tree
Hide file tree
Showing 9 changed files with 579 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,9 @@ AnalysisSettings AnalysisServiceSettings::GetAnalysisSettingsWithTags(
if (is_cloud_analysis()) {
CloudAnalysisSettings cloud_settings;
cloud_settings.analysis_url = GURL(analysis_config_->url);
// We assume all support_tags structs have the same max file size.
cloud_settings.max_file_size =
analysis_config_->supported_tags[0].max_file_size;
DCHECK(cloud_settings.analysis_url.is_valid());
settings.cloud_or_local_settings =
CloudOrLocalAnalysisSettings(std::move(cloud_settings));
Expand All @@ -202,6 +205,9 @@ AnalysisSettings AnalysisServiceSettings::GetAnalysisSettingsWithTags(
local_settings.local_path = analysis_config_->local_path;
local_settings.user_specific = analysis_config_->user_specific;
local_settings.subject_names = analysis_config_->subject_names;
// We assume all support_tags structs have the same max file size.
local_settings.max_file_size =
analysis_config_->supported_tags[0].max_file_size;
local_settings.verification_signatures = verification_signatures_;

settings.cloud_or_local_settings =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,14 @@ base::span<const char* const> CloudOrLocalAnalysisSettings::subject_names()
return absl::get<LocalAnalysisSettings>(*this).subject_names;
}

size_t CloudOrLocalAnalysisSettings::max_file_size() const {
if (is_local_analysis()) {
return absl::get<LocalAnalysisSettings>(*this).max_file_size;
} else {
return absl::get<CloudAnalysisSettings>(*this).max_file_size;
}
}

AnalysisSettings::AnalysisSettings() = default;
AnalysisSettings::AnalysisSettings(AnalysisSettings&&) = default;
AnalysisSettings& AnalysisSettings::operator=(AnalysisSettings&&) = default;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ struct CloudAnalysisSettings {
// The DM token to be used for scanning. May be empty, for example if this
// scan is initiated by APP or for a local content analysis.
std::string dm_token;

// The scanning limit for all data passed to cloud content analysis.
size_t max_file_size;
};

// Struct holding settings data specific to a local analysis.
Expand All @@ -67,7 +70,8 @@ struct LocalAnalysisSettings {
std::string local_path;
bool user_specific = false;
base::span<const char* const> subject_names;

// The scanning limit for pasted text and image in local content analysis.
size_t max_file_size;
// Arrays of base64 encoded signing key signatures.
std::vector<std::string> verification_signatures;
};
Expand Down Expand Up @@ -99,6 +103,9 @@ class CloudOrLocalAnalysisSettings
const std::string local_path() const;
bool user_specific() const;
base::span<const char* const> subject_names() const;

// Field accessible by both CloudAnalysisSettings and LocalAnalysisSettings.
size_t max_file_size() const;
};

// Main struct holding settings data for the content analysis Connector.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,17 @@ void ContentAnalysisDelegate::BypassWarnings(
access_point_, content_size, text_response_, user_justification);
}

// Mark the full image as complying and report a warning bypass.
if (image_warning_) {
result_.image_result = true;

ReportAnalysisConnectorWarningBypass(
profile_, url_, "", "", "Image data", std::string(),
/*mime_type*/ std::string(),
extensions::SafeBrowsingPrivateEventRouter::kTriggerWebContentUpload,
access_point_, data_.image.size(), image_response_, user_justification);
}

if (!warned_file_indices_.empty()) {
files_request_handler_->ReportWarningBypass(user_justification);
// Mark every warned file as complying.
Expand Down Expand Up @@ -344,6 +355,7 @@ ContentAnalysisDelegate::ContentAnalysisDelegate(
user_action_id_ =
base::HexEncode(user_action_token.data(), user_action_token.size());
result_.text_results.resize(data_.text.size(), false);
result_.image_result = false;
result_.paths_results.resize(data_.paths.size(), false);
result_.page_result = false;
}
Expand All @@ -365,11 +377,11 @@ void ContentAnalysisDelegate::StringRequestCallback(

text_request_complete_ = true;

RequestHandlerResult request_handler_result =
string_request_result_ =
CalculateRequestHandlerResult(data_.settings, result, response);

bool text_complies = request_handler_result.complies;
bool should_warn = request_handler_result.final_result ==
bool text_complies = string_request_result_.complies;
bool should_warn = string_request_result_.final_result ==
FinalContentAnalysisResult::WARNING;

std::fill(result_.text_results.begin(), result_.text_results.end(),
Expand All @@ -381,8 +393,8 @@ void ContentAnalysisDelegate::StringRequestCallback(
access_point_, content_size, result, response,
CalculateEventResult(data_.settings, text_complies, should_warn));

UpdateFinalResult(request_handler_result.final_result,
request_handler_result.tag);
UpdateFinalResult(string_request_result_.final_result,
string_request_result_.tag);

if (should_warn) {
text_warning_ = true;
Expand All @@ -392,6 +404,48 @@ void ContentAnalysisDelegate::StringRequestCallback(
MaybeCompleteScanRequest();
}

void ContentAnalysisDelegate::ImageRequestCallback(
BinaryUploadService::Result result,
enterprise_connectors::ContentAnalysisResponse response) {
// Remember to send an ack for this response.
if (result == safe_browsing::BinaryUploadService::Result::SUCCESS) {
final_actions_[response.request_token()] = GetAckFinalAction(response);
}

RecordDeepScanMetrics(
data_.settings.cloud_or_local_settings.is_cloud_analysis(), access_point_,
base::TimeTicks::Now() - upload_start_time_, data_.image.size(), result,
response);

image_request_complete_ = true;

image_request_result_ =
CalculateRequestHandlerResult(data_.settings, result, response);

bool image_complies = image_request_result_.complies;
bool should_warn =
image_request_result_.final_result == FinalContentAnalysisResult::WARNING;

result_.image_result = image_complies;

MaybeReportDeepScanningVerdict(
profile_, url_, "", "", "Image data", std::string(),
/*mime_type*/ std::string(),
extensions::SafeBrowsingPrivateEventRouter::kTriggerWebContentUpload,
access_point_, data_.image.size(), result, response,
CalculateEventResult(data_.settings, image_complies, should_warn));

UpdateFinalResult(image_request_result_.final_result,
image_request_result_.tag);

if (should_warn) {
image_warning_ = true;
image_response_ = std::move(response);
}

MaybeCompleteScanRequest();
}

void ContentAnalysisDelegate::FilesRequestCallback(
std::vector<RequestHandlerResult> results) {
// Remember to send acks for any responses.
Expand Down Expand Up @@ -475,8 +529,10 @@ void ContentAnalysisDelegate::PageRequestCallback(
bool ContentAnalysisDelegate::UploadData() {
upload_start_time_ = base::TimeTicks::Now();

// Create a text request, a page request and a file request for each file.
// Create a text request, an image request, a page request and a file request
// for each file.
PrepareTextRequest();
PrepareImageRequest();
PreparePageRequest();

if (!data_.paths.empty()) {
Expand All @@ -496,8 +552,8 @@ bool ContentAnalysisDelegate::UploadData() {
// Do not add code under this comment. The above line should be the last thing
// this function does before the return statement.

return !text_request_complete_ || !files_request_complete_ ||
!page_request_complete_;
return !text_request_complete_ || !image_request_complete_ ||
!files_request_complete_ || !page_request_complete_;
}

void ContentAnalysisDelegate::PrepareTextRequest() {
Expand Down Expand Up @@ -531,6 +587,33 @@ void ContentAnalysisDelegate::PrepareTextRequest() {
}
}

void ContentAnalysisDelegate::PrepareImageRequest() {
// The request is considered complete if there is no image or if the image is
// too large compared to the maximum size.
image_request_complete_ =
data_.image.empty() ||
data_.image.size() >
data_.settings.cloud_or_local_settings.max_file_size();

if (!data_.image.empty()) {
base::UmaHistogramCustomCounts("Enterprise.OnBulkDataEntry.DataSize",
data_.image.size(),
/*min=*/1,
/*max=*/51 * 1024 * 1024,
/*buckets=*/50);
}

if (!image_request_complete_) {
auto request = std::make_unique<StringAnalysisRequest>(
data_.settings.cloud_or_local_settings, data_.image,
base::BindOnce(&ContentAnalysisDelegate::ImageRequestCallback,
weak_ptr_factory_.GetWeakPtr()));

PrepareRequest(enterprise_connectors::BULK_DATA_ENTRY, request.get());
UploadImageForDeepScanning(std::move(request));
}
}

void ContentAnalysisDelegate::PreparePageRequest() {
// The request is considered complete if the mapped region is invalid since it
// prevents scanning.
Expand Down Expand Up @@ -563,12 +646,10 @@ void ContentAnalysisDelegate::PrepareRequest(
// Include tab page title, user action id, and count of requests per user
// action in local content analysis requests.
if (data_.settings.cloud_or_local_settings.is_local_analysis()) {
// Increment the total number of user action requests by 1.
total_requests_count_++;
request->set_tab_title(title_);
request->set_user_action_id(user_action_id_);
// Set request count to 1 for print/paste event. Request count for file
// events are set in
// chrome/browser/enterprise/connectors/analysis/request_handler_base.cc
request->set_user_action_requests_count(1);
}

request->set_analysis_connector(connector);
Expand All @@ -584,6 +665,7 @@ void ContentAnalysisDelegate::PrepareRequest(

void ContentAnalysisDelegate::FillAllResultsWith(bool status) {
std::fill(result_.text_results.begin(), result_.text_results.end(), status);
result_.image_result = status;
std::fill(result_.paths_results.begin(), result_.paths_results.end(), status);
result_.page_result = status;
}
Expand All @@ -595,14 +677,25 @@ BinaryUploadService* ContentAnalysisDelegate::GetBinaryUploadService() {

void ContentAnalysisDelegate::UploadTextForDeepScanning(
std::unique_ptr<BinaryUploadService::Request> request) {
request->set_user_action_requests_count(total_requests_count_);
BinaryUploadService* upload_service = GetBinaryUploadService();
if (upload_service) {
upload_service->MaybeUploadForDeepScanning(std::move(request));
}
}

void ContentAnalysisDelegate::UploadImageForDeepScanning(
std::unique_ptr<BinaryUploadService::Request> request) {
BinaryUploadService* upload_service = GetBinaryUploadService();
request->set_user_action_requests_count(total_requests_count_);
if (upload_service)
upload_service->MaybeUploadForDeepScanning(std::move(request));
}

void ContentAnalysisDelegate::UploadPageForDeepScanning(
std::unique_ptr<BinaryUploadService::Request> request) {
BinaryUploadService* upload_service = GetBinaryUploadService();
request->set_user_action_requests_count(total_requests_count_);
if (upload_service)
upload_service->MaybeUploadForDeepScanning(std::move(request));
}
Expand All @@ -616,8 +709,8 @@ bool ContentAnalysisDelegate::UpdateDialog() {
}

void ContentAnalysisDelegate::MaybeCompleteScanRequest() {
if (!text_request_complete_ || !files_request_complete_ ||
!page_request_complete_) {
if (!text_request_complete_ || !image_request_complete_ ||
!files_request_complete_ || !page_request_complete_) {
return;
}

Expand Down Expand Up @@ -652,6 +745,22 @@ void ContentAnalysisDelegate::RunCallback() {
ContentAnalysisAcknowledgement::BLOCK;
}
}

// If both image and text are present, synchronize their ack statuses if
// needed.
if (!string_request_result_.request_token.empty() &&
!image_request_result_.request_token.empty()) {
if (!result_.image_result) {
final_actions_[string_request_result_.request_token] =
ContentAnalysisAcknowledgement::BLOCK;
}
// text_results is uniformly updated in StringRequestCallback(), so the
// values should be consistent.
if (!result_.text_results[0]) {
final_actions_[image_request_result_.request_token] =
ContentAnalysisAcknowledgement::BLOCK;
}
}
}

void ContentAnalysisDelegate::UpdateFinalResult(
Expand Down

0 comments on commit ace6e91

Please sign in to comment.